How to dynamically register a type (for example, another implementation) for a Unity container based on the URL of a web application?

I am currently stuck on what I consider to be a simple architecture issue.

I have a controller (these are just examples, since I cannot share my real code, but the principle matters):

public class StackOverflowController : Controller { private readonly IStackOverflowService stackOverflowService; public StackOverflowService (IStackOverflowService stackOverflowService) { this.stackOverflowService = stackOverflowService; } // GET: StackOverflow public ActionResult Index() { var foo = stackOverflowService.Get(bar); return View(foo); } } 

I am using Bootstrapper Unity MVC4 for injection management:

  public static class Bootstrapper { public static IUnityContainer Initialise() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); return container; } private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); container.RegisterType<IStackOverflowService, StackOverflowService>("DefaultStackOverflowService"); return container; } } 

So far so good, the injections work. Where I'm stuck, it’s now easy to use either a configuration or some kind of variable, I want to change the implementation of StackOverflowService .

So, for example, if url = "somethingelse.com", I want to be able to automatically enter a CustomStackOverflowService that inherits from IStackOverflowService. But here I am lost. I tried using Named Registration, but I cannot figure out how to manually allow the correct service to be implemented based on some alternative criteria.

If someone can help, it will be epic :)

EDIT: The criteria for changing the implementation should ideally be calculated in real time, so tell me based on the actual URL that is currently being visited. This is a multidirectional environment, so it can be something like domain.com/stackoverflow vs domain2.com/stackoverflow. The implementation should be different for each site. I don’t even know if you can be honest or how to do it. I am new to the entire subject of IoC.

EDIT 2: I got a little more with this and managed to manually invoke a specific implementation. Now I am considering using the Factory user controller to decide which implementation to use, but I'm not sure if this is the right way, or if there is an easier way to do this.

I need to keep in mind that as more customers enter the market, I need to be able to quickly add additional implementations for each new client. Therefore, ideally, I will have an easy way to control methods for a specific client.

EDIT 3: Further update, I rewrote my controller to allow the constructor to be injected to determine the dependency. However, my original problem is still relevant, since it works when the controller is built, I do not have access to the request and therefore cannot determine the URL to resolve the dependency. Any ideas would be appreciated.

+6
source share
1 answer

Using IControllerFactory and child containers should work, but will closely link your MVC-dependent converter to Unity.

First, change your bootloader to create a child container for each domain, and configure each child container with the special registrations that you need for this domain. In addition, we need to tell MVC that we have an IControllerFactory that we want to use

 public class Bootstrapper { public static IUnityContainer Initialize() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); return container; } private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); // Register unity controller factory to resolve controllers container.RegisterType<IControllerFactory, UnityControllerFactory>(); // default registrations container.RegisterType<IStackOverFlowService, StackOverflowService>(); // Do special registration per domain LocalHostRegistrations(container); SomeThingElseRegistrations(container); return container; } private static void LocalHostRegistrations(IUnityContainer container) { // you can create a child container per domain var localHostContainer = container.CreateChildContainer(); // all special registrations for localhost go in this container // any registrations that are the same as the default we can leave off and unity will // auto find them from the parent localHostContainer.RegisterType<IStackOverFlowService, LocalHostStackOverflowService>(); // register the child container in the parent container with a registration name container.RegisterInstance(typeof(IUnityContainer), "localhost", localHostContainer); } private static void SomeThingElseRegistrations(IUnityContainer container) { var localHostContainer = container.CreateChildContainer(); localHostContainer.RegisterType<IStackOverFlowService, CustomStackOverflowService>(); container.RegisterInstance(typeof(IUnityContainer), "somethingelse", localHostContainer); } } 

Now create a factory controller

 public class UnityControllerFactory : DefaultControllerFactory { public override IController CreateController(RequestContext requestContext, string controllerName) { IController controller = null; var type = GetControllerType(requestContext, controllerName); if (type != null) { // read host from incoming url var domain = requestContext.HttpContext.Request.Url.Host.ToLower(); // get parent unity container var container = DependencyResolver.Current.GetService<IUnityContainer>(); // check if there is a child container for this domain if (container.IsRegistered<IUnityContainer>(domain)) { container = container.Resolve<IUnityContainer>(domain); } controller = container.Resolve(type) as IController; } // if didn't find type or not right type just pass into base to handle errors if (controller == null) { return base.CreateController(requestContext, controllerName); } return controller; } } 

This factory will retrieve the parent unity container from the MVC dependency resolver and check if any child containers are registered. If so, it will use the child container to allow the controller and enter any registrations that are special to the childs container. This strongly connects the unity container to the MVC dependency resolver and bypasses the dependency resolver and uses unity directly, but should give you what you are looking for.

+6
source

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


All Articles