c# – 基于路由参数的子容器注册

我们有一个多宿主的ASP.NET MVC应用程序,它为多个客户端托管一个预订引擎.这些客户端中的每一个都有多个可以影响Unity容器配置的软件包.我们正在为每个请求创建一个子容器,并根据通过该路由传递的客户端和包参数注册不同的接口实现.

目前,我们通过执行以下操作来实现此目的:

> Controller有一个属性ServiceLocator,它使用一个统一容器来解析依赖关系.
> Controller将IUnityContainer注入并分配给属性.
> Controller有一个自定义ActionFilterAttribute,它访问控制器统一容器,创建子容器,根据客户端和包路由参数有条件地注册依赖项实现,然后将此子容器分配给控制器的serviceLocator.
> Controller根据需要使用serviceLocator来解析各个依赖项.

这有效,但真的很笨拙,我觉得最终会不可持续.我正在寻找更好的解决方案.

我们现在仍然坚持使用.NET 4.0,直到我们结束一些遗留的东西,所以我专门针对Unity 2.

我已经尝试创建一个自定义IDependencyResolver来创建子容器并根据路径参数注册依赖项,这些参数在Session或HttpContext项中存储容器,但遇到了null HttpContext问题.是否有任何其他方法可以在路由上进行注册并将依赖项注入控制器构造函数?

最终我还需要一个Web API解决方案.

编辑:示例

public interface IRateService { ... }
public class RemoteRateService : IRateService { ... }
public class LocalRateService : IRateService { ... }

public class CustomDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        if(ChildContainer == null)
        {
            ChildContainer = _container.CreateChildContainer();
            var routeData = HttpContext.Current.Request.RequestContext.RouteData.Values;
            if(routeData["client"] == "ClientA")
                ChildContainer.RegisterType<IRateService, RemoteRateService>();
            else
                ChildContainer.RegisterType<IRateService, LocalRateService>();
        }
        return ChildContainer.Resolve(serviceType);
    }
}

public class RateController : Controller
{
    private IRateService _rateService;
    public RateController(IRateService rateService)
    {
        _rateService = rateService;
    }
    ...
}

url:/ ClientA / Package1 / Rate – RateController获取RemoteRateService
url:/ ClientB / Package2 / Rate – RateController获取LocalRateService

最佳答案 Abatishchev在评论中回答了我的问题,指出了我与IControllerFactory正确的方向.对于此处结尾的随机谷歌搜索,这是我从DefaultControllerFactory继承的基本设置:

public class UnitySessionControllerFactory : DefaultControllerFactory
{
    private const string HttpContextKey = "Container";
    private readonly IUnityContainer _container;

    public UnitySessionControllerFactory (IUnityContainer container)
    {
        _container = container;
    }

    protected IUnityContainer GetChildContainer(RequestContext requestContext)
    {
        var routeData = requestContext.RouteData.Values
        ?? new RouteValueDictionary();
        var clientName = routeData["clientName"] as string;
        var packageId = routeData["packageID"] as int?;

        if (clientName == null)
            throw new ArgumentException("ClientName not included in route parameters");

        var childContainer = requestContext.HttpContext.Session[clientName + HttpContextKey] as IUnityContainer;

        if (childContainer != null)
            return childContainer;

        requestContext.HttpContext.Session[clientName + HttpContextKey] = childContainer = _container.CreateChildContainer();

        var moduleLoader = childContainer.Resolve<ModuleLoader>();

        moduleLoader.LoadModules(clientName, packageId);

        return childContainer;
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controllerType = GetControllerType(requestContext, controllerName);
        var container = GetChildContainer(requestContext);
        return container.Resolve(controllerType) as IController;
    }

    public override void ReleaseController(IController controller) 
    {
        _container.Teardown(controller);
    }
}

原谅这里使用会话.将来,我将在HttpContext.Items中交换它,一旦我能够在我们的项目中使用会话.

为了启用自定义控制器工厂,我将此行添加到Bootstrapper.Initialise()方法中

ControllerBuilder.Current
    .SetControllerFactory(new UnitySessionControllerFactory(container));
点赞