Autofac and MVC integration: Register Type per API Controller

血红的双手。 提交于 2019-12-24 10:59:09

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!