zoukankan      html  css  js  c++  java
  • Autofac 的属性注入,IOC的坑

     Autofac 是一款优秀的IOC的开源工具,完美的适配.Net特性,但是有时候我们想通过属性注入的方式来获取我们注入的对象,对不起,有时候你还真是获取不到,这因为什么呢?

    1.你对Autofac 不太了解,在这个浮躁的社会,没有人会认真的了解每个开源项目,只要求能用就行

    2.没有时间了解,你是一个很忙的人,工作很忙,应酬很忙

    3.刚开始使用Autofac 还没来得及深入了解就要做项目。

    不管是什么原因,总之我们注入的属性就是无法直接由autofac 自动注入,或者说我们希望由Autofac自动注入的属性为Null,这可是很让我们纠结的事情。下面我们就来通过一系列我们可能的操作来还原事情的真相。

    真相1:通过registerType注册类型,希望通过属性获取注入的类型。

     1   class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             ContainerBuilder builder = new ContainerBuilder();
     6          //   builder.RegisterModule(new LoggingModule());
     7             builder.RegisterType<Test>();
     8             builder.RegisterType<Test2>();
     9             var container = builder.Build();
    10 
    11             Test2 test2 = container.Resolve<Test2>();
    12             test2.Show();
    13 
    14         }
    15     }
    16 
    17     public class Test {
    18         public void Show()
    19         {
    20             Console.WriteLine("FileName");
    21         }
    22     }
    23 
    24     public class Test2
    25     {
    26         public Test Test { get; set; }
    27 
    28         public void Show()
    29         {
    30             if (Test != null)
    31             {
    32                 Test.Show();
    33             }
    34         }
    35     }

    我们通过RegisterType注入了两个类型Test和Test2,其中Test2中由一个属性为Test类型的Test变量,我们期望Test会自动注入,我们可以直接使用Test.Show方法,但是现实情况是:

    我们期望会被Autofac自动注入的属性为Null,我们的期望落空。既然通过RegisterType无法注入,那么通过Register注入呢,是否可行呢?

     1  class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             ContainerBuilder builder = new ContainerBuilder();
     6          //   builder.RegisterModule(new LoggingModule());
     7             //builder.RegisterType<Test>();
     8             
     9 
    10             builder.Register(t => new Test()).As<Test>();
    11             builder.RegisterType<Test2>();
    12             var container = builder.Build();
    13 
    14             Test2 test2 = container.Resolve<Test2>();
    15             test2.Show();
    16 
    17         }
    18     }
    19 
    20     public class Test {
    21         public void Show()
    22         {
    23             Console.WriteLine("FileName");
    24         }
    25     }
    26 
    27     public class Test2
    28     {
    29         public Test Test { get; set; }
    30 
    31         public void Show()
    32         {
    33             if (Test != null)
    34             {
    35                 Test.Show();
    36             }
    37         }
    38     }

    我们通过Register注入一个实例,最后我们的期望还是落空了,还有一种方式就是通过Module进行注册,这种方式还不行,那就说明autofac的属性注入式骗人的(心里想的),我们来通过Module来实现。

    真相3:通过module进行注册

      1 class Program
      2     {
      3         static void Main(string[] args)
      4         {
      5             ContainerBuilder builder = new ContainerBuilder();
      6             builder.RegisterModule(new LoggingModule());
      7             //builder.RegisterType<Test>();
      8             
      9 
     10             //builder.Register(t => new Test()).As<Test>();
     11             //builder.RegisterType<Test2>();
     12             var container = builder.Build();
     13 
     14             //Test2 test2 = container.Resolve<Test2>();
     15             Test2 ee = new Test2();
     16             ee.Show();
     17 
     18         }
     19     }
     20 
     21     public class Test {
     22         public void Show()
     23         {
     24             Console.WriteLine("FileName");
     25         }
     26     }
     27 
     28     public class Test2
     29     {
     30         public Test Test { get; set; }
     31 
     32         public void Show()
     33         {
     34             if (Test != null)
     35             {
     36                 Test.Show();
     37             }
     38         }
     39     }
     40     public class LoggingModule : Module
     41     {
     42         private readonly ConcurrentDictionary<string, Test> _loggerCache;
     43 
     44         public LoggingModule()
     45         {
     46             _loggerCache = new ConcurrentDictionary<string, Test>();
     47         }
     48 
     49         protected override void Load(ContainerBuilder moduleBuilder)
     50         {
     51             // by default, use Coevery's logger that delegates to Castle's logger factory
     52             moduleBuilder.RegisterType<Test>().As<Test>().InstancePerLifetimeScope();
     53 
     54 
     55             // call CreateLogger in response to the request for an ILogger implementation
     56            // moduleBuilder.Register(CreateLogger).As<ILogging.ILogger>().InstancePerDependency();
     57 
     58         }
     59 
     60         protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
     61         {
     62             var implementationType = registration.Activator.LimitType;
     63 
     64             // build an array of actions on this type to assign loggers to member properties
     65             var injectors = BuildLoggerInjectors(implementationType).ToArray();
     66 
     67             // if there are no logger properties, there's no reason to hook the activated event
     68             if (!injectors.Any())
     69                 return;
     70 
     71             // otherwise, whan an instance of this component is activated, inject the loggers on the instance
     72             registration.Activated += (s, e) =>
     73             {
     74                 foreach (var injector in injectors)
     75                     injector(e.Context, e.Instance);
     76             };
     77         }
     78 
     79         private IEnumerable<Action<IComponentContext, object>> BuildLoggerInjectors(Type componentType)
     80         {
     81             // Look for settable properties of type "ILogger" 
     82             var loggerProperties = componentType
     83                 .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
     84                 .Select(p => new
     85                 {
     86                     PropertyInfo = p,
     87                     p.PropertyType,
     88                     IndexParameters = p.GetIndexParameters(),
     89                     Accessors = p.GetAccessors(false)
     90                 })
     91                 .Where(x => x.PropertyType == typeof(Test)) // must be a logger
     92                 .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer
     93                 .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set
     94 
     95             // Return an array of actions that resolve a logger and assign the property
     96             foreach (var entry in loggerProperties)
     97             {
     98                 var propertyInfo = entry.PropertyInfo;
     99 
    100                 yield return (ctx, instance) =>
    101                 {
    102                     string component = componentType.ToString();
    103                     var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<Test>(new TypedParameter(typeof(Type), componentType)));
    104                     propertyInfo.SetValue(instance, logger, null);
    105                 };
    106             }
    107         }

    我们通过Module注册了Test,但是我们通过断点调试可以看到,我们通过属性注入的还是没有得到,还是为Null,这是不是autofac不支持属性注入,我们在心里只骂坑爹啊。

    但是我们仔细看一下会发现一个问题,我们的Test2 是通过New得到的,而不是通过autofac得到,我们并没有将Test2注入到autofac中,是不是因为这个愿意呢?

    我们来尝试一下,这可是我最后的机会了,因为除了这个原因我实在想不出还有什么别的原因。

     1  static void Main(string[] args)
     2         {
     3             ContainerBuilder builder = new ContainerBuilder();
     4             builder.RegisterModule(new LoggingModule());
     5             //builder.RegisterType<Test>();
     6 
     7 
     8             //builder.Register(t => new Test()).As<Test>();
     9             builder.RegisterType<Test2>();
    10             var container = builder.Build();
    11 
    12             Test2 test2 = container.Resolve<Test2>();
    13             // Test2 ee = new Test2();
    14             test2.Show();
    15 
    16         }

    我们修改了一下代码,将Test2也注入到Autofac中,然后通过autofac的resolve获取,奇迹出现了,我们看到了我们注册的类型,在属性注入得到了我们想要的实例。

    这不得不说是一个令我激动的事情,因为这个属性得到的实在是太难,让我尝试了很多种,经历了很多次绝望。

    所以我发现,如果你要想实现autofac的自动属性注入,由三个步骤,第一个通过module注册你要通过属性获取的类型,第二个,在属性所在的class中,也要注册到autofac中,最后一点,获取属性所在的class的实例必须通过autofac获取,也就是绝对不要通过new来获取,因为autofac的存在就是为了让你在一定程度上减少new的使用。

    我们使用过autofac的MVC实现,我们发现在controller中可以得到我们的属性值,那是因为controller已经注册到了autofac中,因为肯定有一句builder。registerController的存在。

    所在对于想实现autofac自动属性注入的朋友,一定要记得将类型通过module注入到autofac。并且属性所在的类型必须通过autofac获取,因为我们必须让autofac知道类型的存在,才可能会自动注入。

    这是一篇说明文,简短的说明,希望没有高深的知识点,但是如果你不了解,会花费很长时间才可能查找到错误的地方。应验了那句话,知道了不难,不知道了难上加难。

    我的座右铭:做架构师,要做牛逼的架构师。

  • 相关阅读:
    20000+关注,开源两本硬核的原创电子书!
    Tail Latency学习
    Zabbix5.0 监控redis
    JAVA多线程(九) ForkJoin框架
    JAVA多线程(八) Condition源码分析
    程序员英语学习(二) 标点符号对应的英语单词汇总
    linux shell快速入门
    Ubuntu常用指令和快捷键汇总
    Win10常用快捷键汇总
    算法路漫漫(三) 荷兰国旗
  • 原文地址:https://www.cnblogs.com/jiagoushi/p/4084145.html
Copyright © 2011-2022 走看看