问题
I'm working with .Net 4.5, ASP.NET MVC 5, and the NuGet packages: Autofac 3.5.2 Autofac ASP.NET Web Api 5 Integration 3.0.0-rc1
I have 2 different implementations of an interface:
public class MemoryStreamService : IStreamService { ... }
public class HttpStreamService : IStreamService { ... }
Also another concrete class that uses the interface by constructor injection:
public class MainService : IMainService
{
public MainService(IStreamService streamService) { ... }
}
And I have 2 ASP.NET Web API Controllers using the main service by constructor injection too:
public class MemoryStreamController : ApiController
{
public MemoryStreamController(IMainService mainService) { ... }
}
public class HttpStreamController : ApiController
{
public HttpStreamController(IMainService mainService) { ... }
}
What I want is to use the MemoryStreamService implementation when an instance of IStreamService need to be instantiated in the context of a call to MemoryStreamController, and the HttpStreamService implementation when we are in the context of a call to the HttpStreamController.
I'm trying to accomplish that by registering the controllers and service classes in Autofac builder at Application_Start() within Global.asax.cs in the following way:
builder.RegisterType<MainService>().As<IMainService>();
builder.RegisterType<MemoryStreamService>()
.As<IStreamService>()
.InstancePerApiControllerType(typeof(MemoryStreamController));
builder.RegisterType<HttpStreamService>()
.As<IStreamService>()
.InstancePerApiControllerType(typeof(HttpStreamController));
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
But looks like the InstancePerApiControllerType() does not work for that: I'm getting always the first registered implementation Type of the IStreamService (in this sample, MemoryStreamController) for both Controllers.
How do I need to configure Autofac for getting an instance of MemoryStreamService in the context of a request handled by MemoryStreamController, and an instance of HttpStreamService for calls hadled by HttpStreamController?
EDIT: If I weren't using dependency injection for the controllers, this is what I would do:
public class MemoryStreamController : ApiController
{
public MemoryStreamController()
{
this.mainService = new MainService(new MemoryStreamService());
}
}
public class HttpStreamController : ApiController
{
public HttpStreamController()
{
this.mainService = new MainService(new HttpStreamService());
}
}
So my question can be read as: How can I achieve the same by using Autofac for registering both my controllers and all my services?
回答1:
How about this. First you register your services as named:
builder.RegisterType<MemoryStreamService>()
.Named<IStreamService>("MemoryStreamService")
.InstancePerApiControllerType(typeof(MemoryStreamController));
builder.RegisterType<HttpStreamService>()
.Named<IStreamService>("HttpStreamService")
.InstancePerApiControllerType(typeof(HttpStreamController));
And then you register a func factory that can resolve the correct service based on a name. Like this:
builder.Register<Func<string, IStreamService>>(c =>
{
var context = c.Resolve<IComponentContext>();
return service =>
{
return context.ResolveNamed<IStreamService>(service);
};
});
And then in the controllers when you are resolving the IMainService you can inject a Func<string, IMainService> (the first argument in the constructor for MainService needs to be string). This way you can pass along the name of the service you would like to resolve. Like this:
public class MemoryStreamController : ApiController
{
private IMainService _service;
public MemoryStreamController(Func<string, IMainService> mainService)
{
_service = mainService("MemoryStreamService")
}
}
And then in the constructor for MainService you invoke the func factory with the type name that is injected. Like this:
public MainService(string type, Func<string, IStreamService> factory)
{
_service = factory(type);
}
回答2:
I've followed @TravisIllig advice (thanks!) and:
1) used named registrations for both "stream services",
2) used 2 named registrations for MainService, they differ in the type of stream service they can take as parameter
3) registered the controllers by specifying each named MainService as appropriate.
builder.RegisterType<MemoryStreamService>().Named<IStreamService>("memoryService");
builder.RegisterType<HttpStreamService>().Named<IStreamService>("httpService");
builder
.RegisterType<MainService>()
.As<IMainService>()
.WithParameter(ResolvedParameter.ForNamed<IStreamService>("memoryService"))
.Named<IMainService>("mainServiceForMemoryStreams");
builder
.RegisterType<MainService>()
.As<IMainService>()
.WithParameter(ResolvedParameter.ForNamed<IStreamService>("httpService"))
.Named<IMainService>("mainServiceForHttpStreams");
// This is for registering the other controllers of my API
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// Manually registers controllers that need an specific instance of IStreamService.
// These registrations needs to be placed after the "RegisterApiControllers()" call in order to Autofac to perform the desired injections.
builder.RegisterType<MemoryStreamController>()
.WithParameter(ResolvedParameter.ForNamed<IMainService>("mainServiceForMemoryStreams"));
builder.RegisterType<HttpStreamController>()
.WithParameter(ResolvedParameter.ForNamed<IMainService>("mainServiceForHttpStreams"));
来源:https://stackoverflow.com/questions/27573222/autofac-and-mvc-integration-register-type-per-api-controller