How to use Property Injection with AutoFac?

a 夏天 提交于 2019-11-27 13:55:50

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.

cuongle

Use Property Injection:

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

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

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.

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);

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!