如何使用不同的构造函数参数重用相同的控制器 class
How can I reuse the same controller class using different constructor arguments
我有一个控制器接受一些依赖作为构造函数参数:
public class AccountsController : ApiController
{
public AccountsController(IAccountsService accountService)
{
this.accountService = accountService;
}
// actions
}
public interface IAccountsService
{
IEnumerable<AccountDto> GetAccounts(string userName);
}
为了解决这个依赖关系,我使用 Unity.WebApi 包:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// other configuration
config.DependencyResolver = new UnityDependencyResolver(myContainer);
}
}
我有两个不同的 IAccountsService
实现,我想使用同一个控制器 class 公开它们。从路由的角度来看,我想对操作和参数使用不同的控制器级路径和相同的底层路径结构。
我的方法是从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");
}));
更新:
在使用自托管的情况下,HttpContext.Current
将为空。
您可以做的是创建自定义 IHttpControllerActivator
。这将允许您自定义在当前 HttpRequestMessage
可用的上下文中创建控制器的方式。
这里有一个这样的例子 IHttpControllerActivator
:
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");
}
}
您可以获得有关此方法的更多详细信息here。
请注意,虽然我提供的示例没有使用 DI 容器(因此使用 Pure DI),但您应该能够使其与容器一起工作。
不要忘记删除设置 DependencyResolver
的代码,因为我们使用不同的接缝来扩展框架。
我有一个控制器接受一些依赖作为构造函数参数:
public class AccountsController : ApiController
{
public AccountsController(IAccountsService accountService)
{
this.accountService = accountService;
}
// actions
}
public interface IAccountsService
{
IEnumerable<AccountDto> GetAccounts(string userName);
}
为了解决这个依赖关系,我使用 Unity.WebApi 包:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// other configuration
config.DependencyResolver = new UnityDependencyResolver(myContainer);
}
}
我有两个不同的 IAccountsService
实现,我想使用同一个控制器 class 公开它们。从路由的角度来看,我想对操作和参数使用不同的控制器级路径和相同的底层路径结构。
我的方法是从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");
}));
更新:
在使用自托管的情况下,HttpContext.Current
将为空。
您可以做的是创建自定义 IHttpControllerActivator
。这将允许您自定义在当前 HttpRequestMessage
可用的上下文中创建控制器的方式。
这里有一个这样的例子 IHttpControllerActivator
:
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");
}
}
您可以获得有关此方法的更多详细信息here。
请注意,虽然我提供的示例没有使用 DI 容器(因此使用 Pure DI),但您应该能够使其与容器一起工作。
不要忘记删除设置 DependencyResolver
的代码,因为我们使用不同的接缝来扩展框架。