I am trying to make a method with the following signature:
void Chain(ContainerBuilder builder, IServiceProvider fallbackServiceProvider)
{
// ...
}
The idea is that Chain
can be used as follows:
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>();
Ideally there would be some type of missing dependency handler that I could register with the ContainerBuilder.
Alternatively, I could probably get by with some way to register a component that could intercept every call to Resolve*
, TryResolve*
, etc... This would also need to intercept the dependency resolution for constructor injection.
Unfortunately, there is no way to query the IServiceProvider to get every service it provides. I can only call into the object IServiceProvider.GetService(Type serviceType)
method of the fallbackServicProvider
.
You need a custom IRegistrationSource
implementation: when the container needs to provide a service, it queries the registered registration sources to get any available implementations.
So inside the registeration source you can ask your IServiceProvider
to give you a fallback implementation for a given type.
Here is a good article intoroducing the whole registration source in Autofac: Declarative Context Adapters in Autofac 2
Based on that I've knocked together a prototype IRegistrationSource
implementation (so it is not fully tested nor production ready, but it was working with your sample scenario) what you can build on:
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; } }
}
And you can use it like this:
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();