Get the container instance for Simple Injector

廉价感情. 提交于 2019-12-31 12:35:14

问题


I am using Simple Injector with a ASP.NET MVC project. I added the SimpleInjector.Integration.Web.Mvc nuget package. This adds SimpleInjectorInitializer class in App_Start folder and initializes the DI. The code looks something like

public static void Initialize()
{
    // Did you know the container can diagnose your configuration? 
    // Go to: https://simpleinjector.org/diagnostics
    var container = new Container();

    //Container configuration code
    DependencyResolver.SetResolver(
        new SimpleInjectorDependencyResolver(container));
}

This configures the DI for the MVC controller correctly.

My question is, if I want to get the instance of the container in any of the controller\class to resolve some dependency manually how can I do it.

I have earlier worked on AutoFac and it has a dependency interface IComponentContext which can be injected into any class that needs to do any resolution manually.

Update:

Here is a scenario. My controller uses a service who initialization depends upon the input parameter passed in the controller method and hence the dependency cannot be instantiated during construction time.

I understand that this is somewhat an anti pattern for DI, but it is requirement at few places and hence injecting the DI container is next best thing. Simple Injector samples should use of static variable to share the container which i want to avoid and also it is not possible by the way SimpleInjectorInitializer works.


回答1:


Except for any code that is part of the startup path of the application, no code should depend directly on the container (or a container abstraction, container facade, etc). This pattern is called Service Locator and Mark Seemann has a good explanation why this is a bad idea.

So components (such as Controllers) should not depend on the container directly, since this hides the used dependencies and makes classes harder to test. Furthermore your code starts to depend on an external framework (making it harder to change) or depending on an abstraction it doesn't need to know about.

My controller uses a service who initialization depends upon the input parameter passed in the controller method and hence the dependency cannot be instantiated during construction time

There's a general pattern for this problem: the abstract factory design pattern. The factory pattern allows you to delay the creation of types and allows you to pass in extra runtime parameters for the construction of a certain type. When you do this, your controller doesn't have to depend on Container and it prevents you from having to pass in a constructed container in your unit tests (DI frameworks should in general not be used in your unit test projects).

Do note however that letting your components require runtime data during creation is a code smell. Prevent doing that.

You might think that by doing this we are just moving the problem to the factory implementation. Although we are moving the dependency on the container into the factory implementation, we are in fact solving the problem because the factory implementation will be part of the application's Composition Root, which allows the application code itself oblivious to any DI framework.

So this is how I advice you to structure your code:

// Definition of the factory in the UI or BL layer
public interface ISomeServiceFactory
{
    ISomeService Create(int inputParameter);
}

// Controller depending on that factory:
public class MyController : Controller
{
    private readonly ISomeServiceFactory factory;

    public MyController(ISomeServiceFactory factory)
    {
        this.factory = factory;
    }

    public ActionResult Index(int value)
    {
        // here we use that factory
        var service = this.factory.Create(value);
    }
}

In your composition root (the start up path) we define the factory implementation and the registration for it:

private class SomeServiceFactory : ISomeServiceFactory
{
    private readonly Container container;

    // Here we depend on Container, which is fine, since
    // we're inside the composition root. The rest of the
    // application knows nothing about a DI framework.
    public SomeServiceFactory(Container container)
    {
        this.container = container;
    }

    public ISomeService Create(int inputParameter)
    {
        // Do what ever we need to do here. For instance:
        if (inputParameter == 0)
            return this.container.GetInstance<Service1>();
        else
            return this.container.GetInstance<Service2>();
    }
}

public static void Initialize()
{
    var container = new Container();

    container.RegisterSingle<ISomeServiceFactory, SomeServiceFactory>();
}

Upon creation, the Container registers itself (using the call RegisterSingle<Container>(this)) so you can always inject the container into any component. That's similar to injecting the IComponentContext when working with Autofac. But the same holds for Autofac, Simple Injector, and any other container: you don't want to inject your container into components that are located outside the composition root (and there hardly ever is a reason for it).



来源:https://stackoverflow.com/questions/17906423/get-the-container-instance-for-simple-injector

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!