如何根据传递到的服务来解析接口

时间:2011-06-07 08:43:25

标签: c# autofac

我有一个界面。

public interface ISomeInterface {...}

和两个实现(SomeImpl1和SomeImpl2):

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

我还有两个服务,我注入了ISomeInterface(通过contructor):

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

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

我正在使用Autofac作为我的IoC工具。 这个问题。如何配置Autofac注册,以便SomeImpl1自动注入Service1,SomeImpl2将自动注入Service2。

谢谢!

3 个答案:

答案 0 :(得分:32)

Autofac支持identification of services by name。使用此功能,您可以使用名称注册实现(使用Named扩展方法)。然后,您可以使用ResolveNamed扩展方法在IServiceX注册代理中按名称解析它们。以下代码演示了这一点。

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

答案 1 :(得分: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. 我们需要反思来构建可能会影响性能的类型(而不是构建高效委托的容器)(尽管我们可以通过缓存类型来加快速度)。
  4. 从好的方面来看,这种设计很简单,适用于几乎所有容器。

答案 2 :(得分: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"))));