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知道類型的存在,才可能會自動注入。

 

這是一篇說明文,簡短的說明,希望沒有高深的知識點,但是如果你不了解,會花費很長時間才可能查找到錯誤的地方。應驗了那句話,知道了不難,不知道了難上加難。

 

我的座右銘:做架構師,要做牛逼的架構師。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM