【问题标题】:How to use Property Injection with AutoFac?如何在 AutoFac 中使用属性注入?
【发布时间】:2013-03-24 15:38:33
【问题描述】:

在控制台应用程序中,我使用的是 Log4Net,并且在 Main 方法中我正在获取记录器对象。现在,我想通过让所有类从具有 ILog 属性并且应该由属性注入而不是构造函数注入设置的 BaseClass 继承来使这个日志对象在我的所有类中可用。

我正在使用 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");  
        }
    }
}

【问题讨论】:

  • 您还可以在每个类中创建一个静态记录器实例。这样,每个记录器将自动具有定义它的类的名称。
  • Product的Ilog中删除static
  • 我也尝试过成为实例,但没有帮助。
  • 日志(几乎)永远不需要由 DI 容器管理。直接用log4net就好了。
  • @default.kramer 我一直看到这种说法,但我不同意。根据我的经验,日志记录是应该抽象的东西的完美示例。使用像 Log4Net 这样的实现会直接将您与该实现耦合。

标签: c# asp.net inversion-of-control log4net autofac


【解决方案1】:

在我看来,解决方案 Ninject created 比 Autofac 中的 propertyinjection 好得多。因此,我创建了一个自定义属性,它是一个 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 中自动属性注入的一种非常巧妙的方法。也许它将来对某人有用。

【讨论】:

  • 你有没有 postsharp 的解决方案吗?
  • 不,但我认为您应该能够通过从 System.Attribute 继承并自己构建此功能来自己创建自定义属性。
  • 在 mvvm WPF 中没有可用的dependencyResolver 如何使用 Postsharp /Autofac/ MVVM 实现相同的效果?
  • 检查位置类型是否为接口应该在方面的CompileTimeValidate方法中实现,因此检查导致构建时错误而不是运行时错误。见doc.postsharp.net/aspect-validation
  • 因创意而受到赞誉。是的,您编写的代码更少,但是这不是一个非常松散耦合的解决方案。解决方案依赖于 MVC 的 DependencyResolver 类。如果属性属于数据层中的一个类,这将不起作用。
【解决方案2】:

使用Property Injection:

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

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

【讨论】:

  • 谢谢,但是这里的 Log 类是什么/在哪里?我只有我认为的 ILog 界面。从 LogManager.GetLogger("LoggerName"); 中检索 ILog 对象;
  • 非常感谢,我现在可以通过 container.Resolve() 从容器中查看日志对象。但是我怎么能要求 AutoFac 在它看到的任何地方自动解析 ILog 呢?当我访问 CustomClass 中的 Log 属性时,它不会自动解决它并当前引发异常。
  • @TheLight:你能尝试使用PropertiesAutowired吗?
  • 我做到了。它会正确注册它,但不会自动解析它。
  • PropertiesAutowired 的可怕之处在于它会进行隐式属性注入,这意味着将跳过任何无法解析的依赖项。这很容易错过配置错误,并可能导致应用程序在运行时失败。
【解决方案3】:

属性注入适用于Properties,不适用于Fields。在您的课程中,Log 是一个字段而不是属性,因此 Autofac 永远不会解析它。

【讨论】:

    【解决方案4】:

    我不想使用 postsharp,所以我做了一个快速的解决方案,但它不会自动注入。我是 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】:

      使用Property Injection(除了@cuongle 答案)。

      选项 1:

      builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();
      
      builder.RegisterType<Product>()
              .WithProperty("Log", LogManager.GetLogger("LoggerName"));
      

      选项 2:

      或者您可以在Product 类中添加一个SetLog 方法:

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

      这样您就不必调用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

      一旦组件完全构建,就会引发 OnActivated 事件。 在这里,您可以执行依赖于 组件正在完全构建 - 这些应该很少见。

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

      这些选项提供了更多控制权,您不必担心@steven 评论:

      PropertiesAutowired 的可怕之处在于它确实 隐式属性注入,这意味着任何无法解析的 将跳过依赖项。这使得很容易错过配置 错误并可能导致应用程序在运行时失败

      【讨论】:

        【解决方案6】:

        有一个接口IPropertySelector 可以实现并通过.PropertiesAutowired(new MyPropertySelector()) 传递实现。这将允许你实现任何你想要的逻辑。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-12-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-26
          • 1970-01-01
          • 1970-01-01
          • 2012-09-21
          • 1970-01-01
          相关资源
          最近更新 更多