[ASP.NET Core 3 frame Disclosure] dependency injection: Dependency injection mode

IoC mainly such a design idea: by a group of the general flow of control is transferred from the application into the frame to achieve a multiplex process, and between the frame and the code in accordance with the application of "Hollywood rules" to achieve interaction. We can use a number of different design patterns in a manner IoC, such as our template previously described methods, method and abstract factory plant, then we introduce a more valuable IoC pattern: dependency injection (DI: Dependency Injection).

First, the object provided by the container

The method and plant described earlier and the same abstract factory model, dependency injection is an "object provides" design pattern where the object we are collectively referred to as "service", "clients" or "service instance." In applications using a dependency injection, we define a type, you can just rely on it directly to the service using a corresponding manner inject come in on it.

When the application starts, we will be required to service a global register. In general, the service mostly to register for the interface or inherit the abstract class, help the service we provide registration information corresponding to the service instance in the subsequent process of consumption. According to "Hollywood rules", the application only need to define and register the required good service, provide the service instance is accomplished entirely to the framework, the framework will use a separate "container (Container)" to provide every required service instance.

Our framework will be used to provide this service containers called "dependency injection container," there are a lot of people referred to as "IoC container," according to previously described for IoC, I do not think the latter is a reasonable title. Dependency Injection container has been able to follow the way we want them to provide the services required because the container is to create a service based on the registration information, service registration all the information needed to provide service instances included.

As a simple example, we create a dependency injection container Cat named type, then we can call this extension method as follows GetService <T> Gets the specified type of service object from a Cat object. The reason I named it Cat, from all of us are very familiar with a cartoon "Doraemon (A Dream)." Viking's four dimensional pocket that is ideal for a dependency injection container, Bear just need to tell the duo demand corresponding A dream, it is from this pocket to give the corresponding magic. Dependency injection is also true container, the consumer only needs to tell the service type of container required service (typically a service interface or an abstract service class) can be obtained matching the service instance.

public static class CatExtensions
{  
    public static T GetService<T>(this Cat cat);
}

MVC framework for our presentation, we were using different design patterns in front of the frame of core types were MvcEngine "transformation" and now we use injection dependent manner, and using the container Cat follows this manner re-implement it, we will find MvcEngine become very simple and clear.

public class MvcEngine
{
    public Cat Cat { get; }
    public MvcEngine(Cat cat) => Cat = cat;
        
    public async Task StartAsync(Uri address)
    {
        var listener = Cat.GetService<IWebListener>();
        var activator = Cat.GetService<IControllerActivator>();
        var executor = Cat.GetService<IControllerExecutor>();
        var renderer = Cat.GetService<IViewRenderer>();

        await listener.ListenAsync(address);
        while (true)
        {
            var httpContext = await listener.ReceiveAsync();
            var controller = await activator.CreateControllerAsync(httpContext);
            try
            {
                var view = await executor.ExecuteAsync(controller, httpContext);
                await renderer.RenderAsync(view, httpContext);
            }
            finally
            {
                await activator.ReleaseAsync(controller);
            }
        }
    }        
}

Dependency injection embodies one of the most direct service consumption patterns, consumers only need to tell the provider (dependency injection container) the type of services required, which can provide a service instance matches the pre-registered according to the rules. Since the final decision on the registration service dependency injection container will provide a kind of service instances according to the specified type of service, so we can be done by modifying the way the service was registered customize the frame. If an application needs to use a single previously defined SingletonControllerActivator embodiment mode activated target Controller, it will be in the following form MvcEngine SingletonControllerActivator register before starting the injection will be dependent to a container.

public class App
{
    static void Main(string[] args)
    {
        var cat = new Cat() .Register<ControllerActivator, SingletonControllerActivator>();
        var engine     = new MvcEngine(cat);
        var address     = new Uri("http://localhost/mvcapp");
        engine.StartAsync(address);
    }
}

Two, three, dependency injection

A task often requires multiple objects collaborate to complete, or that an object needs to rely on other objects directly or indirectly, in time to complete a task to complete some necessary steps, so the runtime dependencies between objects relationship is determined by the target task is "constant", naturally, no "decoupling" argument. A runtime object is defined by the corresponding class, the coupling between classes by abstracting dependent manner to reduce or terminate.

From the service consumer point of view, we consumption by means of an interface to abstract service, the service consumer programs for specific types of services depend on the above can be transferred to rely on the service interface, but always available to consumers at runtime It is a target for a specific type of service. Moreover, to complete custom actions in the service interface, this object may need to participate in other related objects, in other words, the dependent services provided by the object may have dependencies on other service objects. As a target dependent services provider into the container, it will provide all dependent services according to this example of the dependency chain.

As shown below, the application framework calls GetService <IFoo> Method injected into the vessel to obtain a dependent IFoo service object implements the interface, which will create a type of object based Foo mapping relationship registered in advance. Since the Foo object needs to participate Bar and Gux object to complete the operation target, it has a direct dependency for Foo Bar and Gux of. As clients Bar, it relies Baz, then Baz became indirectly dependent Foo's. Dependence on the final injection Foo object container provided, it directly or indirectly dependent objects Bar, Baz and Qux are previously initialized and automatically injected into the subject.

3-5

From the perspective of object-oriented programming is concerned, in the field or attribute type is mainly dependent on a form. If the Type A has a field or attribute of type B, then A is generated dependent on B, we can be simply understood as dependency injection initializing an automated way for dependent fields or attributes. We can achieve this in three main ways, this is the highlight of the following three kinds of dependency injection.

Constructor injection

Constructor is injected in the constructor parameter dependent objects by means of injection into the object is created by it. The following code fragment, for Foo Bar dependence on the read-only property is reflected in the Bar, implemented in the constructor for the initialization of the attribute, the specific attribute values ​​passed parameters provided by the constructor.

public class Foo
{
    public IBar Bar{get;}
    public Foo(IBar bar) =>Bar = bar;
}

In addition, the configuration of the injection device further reflected in the choice of the constructor. The following code fragment, Foo class defines two constructor dependency injection container first need to select a suitable configuration function before creating Foo object. As for how to select the target constructor, a different dependency injection containers may have different policies, such parameters may be selected or at least up to the constructor, or may be marked on a target characteristic InjectionAttribute constructor in the manner as shown below.

public class Foo
{
    public IBar Bar{get;}
    public IBaz Baz {get;}

    [Injection]
    public Foo(IBar bar) =>Bar = bar;
    public Foo(IBar bar, IBaz):this(bar)=>Baz = baz;
}

Properties injection

If you rely directly reflects the class for an attribute, and the read-only attribute is not, we can make dependency injection container automatically be assigned after the object is created so as to achieve the purpose of dependency injection. In general, when we define this type, it is necessary to explicitly identified as such attributes to be automatically injected dependency attribute to distinguish it from other common properties. The following code fragment, Foo class defines two public properties writable Bar and Baz, we have by way of tagging InjectionAttribute characteristic attribute set to automatically injected Baz dependency property. For Foo object container provided by the dependency injection, Baz its properties will be automatically initialized.

public class Foo
{
    public IBar Bar{get; set;}

    [Injection]
    public IBaz Baz {get; set;}
}

Method Injection

Reflected field or attribute dependencies can be in the form of a method of initialization. The following code fragment, for Foo Bar dependent reflected in the read-only attribute, implemented in the Initialize method for initializing the attribute, the specific attribute values ​​provided by the parameters passed to the method. We also marked by characteristic manner (InjectionAttribute) of the method is identified as the injection method. Dependency Injection container After creating a Foo object constructor is called, it will automatically call the Initialize method to assign read-only property Bar.

public class Foo
{
    public IBar Bar{get;}

    [Injection]
    public Initialize(IBar bar)=> Bar = bar;
}

In addition to achieving the above injected into the vessel through which rely on automatic initialization call service process, we can also use another method to achieve it freer injection, this injection method has wide application in ASP.NET Core applications . ASP.NET Core at startup will call the registered Startup object to complete the registration middleware, we define the Startup type when it does not need to implement an interface, the method for registering Configure middleware is not a fixed statement, we can follow the following manner dependent on any instance of the service is directly injected into this method.

public class Startup
{
    public void Configure(IApplicationBuilder app, IFoo foo, IBar bar, IBaz baz);
}

Similar injection method applied to the same type of definition of middleware. As with the type of Startup to register middleware, the middleware type in ASP.NET Core framework also need to implement a predefined interface, or InvokeAsync Invoke method for processing a request may also be injected in any manner as follows dependent services.

public class FoobarMiddleware
{
    private readonly RequestDelegate _next; 
    public FoobarMiddleware(RequestDelegate next)=> _next = next;

    public Task InvokeAsync(HttpContext httpContext, IFoo foo, IBar bar, IBaz baz);
}

In this way the above method led to the injection of a "convention oriented" programming. By eliminating the need to achieve a predefined interface or inherit certain predefined base class statement override or need to implement the method will be less correspond to the restriction, so that the most direct possible way dependent services injection to the method. For these types of injection method described earlier, constructor injection is the most ideal form , I personally do not recommend the use of the properties and methods of injection injection (injection except that the previously described method agreed on).

Three, Service Locator pattern

Suppose we need to define a service type Foo, it relies on two additional services Bar and Baz, the latter corresponding to the services and interfaces are IBar IBaz. If the current application injection container having a dependency (assuming similar Cat we defined in the above), then we can use the following two ways to define the service type Foo.

public class Foo : IFoo
{
    public IBar Bar { get; }
    public IBaz Baz { get; }
    public Foo(IBar bar, IBaz baz)
    {
        Bar = bar;
        Baz = baz;
    }  
    public async Task InvokeAsync()
    {
        await Bar.InvokeAsync();
        await Baz.InvokeAsync();
    }
}

public class Foo : IFoo
{
    public Cat Cat { get; }
    public Foo(Cat cat) => Cat = cat; 
    public async Task InvokeAsync()
    {
        await Cat.GetService<IBar>().InvokeAsync();
        await Cat.GetService<IBaz>().InvokeAsync();
    }
}

On the surface, these two types of services defined manner provided above looks good, at least they are all dependent services solutions for coupling problem, and will depend on the transition for service implementation to rely for the interface. So which is better? I think some people will choose the second definition of the way, because this is not only defined way less code, for providing services and more direct. We directly in the constructor "injected" with representatives of the "dependency injection container," the Cat object, anywhere to depend on the service, we only need to use the service instance corresponding it to provide it.

But in fact the second definition methods used design pattern is simply not a "dependency injection" , but is called a " Service Locator " design pattern. Service Locator pattern also has a registry created by the global container service to provide the required service instances, the container is called "Service Locator". "Dependency injection container" and "Service Locator" is actually the same thing different names in different design patterns of fills, then the difference between Dependency Injection and Service Locator is reflected in what place?

I think the difference between the two can be distinguished design patterns "into the container-dependent" angle or "Service Locator" from whom use. In the application of a use of dependency injection, we only need a standard injection form of good service type definitions, and complete the service registration before the application starts it, the framework of its engine during operation will use dependency injection container to provide the service instance currently required. In other words, dependency should be injected into the container frame instead of the user application . Service Locator pattern clearly not the case, it is clear that the application to use it to provide the required service instances, so its users is the application .

We can also tell the difference between them from a different angle. Since the dependent services is "injected" in a manner to provide, so the use of application dependency injection patterns can be seen as a service "pushed " to the dependency injection container, in the application mode is to use the Service Locator Service Locator to " pull " take the required services, the push-pull can accurately reflect the differences between the two. Well, since there is a difference between the two, it really is better?

Back in 2010, Mark Seemann in his blog Service Locator will be regarded as an "anti-mode (Anti-Pattern)", although some people have different opinions, but I personally do not recommend using this very design patterns. I am against using Service Locator and against the use of the aforementioned properties and methods inject injection have similar reasons.

The spirit of "loosely coupled, highly cohesive" design principle, since we will define a set of actions related in a multiplexed with the service, it should try to claim the service itself not only has the characteristics of independence and autonomy, but also the requirements of service inter should have clear boundaries, dependencies between services should be clear and not vague. Whether using property injection or implantation method, or using Service Locator to provide a current dependent services, which undoubtedly is the current service has added a new dependency , that is injected into the container or Service Locator dependence for reliance.

The current service has for dependence and other services for dependency injection container or Service Locator relies essentially different, the former is a type of dependence on, whether it is service-based interface or implementation type, which is based on " contract" dependence. This dependence is not only clear , but also guaranteed of. But dependency injection container or Service Locator is essentially a black box , it can provide the required services are provided corresponding service has been registered beforehand added into the container, but this dependence is not only vague but also unreliable in.

Dependency injection framework ASP.NET Core framework uses only supports constructor injection, and does not support the properties and methods injection (similar to Startup and middleware injection except in the agreed-based), but we probably will follow unwittingly Service Locator pattern to write our code. In a sense, when we use in the program IServiceProvider (represented dependency injection container) to extract a service instance, it means that we are already using Service Locator pattern, so when we encounter this situation when should ponder whether some need to do so.

[ASP.NET Core 3 frame Disclosure] dependency injection: Inversion of Control
[ASP.NET Core 3 frame Disclosure] dependency injection: the IoC mode
[ASP.NET Core 3 frame Disclosure] dependency injection: Dependency injection mode
[ASP.NET Core 3 framework Secret] dependency injection: a mini-version of the DI framework

Guess you like

Origin www.cnblogs.com/artech/p/inside-asp-net-core-03-03.html