.NET手記-Autofac進階(注冊的概念 Registering Concepts)


通過創建ContainerBuilder並配置暴露的service(接口或者類型)來使用Autofac注冊我們的組件。

組件(Components) 可以通過反射, 對象實例,或者lambda表達式來創建. ContainerBuilder有一系列的Register()方法來實現組件的注冊。

ContainerBuilder中每個組件都能通過As()方法來暴露他們一個或多個service.

 

// Create the builder with which components/services are registered.
var builder = new ContainerBuilder();

// Register types that expose interfaces...
builder.RegisterType<ConsoleLogger>().As<ILogger>();

// Register instances of objects you create...
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

// Register expressions that execute to create objects...
builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>();

// Build the container to finalize registrations
// and prepare for object resolution.
var container = builder.Build();

// Now you can resolve services using Autofac. For example,
// this line will execute the lambda expression registered
// to the IConfigReader service.
using(var scope = container.BeginLifetimeScope())
{
  var reader = container.Resolve<IConfigReader>();
}

 

反射組件 Reflection Components

 

通過類型注冊

 

通過反射生成組件最典型的方法是通過類型注冊:

 

var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>();
builder.RegisterType(typeof(ConfigReader));

 

當使用基於反射的組件時,Autofac會自動使用容器中最可能獲取到的參數來構造實例你的類

例如,你的類有3個構造函數

 

public class MyComponent
{
    public MyComponent() { /* ... */ }
    public MyComponent(ILogger logger) { /* ... */ }
    public MyComponent(ILogger logger, IConfigReader reader) { /* ... */ }
}

 

現在使用如下方法注冊你的組件:

 

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
var container = builder.Build();

using(var scope = container.BeginLifetimeScope())
{
  var component = container.Resolve<MyComponent>();
}

 

當你解析你的組件時,Autofac發現你已經注冊了ILogger,但是沒有注冊IConfigReader。在這個例子中,第二個構造函數會被執行,因為容器中具有最符合的參數。

注意:任何你注冊的Type必須都是實類型,這意味着你不能直接注冊抽象類或者接口。應為在解析組件時,對象可能會被new實例化,而接口或者抽象類是無法直接實例化的。

 

指定構造函數 Specifying a Constructor

 

你可以通過方法UsingConstructor手動指定使用哪個構造函數,其參數為指定構造函數的參數類型。

 

var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

 

注意你提供的參數必須能在容器中獲取,不然會出現錯誤。

 

實例組件 Instance Components

 

在很多情況下,你可能想要預先生成對象實例並將它注冊到容器中。你可以通過使用RegisterInstance方法來實現:

 

var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

 

當你這么做的時候,一些事情需要被考慮到。Autofac會自動處理對象的釋放,但是你很可能想自己控制對象的生命周期,而不是讓Autofac調用對象Dispose方法。在這種情況下,你需要使用ExternallyOwned方法:

 

var output = new StringWriter();
builder.RegisterInstance(output)
       .As<TextWriter>()
       .ExternallyOwned();

 

當我們集成Autofac到現有項目中時,可能會存在容器中一些組件用到的對象的單例寫法。RegisterInstance()也用來處理這種情況,你可以通過容器來注冊它們,而不是直接使用它們:

 

builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

 

這確保了單例最后會被排除,並使用容器托管的對象來替代他。

 

表達式組件 Lambda Expression Components

 

反射是創建組件相當好的默認選擇,盡管當組件創建邏輯超出簡單構造函數調用后,事情可能會變混亂。

Autofac支持通過委托或者Lambda表達式來創建組件:

 

builder.Register(c => new A(c.Resolve<B>()));

 

提供的參數c是一個組件上下文對象(IComponentContext),在上下文中創建組件。你可以通過它從容器中解析出其他的值來輔助創建你的組件。使用它而不是使用閉包去訪問容器是很重要的,為了層疊容器能夠被正確支持。

使用此上下文參數能夠滿足額外的依賴項。在這個例子中,類型A構造函數要求的B類型參數可能還會依賴其他類型參數。表達式創建的組件暴露的默認服務是通過表達式返回類型來推斷的。

下面會給出一些例子:

 

復雜參數

 

builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));

 

屬性注入

 

builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));

 

通過參數值選擇實現方式

 

builder.Register<CreditCard>(
  (c, p) =>
    {
      var accountId = p.Named<string>("accountId");
      if (accountId.StartsWith("9"))
      {
        return new GoldCard(accountId);
      }
      else
      {
        return new StandardCard(accountId);
      }
    });

 

在這個例子中,CreditCard被兩個類實現,分別是GoldenCard和StandardCard,哪種類型被實例取決於輸入的卡號。

參數通過可選構造參數p來傳入,注冊方式可能像這樣:

 

var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));

 

范型組件 

 

Autofac支持范型,通過使用RegisterGeneric()方法:

 

builder.RegisterGeneric(typeof(NHibernateRepository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();

 

當一個匹配的Service類型被請求時,容器會自動映射它到一個相近的實現類型:

 

// Autofac將會返回NHibernateRepository<Task>類型
var tasks = container.Resolve<IRepository<Task>>();

 

指定類型服務的注冊將會覆蓋掉范型版本。

 

服務 vs 組件 Services vs. Components

 

當注冊組件時,我們需要告訴Autofac組件暴露了哪種服務。大多數情況下,會暴露組件自身的類型。

 

// This exposes the service "CallLogger"
builder.RegisterType<CallLogger>();

 

組件僅能被其暴露的服務來解析出實例,這就意味着如下:

 

// This will work because the component
// exposes the type by default:
scope.Resolve<CallLogger>();

// 這個將會失敗
// tell the registration to also expose
// the ILogger interface on CallLogger:
scope.Resolve<ILogger>();

 

你也可以使用任何數量的服務來暴露你的組件:

 

builder.RegisterType<CallLogger>()
       .As<ILogger>()
       .As<ICallInterceptor>();

 

你可以通過你暴露的服務來解析出組件實例,但這同時意味着默認服務(組件自身類型)將被覆蓋:

 

// These will both work because we exposed
// the appropriate services in the registration:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();

// 失敗
// 默認服務被覆蓋
scope.Resolve<CallLogger>();

 

如果你想為組件暴露一系列服務,同時自身類型仍然可用,你需要使用AsSelf方法:

 

builder.RegisterType<CallLogger>()
       .AsSelf()
       .As<ILogger>()
       .As<ICallInterceptor>();

 

現在所有服務均可以解析:

 

// These will all work because we exposed
// the appropriate services in the registration:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();
scope.Resolve<CallLogger>();

 

 

默認注冊 Default Registrations

 

如果有多個提供相同service的組件被注冊,Autofac默認使用最后注冊的組件。

 

builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();

 

在這個例子中,FileLogger將會成為ILogger的默認組件提供者,因為它是最后注冊的。

為了改寫這種行為,使用PreserveExistingDefaults()來修改:

 

builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>().PreserveExistingDefaults();

 

在這種情況下,ConsoleLogger將會成為默認組件提供者。

 

配置文件注冊 Configuration of Registrations

 

你可以使用定義的XML或者代碼模塊(Module)來實現孕事批量注冊或者更改。也可以使用Autofac modules進行一些動態注冊生成或者條件注冊邏輯。具體請看:http://autofac.readthedocs.org/en/latest/configuration/index.html

 

 

動態注冊 Dynamically-Provided Registrations

 

Autofac modules是最簡單的方式來引入動態注冊邏輯或簡單交叉特性。例如,你可以使用module動態地附加一個Log4net實例到解析出的一個service上。請看:http://autofac.readthedocs.org/en/latest/examples/log4net.html

 


免責聲明!

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



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