前面談過傳入建構參數,但並非所有物件參數都可由建構式傳入,有些要透過屬性指定(例如: new MyObject() { SomeProperty = SomeValue };),而這也是IoC/DI的工作職掌之一,專業術語叫Property Injection(屬性注入)。

解說前先介紹幾個測試用類別: Worker類別有個屬性Logger,接受實作ILogger介面的記錄元件;我們簡單寫個Logger類別實作ILogger,將訊息輸出到Console敷衍兩下湊數。

using System;
public class Worker
{
    public ILogger Logger { get; set; }
    public void DoSomething(string command)
    {
        Console.WriteLine("JOB:" + command);
        Logger.Log(command);
    }
}
public interface ILogger
{
    void Log(string msg);
}
public class Logger : ILogger
{
    public void Log(string msg)
    {
        Console.WriteLine("LOG:" + msg);
    }
}

Autofac指定屬性的方法有三種。第一種的做法是透過Register()自訂物件建立細節,Register() Lambda所傳入的c即為Autofac容器(IContainer或ILifetimeScope),可透過c.ResolveType<ILogger>取得已註冊的ILogger實作。

private static void 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()邏輯,提供良好的自訂彈性,細節可參考文件)

private static void 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的精神,深得我心。

private static void 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");
}

Comments

Be the first to post a comment

Post a comment