Lets say I have the following service and components:
public interface IService
{
void DoWork();
}
public class ServiceA : IService
{
private readonly string _name;
public ServiceA(string name)
{
_name = name;
}
public void DoWork()
{
//ServiceA DoWork implementation
}
}
public class ServiceB : IService
{
private readonly string _name;
public ServiceB(string name)
{
_name = name;
}
public void DoWork()
{
//ServiceB DoWork implementation
}
}
Notice that each component takes a constructor parameter name
. Lets also say that name
is determined at runtime.
I've been going through the AutoFac documentation to try to find a type-safe way to resolve components like this without having to reference the container directly. If I had only one implementation of IService
then I could just use a Delegate Factory to pass the runtime parameter to the constructor. However, I have two implementations, and the one that should be used must also be determined at runtime. If I didn't have the name
constructor parameter then I could register the two components by Key and the resolve using IIndex
I can't figure out how to do both. Is there way I can somehow combine the use of Delegate Factories and IIndex
component resolution? Or is there another way to register and resolve both components without having to directly reference the container?
As you say, your two individual requirements are natively supported by AutoFac.
However, there doesn't appear to be direct support for using these two constructs together. I.e. the following does not work:
public enum ServiceType
{
ServiceA,
ServiceB
}
public class MyComponent
{
public MyComponent(Func<string, IIndex<ServiceType, IService> factory)
{
var service = factory("some_string")[ServiceType.ServiceA];
}
}
My work around for this has always been to move the resolution of the service to a factory per service implementation. This then works as follows:
Heres a rough-and-ready example. Note the multiple factories could be reduced to a single generic factory - but I've left it as-is for clarity:
Service implementations
public enum ServiceType
{
NotSet,
ServiceA,
ServiceB
}
public interface IService
{
string DoWork();
}
public class ServiceA : IService
{
private readonly string _name;
public ServiceA(string name)
{
_name = name;
}
public string DoWork()
{
throw new NotImplementedException();
}
}
public class ServiceB : IService
{
private readonly string _name;
public ServiceB(string name)
{
_name = name;
}
public string DoWork()
{
throw new NotImplementedException();
}
}
Service factories
public interface IServiceFactory
{
IService Create(string name);
}
public class ServiceAFactory : IServiceFactory
{
private readonly Func<string, ServiceA> _factory;
public ServiceAFactory(Func<string, ServiceA> factory)
{
_factory = factory;
}
public IService Create(string name)
{
return _factory(name);
}
}
public class ServiceBFactory : IServiceFactory
{
private readonly Func<string, ServiceB> _factory;
public ServiceBFactory(Func<string, ServiceB> factory)
{
_factory = factory;
}
public IService Create(string name)
{
return _factory(name);
}
}
Service registrations
builder.RegisterType<ServiceA>().As<ServiceA>();
builder.RegisterType<ServiceB>().As<ServiceB>();
builder.RegisterType<ServiceAFactory>().Keyed<IServiceFactory>(ServiceType.ServiceA);
builder.RegisterType<ServiceBFactory>().Keyed<IServiceFactory>(ServiceType.ServiceB);
builder.RegisterType<ComponentWithServiceDependency>().As<ComponentWithServiceDependency>();
Example usage
public class ComponentWithServiceDependency
{
private readonly IService _service;
public ComponentWithServiceDependency(IIndex<ServiceType, IServiceFactory> serviceFactories)
{
// Resolve the ServiceB service implementation,
// using the string "test" as its constructor dependency
_service = serviceFactories[ServiceType.ServiceB].Create("test");
}
public string Test()
{
return _service.DoWork();
}
}
You can use Interface Segregation.
public interface IService
{
void DoWork();
}
public interface IServiceA : IService
{
}
public interface IServiceB : IService
{
}
public class ServiceA : IServiceA
{
private readonly string _name;
public ServiceA(string name)
{
_name = name;
}
public void DoWork()
{
//ServiceA DoWork implementation
}
}
public class ServiceB : IServiceB
{
private readonly string _name;
public ServiceB(string name)
{
_name = name;
}
public void DoWork()
{
//ServiceB DoWork implementation
}
}
Then, you can inject delegate factories like so:
public class ClientA
{
public ClientA(Func<string, IServiceA> serviceAFactory, Func<string, IServiceB> serviceBFactory)
{
this.serviceAFactory = serviceAFactory;
this.serviceBFactory = serviceBFactory;
}
public CreateServices()
{
var runTimeName = "runTimeName";
var serviceA = this.serviceAFactory(runTimeName);
var serviceB = this.ServiceBFactory(runTimeName);
}
}
Autofac will generate a delegate factory for each interface you register:
public class MyModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ServiceA>()
.As<IService>()
.As<IServiceA>();
builder.RegisterType<ServiceB>()
.As<IService>()
.As<IServiceB>();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With