前面談過傳入建構參數,但並非所有物件參數都可由建構式傳入,有些要透過屬性指定(例如: new MyObject() { SomeProperty = SomeValue };),而這也是IoC/DI的工作職掌之一,專業術語叫Property Injection(屬性注入)。
解說前先介紹幾個測試用類別: Worker類別有個屬性Logger,接受實作ILogger介面的記錄元件;我們簡單寫個Logger類別實作ILogger,將訊息輸出到Console敷衍兩下湊數。
using System;
publicclass Worker
{
public ILogger Logger { get; set; }
publicvoid DoSomething(string command)
{
Console.WriteLine("JOB:" + command);
Logger.Log(command);
}
}
publicinterface ILogger
{
void Log(string msg);
}
publicclass Logger : ILogger
{
publicvoid Log(string msg)
{
Console.WriteLine("LOG:" + msg);
}
}
Autofac指定屬性的方法有三種。第一種的做法是透過Register()自訂物件建立細節,Register() Lambda所傳入的c即為Autofac容器(IContainer或ILifetimeScope),可透過c.ResolveType<ILogger>取得已註冊的ILogger實作。
privatestaticvoid test1()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>();
//方法自訂建構程序,傳回物件。建立物件時一併指定Property
builder.Register(c =>
new Worker() {
Logger = c.Resolve<ILogger>()
});
IContainer container = builder.Build();
var worker = container.Resolve<Worker>();
worker.DoSomething("Wash the dog");
}
第二種做法是利用Autofac物件建立事件OnActivated,於物件建立完成後指定。OnActivated事件傳入參數的Instance屬性為剛建好的物件,而Context屬性則為Autofac容器。(PS: 除了OnActivated,還有OnActivating事件可以置換Instance、注入屬性或進行其他初始化;OnRelease事件則可取代物件原有的Dispose()邏輯,提供良好的自訂彈性,細節可參考文件)
privatestaticvoid test2()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>();
//利用OnActivated事件,物件建立後指定Property
//OnActivated事件會傳入IActivatedEventArgs,
//其中的Instance為剛建好的物件、Context為IContainer或ILifetimeScope容器
builder.RegisterType<Worker>().OnActivated(
e => e.Instance.Logger = e.Context.Resolve<ILogger>());
IContainer container = builder.Build();
var worker = container.Resolve<Worker>();
worker.DoSomething("Wash the dog");
}
第三種方法我覺得最酷!
RegisterType()時直接加上PropertyAutowired(),則Autofac建立物件時將一併掃瞄物件所有屬性,只要該屬性型別已被註冊,就自動產生(或取得)Instance傳入,即便事後增加Property也無需更動註冊程序,算是貫徹了IoC/DI的精神,深得我心。
privatestaticvoid test3()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>();
//透過PropertyAutowired()交由Autofac自動解析
builder.RegisterType<Worker>().PropertiesAutowired();
IContainer container = builder.Build();
var worker = container.Resolve<Worker>();
worker.DoSomething("Wash the dog");
}