2011-06-07 44 views
20

如何解決基於服務接口我有一個接口。在那裏的傳遞給

public interface ISomeInterface {...} 

和兩個實現(SomeImpl1和SomeImpl2):

public class SomeImpl1 : ISomeInterface {...} 
public class SomeImpl2 : ISomeInterface {...} 

我也有兩個服務在那裏我(通過構造器)注入ISomeInterface:

public class Service1 : IService1 
{ 
    public Service1(ISomeInterface someInterface) 
    { 
    } 
... 
} 

public class Service2 : IService2 
{ 
    public Service2(ISomeInterface someInterface) 
    { 
    } 
... 
} 

我使用AUT ofac作爲我的IoC工具。 這個問題。如何配置Autofac註冊這樣SomeImpl1將自動注入到服務1,和SomeImpl2將自動注入到服務2。

謝謝!

回答

28

Autofac支持identification of services by name。使用這個,你可以註冊你的實現名稱(使用Named擴展方法)。然後,您可以按名稱IServiceX註冊代表解決這些問題,使用ResolveNamed擴展方法。以下代碼演示了這一點。

var cb = new ContainerBuilder(); 
cb.Register(c => new SomeImpl1()).Named<ISomeInterface>("impl1"); 
cb.Register(c => new SomeImpl2()).Named<ISomeInterface>("impl2"); 
cb.Register(c => new Service1(c.ResolveNamed<ISomeInterface>("impl1"))).As<IService1>(); 
cb.Register(c => new Service2(c.ResolveNamed<ISomeInterface>("impl2"))).As<IService2>(); 
var container = cb.Build(); 

var s1 = container.Resolve<IService1>();//Contains impl1 
var s2 = container.Resolve<IService2>();//Contains impl2 

使用RegisterType(相對於Register

可以使用在組合RegisterType擴展方法與WithParameterResolvedParameter實現相同的結果替代。這是有用的,如果構造採取命名的參數也需要你不在乎在註冊委託指定其他非命名參數:

var cb = new ContainerBuilder(); 
cb.RegisterType<SomeImpl1>().Named<ISomeInterface>("impl1"); 
cb.RegisterType<SomeImpl2>().Named<ISomeInterface>("impl2"); 
cb.RegisterType<Service1>().As<IService1>().WithParameter(ResolvedParameter.ForNamed<ISomeInterface>("impl1")); 
cb.RegisterType<Service2>().As<IService2>().WithParameter(ResolvedParameter.ForNamed<ISomeInterface>("impl2")); 
var container = cb.Build(); 

var s1 = container.Resolve<IService1>();//Contains impl1 
var s2 = container.Resolve<IService2>();//Contains impl2 
+0

感謝您的答覆。它工作正常。但是我的SomeImpl1和SomeImpl2依賴於很多其他服務。我不想在註冊時指定所有人。有沒有辦法在註冊期間不創建實例來實現它:new SomeImpl1()? – 2011-06-07 12:02:47

+0

我的意思是:builder.RegisterType ().As ()。([如何在Service1的第一個參數中指定「impl1」?]); – 2011-06-07 12:05:11

+0

我已經更新了答案,顯示瞭如何使用'RegisterType'獲得同樣的結果。 – bentayloruk 2011-06-07 13:11:00

4

如果你可以從構造函數注入切換到財產注入,讓這兩種服務從同一個基類派生(或實現相同的接口),你可以做到以下幾點:

builder.RegisterType<ServiceBase>().OnActivating(e => 
{ 
    var type = e.Instance.GetType(); 

    // get ISomeInterface based on instance type, i.e.: 
    ISomeInterface dependency = 
     e.Context.ResolveNamed<ISomeInterface>(type.Name); 

    e.Instance.SomeInterface = dependency; 
}); 

對於這個工作,你需要定義的基本類型(或接口)的屬性。該解決方案非常靈活,甚至可以讓你到複雜的東西,如注入泛型類型,基於父類型,可參見下圖:

builder.RegisterType<ServiceBase>().OnActivating(e => 
{ 
    var type = 
     typeof(GenericImpl<>).MakeGenericType(e.Instance.GetType()); 

    e.Instance.SomeInterface = (ISomeInterface)e.Context.Resolve(type); 
}); 

這種方法有幾個缺點:

  1. 我們需要財產注射。
  2. 我們需要一個包含該屬性的基類型或接口。
  3. 我們需要反射來構建類型,這可能會影響性能(而不是容器構建高效的委託)(儘管我們可以通過緩存類型來加快速度)。

這個設計很簡單,適用於幾乎所有的容器。

2

四個這樣的變體autofac documentation描述:

選項1:重新設計的接口

當你遇到,你必須實現相同的服務一堆部件 的情況但它們不能同等對待,這通常是一個界面設計問題。

從面向對象的開發角度來看,你會希望你的 對象遵守Liskov替換原則和這種類型的 。

通過做一些界面重新設計,您不必「通過上下文選擇 依賴關係」 - 您可以使用類型進行區分,並在解析過程中使用 自動連線魔術。

如果您有能力影響解決方案的更改,這是 建議的選項。

選項2:變更中的註冊

您可以適當的類型,在這種方式,消耗組件手動關聯:

var builder = new ContainerBuilder(); 
builder.Register(ctx => new ShippingProcessor(new PostalServiceSender())); 
builder.Register(ctx => new CustomerNotifier(new EmailNotifier())); 
var container = builder.Build(); 

// Lambda registrations resolve based on the specific type, not the 
// ISender interface. 
builder.Register(ctx => new ShippingProcessor(ctx.Resolve<PostalServiceSender>())); 
builder.Register(ctx => new CustomerNotifier(ctx.Resolve<EmailNotifier>())); 
var container = builder.Build(); 

選項3:使用鍵控服務

builder.RegisterType<PostalServiceSender>() 
      .As<ISender>() 
      .Keyed<ISender>("order"); 
    builder.RegisterType<EmailNotifier>() 
      .As<ISender>() 
      .Keyed<ISender>("notification"); 

builder.RegisterType<ShippingProcessor>() 
      .WithParameter(
      new ResolvedParameter(
       (pi, ctx) => pi.ParameterType == typeof(ISender), 
       (pi, ctx) => ctx.ResolveKeyed<ISender>("order"))); 
    builder.RegisterType<CustomerNotifier>(); 
      .WithParameter(
      new ResolvedParameter(
       (pi, ctx) => pi.ParameterType == typeof(ISender), 
       (pi, ctx) => ctx.ResolveKeyed<ISender>("notification"))); 

選擇4:利用元數據

builder.RegisterType<PostalServiceSender>() 
      .As<ISender>() 
      .WithMetadata("SendAllowed", "order"); 
    builder.RegisterType<EmailNotifier>() 
      .As<ISender>() 
      .WithMetadata("SendAllowed", "notification"); 

builder.RegisterType<ShippingProcessor>() 
      .WithParameter(
      new ResolvedParameter(
       (pi, ctx) => pi.ParameterType == typeof(ISender), 
       (pi, ctx) => ctx.Resolve<IEnumerable<Meta<ISender>>>() 
           .First(a => a.Metadata["SendAllowed"].Equals("order")))); 
    builder.RegisterType<CustomerNotifier>(); 
      .WithParameter(
      new ResolvedParameter(
       (pi, ctx) => pi.ParameterType == typeof(ISender), 
       (pi, ctx) => ctx.Resolve<IEnumerable<Meta<ISender>>>() 
           .First(a => a.Metadata["SendAllowed"].Equals("notification"))));