2011-06-07 48 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"))));