Register a child container by route parameters

We have a multi-tenant application ASP.NET MVC, which hosts a reservation system for several customers. Each of these clients has several packages that can affect the configuration of the Unity Container. We create a child container for each request and register various interface implementations based on client and package parameters passing through the route.

We are currently doing this by following these steps:

  • The controller has a ServiceLocator property that uses the unit container to resolve dependencies.
  • The controller receives an IUnityContainer, which is entered and assigned to the property.
  • The controller has its own ActionFilterAttribute, which accesses the controller unity container, creates a child container, conditionally registers dependency implementations based on client and package route parameters, and then assigns this child container to the serviceLocator controller.
  • The controller uses serviceLocator on demand to resolve individual dependencies.

It works, but is really awkward, and I feel that in the end it will be unstable. I am looking for the best solution.

We are stuck on .NET 4.0 for the time being until we complete some outdated stuff, so I am specifically targeting Unity 2.

I tried to create a custom IDependencyResolver to create a child container and register dependencies based on route parameters that store the container in the session or in HttpContext elements, but faced with zero HttpContext problems. Is there any other way to base registration on a route and have dependencies entered into the controller constructor?

Ultimately, I will also need a solution for the web API.

Edit: Example

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 gets RemoteRateService
url: / ClientB / Package2 / Rate - RateController gets LocalRateService

+4
source share
1 answer

, IControllerFactory. Google, , , , 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, , .

factory, Bootstrapper.Initialise()

ControllerBuilder.Current
    .SetControllerFactory(new UnitySessionControllerFactory(container));
+2

Source: https://habr.com/ru/post/1527257/


All Articles