Autofac 是一款優秀的IOC的開源工具,完美的適配.Net特性,可是有時候咱們想經過屬性注入的方式來獲取咱們注入的對象,對不起,有時候你還真是獲取不到,這由於什麼呢?架構
1.你對Autofac 不太瞭解,在這個浮躁的社會,沒有人會認真的瞭解每一個開源項目,只要求能用就行ide
2.沒有時間瞭解,你是一個很忙的人,工做很忙,應酬很忙工具
3.剛開始使用Autofac 還沒來得及深刻了解就要作項目。ui
無論是什麼緣由,總之咱們注入的屬性就是沒法直接由autofac 自動注入,或者說咱們但願由Autofac自動注入的屬性爲Null,這但是很讓咱們糾結的事情。下面咱們就來經過一系列咱們可能的操做來還原事情的真相。this
真相1:經過registerType註冊類型,但願經過屬性獲取注入的類型。spa
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注入呢,是否可行呢?code
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來實現。component
真相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知道類型的存在,纔可能會自動注入。
這是一篇說明文,簡短的說明,但願沒有高深的知識點,可是若是你不瞭解,會花費很長時間纔可能查找到錯誤的地方。應驗了那句話,知道了不難,不知道了難上加難。
個人座右銘:作架構師,要作牛逼的架構師。