簡體   English   中英

基於路由參數的子容器注冊

[英]Child container registration based on route parameters

我們有一個多宿主的ASP.NET MVC應用程序,它為多個客戶端托管一個預訂引擎。 這些客戶端中的每一個都有多個可以影響Unity容器配置的軟件包。 我們正在為每個請求創建一個子容器,並根據通過該路由傳遞的客戶端和包參數注冊不同的接口實現。

目前,我們通過執行以下操作來實現此目的:

  1. Controller有一個ServiceLocator屬性,它使用一個統一容器來解析依賴關系。
  2. Controller將IUnityContainer注入並分配給屬性。
  3. Controller有一個自定義的ActionFilterAttribute,它訪問控制器統一容器,創建一個子容器,根據客戶端和包路由參數有條件地注冊依賴實現,然后將這個子容器分配給控制器的serviceLocator。
  4. 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));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM