Подтвердить что ты не робот

Как использовать функцию Injection с AutoFac?

В приложении "Консоль" я использую Log4Net, а в основном методе я получаю объект журнала. Теперь я хотел бы сделать этот лог-объект доступным во всех моих классах, разрешив всем классам наследовать от BaseClass, который имеет свойство ILog, и должен быть установлен с помощью Property Injection, а не с инжектором конструктора.

Я использую контейнер AutoFac IoC, как приложить мой объект журнала к свойству Log каждого моего класса?

Какой лучший/самый простой способ достичь этого?

Есть ли способ автоматически разрешать типы?

Ниже мое тестовое приложение:

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");  
        }
    }
}
4b9b3361

Ответ 1

Используйте Вложение свойств:

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

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

Ответ 2

На мой взгляд, решение Ninject создано намного лучше, чем propertyinjection в Autofac. Поэтому я создал настраиваемый атрибут, который является аспектом postsharp, который автоматически вводит мои классы:

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

Мой аспект:

[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();
        }
    }
}

Я знаю, что ответ на вопрос уже дан, но я подумал, что это был действительно опрятный способ решения автоматической инъекции свойств в Autofac. Возможно, это будет полезно кому-то в будущем.

Ответ 3

Включение свойств для Свойства, а не для Поля. В вашем классе Log является полем, а не свойством, и поэтому он никогда не будет разрешен Autofac.

Ответ 4

Я не хотел использовать postharp, поэтому я сделал быстрое решение, но оно не запускается автоматически. Я новичок в Autofac, и на этом пути должно быть возможно.

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

Используйте его с этим вызовом:

AutofactResolver.InjectProperties(sourceObject);

Ответ 5

Используйте свойство Injection (В дополнение к ответу @cuongle).

Опция 1:

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

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

Вариант 2:

Или вы можете добавить метод SetLog в класс Product:

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

Таким образом, вам не придется LogManager.GetLogger("LoggerName") вызывать LogManager.GetLogger("LoggerName") а использовать контекст компоновщика для разрешения Log.

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

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

Вариант 3:

Используйте OnActvated:

Событие OnActivation возникает, когда компонент полностью создан. Здесь вы можете выполнять задачи на уровне приложения, которые зависят от полностью создаваемого компонента - это должно быть редко.

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

Эти параметры дают больше контроля, и вам не придется беспокоиться о комментарии @steven:

Однако в PropertiesAutowired страшно то, что он неявно внедряет свойства, что означает, что любые неразрешимые зависимости будут пропущены. Это позволяет легко пропустить ошибки конфигурации и может привести к сбою приложения во время выполнения