C# 如何使用不同的构造函数参数重用同一控制器类

C# 如何使用不同的构造函数参数重用同一控制器类,c#,asp.net,asp.net-web-api,dependency-injection,unity-container,C#,Asp.net,Asp.net Web Api,Dependency Injection,Unity Container,我有一个控制器,它接受一些依赖项作为构造函数参数: public class AccountsController : ApiController { public AccountsController(IAccountsService accountService) { this.accountService = accountService; } // actions } public interface IAccountsService {

我有一个控制器,它接受一些依赖项作为构造函数参数:

public class AccountsController : ApiController
{
    public AccountsController(IAccountsService accountService)
    {
        this.accountService = accountService;
    }
    // actions
}

public interface IAccountsService
{
    IEnumerable<AccountDto> GetAccounts(string userName);
}
我有两个不同的
IAccountsService
实现,我想使用相同的控制器类公开这两个实现。从路由的角度来看,我希望对操作和参数使用不同的控制器级路径和相同的底层路径结构

我的方法是从
AccountsController
继承两个控制器,并将它们注册到
UnityContainer
中,以使用不同的
IAccountsService
实现

public class Accounts1Controller : AccountsController
{
    public Accounts1Controller([Dependency("Service1")]IAccountsService accountService) :
        base(accountService) { }
}

public class Accounts2Controller : AccountsController
{
    public Accounts2Controller([Dependency("Service2")]IAccountsService accountService) :
        base(accountService) { }
}
有没有更直接的方法


我更愿意让控制器容器不被察觉,并避免冗余继承(不管对DI框架来说,Unity不是一个单一选项)。

这里有一种方法:

假设这两条路线如下:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi2",
    routeTemplate: "api2/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
您可以让Unity根据请求的Url解析正确的服务,如下所示:

//Map IAccountsService to AccountsService1 with name Service1
container.RegisterType<IAccountsService, AccountsService1>("Service1");

//Map IAccountsService to AccountsService2 with name Service2
container.RegisterType<IAccountsService, AccountsService2>("Service2");

//At composition time, map IAccountsService to appropriate
//service based on Url
container.RegisterType<IAccountsService>(new InjectionFactory(c =>
{
    var pathAndQuery = HttpContext.Current.Request.Url.PathAndQuery;

    if(pathAndQuery.StartsWith("/api2"))
        return c.Resolve<IAccountsService>("Service2");
    else if(pathAndQuery.StartsWith("/api"))
        return c.Resolve<IAccountsService>("Service1");

    throw new Exception("Unexpected Url");
}));
您可以获得有关此方法的更多详细信息

请注意,尽管我提供的示例没有使用DI容器(因此使用),但您应该能够使其与容器一起工作


不要忘记删除设置依赖解析程序的代码,因为我们正在使用不同的seam来扩展框架。

这还不是很清楚。控制器接受一个对象作为参数,并有一个字段来保存对它的引用。那么,您如何设想它能够同时使用这两种实现呢?@Andrei,控制器公开了一组操作,这些操作将
IAccountsService
用作数据源。我将为
IAccountsService
的两个实现制作相同的web API。这两种实现的操作集是相同的。我建议使用
代理
设计模式。
Proxy
类将同时具有
IAccountsService
的两个实现,并决定使用哪个实现。对于DI框架,您将给出代理类的实现。@Andrei,添加了一种可能的方法,请参阅我的编辑。@stop cran,Unity有命名依赖项的概念。这和L J的建议应该是你的一个好选择,这是我所期待的!不幸的是,我已经尝试过了,并且
HttpContext.Current
每次都是空的。也许,它是在解决依赖关系之后分配的。实际上,我在发布答案之前测试了这段代码。您使用的是什么版本的Web API?您的配置有什么特别之处吗?经过调查,发现原因是使用自主机-请看这个问题-它可以工作,谢谢!我看到问题已经减少到每个请求的依赖解决方案。作为参考,还有另一种方法-
//Map IAccountsService to AccountsService1 with name Service1
container.RegisterType<IAccountsService, AccountsService1>("Service1");

//Map IAccountsService to AccountsService2 with name Service2
container.RegisterType<IAccountsService, AccountsService2>("Service2");

//At composition time, map IAccountsService to appropriate
//service based on Url
container.RegisterType<IAccountsService>(new InjectionFactory(c =>
{
    var pathAndQuery = HttpContext.Current.Request.Url.PathAndQuery;

    if(pathAndQuery.StartsWith("/api2"))
        return c.Resolve<IAccountsService>("Service2");
    else if(pathAndQuery.StartsWith("/api"))
        return c.Resolve<IAccountsService>("Service1");

    throw new Exception("Unexpected Url");
}));
public class MyControllerActivator : IHttpControllerActivator
{
    public IHttpController Create(
        HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        if (controllerType == typeof (ValuesController))
        {
            var pathAndQuery = request.RequestUri.PathAndQuery;

            IAccountsService svc;

            if (pathAndQuery.StartsWith("/api2"))
                svc = new Service2();
            else if (pathAndQuery.StartsWith("/api"))
                svc = new Service1();
            else 
                throw new Exception("Unexpected Url");

            return new ValuesController(svc);
        }

        throw new Exception("Unexpected Controller Type");
    }
}