How to use Property Injection with AutoFac?

38,451

Solution 1

Use Property Injection:

builder.Register(c => LogManager.GetLogger("LoggerName"))
       .As<ILog>();

builder.RegisterType<CustomClass>()
       .PropertiesAutowired();

Solution 2

In my opinion the solution Ninject created is much nicer than the propertyinjection in Autofac. Therefore I created a a custom attribute which is a postsharp aspect which automatically injects my classes:

[AutofacResolve]
public IStorageManager StorageManager { get; set; }

My aspect:

[Serializable]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class AutofacResolveAttribute : LocationInterceptionAspect
{
    public override void OnGetValue(LocationInterceptionArgs args)
    {
        args.ProceedGetValue();

        if (!args.Location.LocationType.IsInterface) return;

        if ( args.Value != null )
        {
           args.Value = DependencyResolver.Current.GetService(args.Location.LocationType);
           args.ProceedSetValue();
        }
    }
}

I know the answer on the question is already given but I thought this was a really neat way of solving automatic property injection in Autofac. Maybe it'll be useful to somebody in the future.

Solution 3

Property injection works for Properties and not for Fields. In your class, Log is a field and not a property and hence it will never get resolved by the Autofac.

Solution 4

I didn't want to use postsharp so I made a quick solution, but it doesn't auto inject. I am new to Autofac, and it should be possible to build on to this solution.

[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class AutofacResolveAttribute : Attribute
{
}

public class AutofactResolver
{
    /// <summary>
    /// Injecting objects into properties marked with "AutofacResolve"
    /// </summary>
    /// <param name="obj">Source object</param>
    public static void InjectProperties(object obj)
    {
        var propertiesToInject = obj.GetType().GetProperties()
             .Where(x => x.CustomAttributes.Any(y => y.AttributeType.Name == nameof(AutofacResolveAttribute))).ToList();

        foreach (var property in propertiesToInject)
        {
            var objectToInject = Autofact.SharedContainer.Resolve(property.PropertyType);
            property.SetValue(obj, objectToInject, null);
        }
    }
}

Use it with this call:

AutofactResolver.InjectProperties(sourceObject);

Solution 5

Use Property Injection (In addition to @cuongle answer).

Option 1:

builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();

builder.RegisterType<Product>()
        .WithProperty("Log", LogManager.GetLogger("LoggerName"));

Option 2:

Or you can add a SetLog method to the Product class:

public class Product
{
    public static ILog Log { get; set; }
    public SetLog(Log log)
    {
        this.Log = log;
    }
}

This way you won't have to call LogManager.GetLogger("LoggerName") twice but to use the context of the builder in order to resolve the Log.

builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();

builder.Register(c => 
    var product = new Product();
    product.SetLog(c.Resolve<Log>());
    return product;
);

Option 3:

Use the OnActvated:

The OnActivated event is raised once a component is fully constructed. Here you can perform application-level tasks that depend on the component being fully constructed - these should be rare.

builder.RegisterType<Product>()
    .OnActivated((IActivatedEventArgs<Log> e) =>
    {
        var product = e.Context.Resolve<Parent>();
        e.Instance.SetParent(product);
    });

These options gives more control, and you will not have to worry about @steven comment:

The scary thing with PropertiesAutowired however is that it does implicit property injection, which means that any unresolvable dependencies will be skipped. This makes it easy to miss configuration errors and can result in application that fails at runtime

Share:
38,451
The Light
Author by

The Light

I love thinking and writing .NET applications and have over 13 years experience + MCPD, MCTS, MCAD, MCP.

Updated on January 10, 2022

Comments

  • The Light
    The Light over 2 years

    In a Console application, I'm using Log4Net and in the Main method I'm getting the logger object. Now, I'd like to make this log object available in all my classes by letting all the classes inherit from a BaseClass which has a ILog property and is supposed to be set by Property Injection rather than Constructor Injection.

    I'm using AutoFac IoC container, how to inject my log Object to the Log property of my every class?

    What's the best/easiest way to achieve this?

    Is there any way to automatically resolve types?

    Below is my test application:

    namespace ConsoleApplication1
    {
        class Program
        {
            static ILog Log;
            static IContainer Container;
    
            static void Main(string[] args)
            {                
               InitializeLogger();
    
               InitializeAutoFac();
    
                // the below works but could it be done automatically (without specifying the name of each class)?
               Product.Log = Container.Resolve<ILog>();
    
               // tried below but didn't inject ILog object into the Product
               Container.Resolve<Product>();
    
               RunTest();
    
                Console.ReadLine();
            }
    
            private static void RunTest()
            {
                var product = new Product();
                product.Do();
            }
    
            private static void InitializeAutoFac()
            {
                var builder = new ContainerBuilder();
    
                builder.Register(c => Log).As<ILog>();
    
                builder.RegisterType<Product>().PropertiesAutowired();
    
                Container = builder.Build();            
            }
    
            private static void InitializeLogger()
            {
                log4net.Config.XmlConfigurator.Configure();
    
                Log = LogManager.GetLogger("LoggerName");
            }
        }
    
        public class Product
        {
            public static ILog Log { get; set; }
    
            public void Do()
            {
                // this throws exception because Log is not set   
                Log.Debug("some Debug");  
            }
        }
    }