给定一个ContainerBuilder,我可以注册一个缺少的依赖处理程序吗?

hal*_*r73 9 c# autofac

我正在尝试使用以下签名创建一个方法:

void Chain(ContainerBuilder builder, IServiceProvider fallbackServiceProvider)
{
   // ...
}
Run Code Online (Sandbox Code Playgroud)

这个想法是Chain可以使用如下:

IServiceProvider fallbackProvider = someExternalProvider;
var builder = new ContainerBuilder();

// Custom registration might happen before and/or after the call to Chain
builder.Register<MyCustomService>().As<IMyCustomService>();
builder.Register<MyExternalServiceReplacement>.As<IExternalService>();

Chain(builder, someExternalProvider);

IContainer container = builder.Build();

// customService should be a MyCustomService
var customService = container.Resolve<IMyCustomService>();

// replacedService should be overridden by MyExternalServiceReplacement
// even though an IExternalService also exists in someExternalProvider
var replacedService = container.Resolve<IExternalService>();

// nonReplacedService should come from someExternalProvider since
// no IExternalService2 was registered with the ContainerBuilder
var nonReplacedService = container.Resolve<IExternalService2>();
Run Code Online (Sandbox Code Playgroud)

理想情况下,我可以使用ContainerBuilder注册某种类型的缺失依赖处理程序.

另外,我大概可以用某种方式获得通过注册可能会截取每次调用的组件Resolve*,TryResolve*等等...这也需要拦截的构造函数注入依赖解析.

不幸的是,没有办法查询IServiceProvider来获取它提供的每个服务.我只能调用object IServiceProvider.GetService(Type serviceType)方法了fallbackServicProvider.

nem*_*esv 8

您需要一个自定义IRegistrationSource实现:当容器需要提供服务时,它会查询已注册的注册源以获取任何可用的实现.

因此,在注册源中,您可以要求您IServiceProvider为给定类型提供回退实现.

这是一篇很好的文章,介绍了Autofac中的整个注册源:Autofac 2中的声明性上下文适配器

基于此,我将原型IRegistrationSource实现(因此未完全测试或生产就绪,但它正在使用您的示例场景)组合在一起,您可以构建的内容:

public class MyRegistrationSource : IRegistrationSource
{
    private readonly IServiceProvider serviceProvider;

    public MyRegistrationSource(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service,
        Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        // there are other registration exists in the container
        if (registrationAccessor(service).Any())
            return Enumerable.Empty<IComponentRegistration>();

        var swt = service as IServiceWithType;
        if (swt == null)
            return Enumerable.Empty<IComponentRegistration>();

        // try to get an instance from the IServiceProvider
        var instance = serviceProvider.GetService(swt.ServiceType);
        if (instance == null)
            return Enumerable.Empty<IComponentRegistration>();

        // register the instance in the container
        return new[]
            {
                RegistrationBuilder.ForDelegate(swt.ServiceType, 
                    (c, p) => instance)
                    .CreateRegistration()
            };
    }
    public bool IsAdapterForIndividualComponents { get { return false; } }
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

 var builder = new ContainerBuilder();

 // Custom registration might happen before and/or after the call to Chain
 builder.RegisterType<MyCustomService>().As<IMyCustomService>();
 builder.RegisterType<MyExternalServiceReplacement>().As<IExternalService>();

 //Chain(builder, someExternalProvider);
 builder.RegisterSource(new MyRegistrationSource(new ServiceProvider()));

 IContainer container = builder.Build();
Run Code Online (Sandbox Code Playgroud)