ABP Official documents (vii) Dependency injection

Source: Internet
Author: User
Tags constructor static class
2.1 ABP Public structure-Dependency injection

If you already know the concept of dependency injection, constructors, and attribute injection patterns, you can skip this section.

Wikipedia: "Dependency injection is a software design pattern that means that one or more dependencies (or services) are injected, or passed by reference, passed in to a dependent object (or client) and become part of the customer's state. Patterns separate the creation of customer dependencies by their behavior, which allows the program design to be loosely coupled, while adhering to dependency inversion and a single responsibility principle. Directly compared to the service locator mode, it allows customers to understand the mechanisms they use to find dependencies. ”

Dependency management, modular development, and application modularity are difficult to use without dependency injection technology. 2.1.1 The problem of traditional ways

In an application, classes depend on each other. Suppose we have an application service that uses the warehousing (repository) class to insert entities into the database. In this case, the application service class relies on the warehousing (repository) class. Take a look at the following example:

public class Personappservice
{
    private ipersonrepository _personrepository;

    Public Personappservice ()
    {
        _personrepository = new Personrepository ();            
    }

    public void Createperson (string name, int.)
    {
        var person = new Person {name = name, age = age};
        _personrepository.insert (person);
    }
}

Personappservice inserts a person into the database using Personrepository. The problem with this piece of code:

Personappservice calls the Createperson method through IPersonRepository, so this method relies on ipersonrepository instead of the personrepository concrete class. But Personappservice (the constructor) still relies on personrepository. Components should depend on interfaces rather than implementations. This is the so-called dependency inversion principle.

If Personappservice creates the personpeository himself, it becomes dependent on a specific ipersonrepository interface implementation and cannot work with another implementation. Therefore, the separation of interfaces from implementation becomes meaningless, and hard dependencies make the code based on tight coupling and low reuse. Hard-Dependency (hard-dependency) makes the code tightly coupled and less reusable.

We may need to change the way we create personrepository in the future. That is, we might want to create it as a singleton (a single shared instance instead of creating an object for each use). Or we might want to create multiple classes to implement IPersonRepository and create objects based on conditions. In this case, we need to modify all classes that depend on IPersonRepository.

With this dependency, it is difficult (or impossible) to unit test the Personappservice.

To overcome these problems, you can use Factory mode. Therefore, the created warehousing class is abstract. Look at the following code:

public class Personappservice
{
    private ipersonrepository _personrepository;

    Public Personappservice ()
    {
        _personrepository = Personrepositoryfactory.create ();            
    }

    public void Createperson (string name, int.)
    {
        var person = new Person {name = name, age = age};
        _personrepository.insert (person);
    }
}

Personrepositoryfactory is a static class that creates and returns a ipersonrepository. This is called the Service locator mode. The above dependencies are resolved because Personappservice does not need to create an IPersonRepository implementation object, which depends on the Personrepositoryfactory's create method. However, there are still some problems:

At this point, Personappservice depends on personrepositoryfactory. This is easier to accept, but there is still a hard dependency (hard-dependency).

Write a factory class/method tedious for each library or for each dependency.

Testability is still not good, because it is difficult to make personappservice use mocks to implement IPersonRepository. 2.1.2 Solutions

There are some best practices (patterns) for class dependencies. 1. Constructor Injection (Constructor injection)

Rewrite The example above, as shown below:

public class Personappservice
{
    private ipersonrepository _personrepository;

    Public Personappservice (ipersonrepository personrepository)
    {
        _personrepository = personrepository;
    }

    public void Createperson (string name, int.)
    {
        var person = new Person {name = name, age = age};
        _personrepository.insert (person);
    }
}

This is called constructor injection. Now, Personappservice does not know which classes implement ipersonrepository and how to create it. Who needs to use Personappservice, first create a ipersonrepository personappservice and pass it to the constructor, as follows:

var repository = new Personrepository ();
var personservice = new Personappservice (repository);
Personservice.createperson ("Yunus Emre", 19);

Constructor injection is a perfect way to make a class independent of the dependent object creation. However, the above code has some problems:

Creating a personappservice becomes difficult. Think about it if it has 4 dependencies, we have to create these four dependent objects and pass them to the constructor Personappservice.

Dependent classes may have other dependencies (here, personrepository may have dependencies). So, we have to create all the dependencies of Personappservice, dependencies on all dependencies, and so on. So, dependencies make it too complicated for us to create an object.

Fortunately, the dependency Injection framework automates the management of dependencies. 2. Attribute injection (property injection)

The injection pattern using constructors is a perfect way to provide a class's dependency. In this way, only instances that rely on you to create a class are provided. It is also a powerful way to explicitly declare what kind of dependencies a class needs to work correctly.

However, in some cases, the class relies on another class, but it can also be without it. This is usually appropriate for crosscutting concerns, such as logging. A class can have no logbook, but it can write logs if you provide a log object. In this case, you can define dependencies as public properties, rather than having them put in constructors. Think about it, if we want to write a log in Personappservice. We can rewrite the class as follows:

public class Personappservice
{public
    ILogger Logger {get; set;}

    Private IPersonRepository _personrepository;

    Public Personappservice (ipersonrepository personrepository)
    {
        _personrepository = personrepository;
        Logger = nulllogger.instance;
    }

    public void Createperson (string name, int.)
    {
        logger.debug ("Inserting a new person to database with name =" + name);
        var person = new Person {name = name, age = age};
        _personrepository.insert (person);
        Logger.debug ("Successfully inserted!");
    }
}

Nulllogger.instance is a singleton object that implements the ILogger interface, but actually does nothing (does not write the log.) It implements the ILogger instance, and the method body is empty). Now, Personappservice can write the log, if you set the logger for the Personappservice instance, as follows:

Var personservice = new Personappservice (new Personrepository ());
    Personservice.logger = new Log4netlogger ();
    Personservice.createperson ("Yunus Emre", 19);

Suppose Log4netlogger implements ILogger instances so that we can write logs using the Log4net library. Therefore, Personappservice can write logs. If we do not set logger,personappservice, we will not write the log. Therefore, we can say that the Personappservice ILogger instance is an optional dependency.

Almost all of the dependency injection frameworks support attribute injection patterns. 3. Dependency Injection Framework

There are many dependency injection frameworks that can automatically resolve dependencies. They can create all dependencies (recursive dependencies and dependencies). So you just need to rely on the injection pattern to write classes and class constructors & properties, others to the DI framework to handle. In good applications, classes are even independent of the DI framework. The entire application will have only a few lines of code or classes that show interaction with the DI framework.

The ABP dependency injection is based on the Castle Windsor framework. Castle Windsor One of the most mature di frameworks. There are many such frameworks, such as UNITY,NINJECT,STRUCTUREMAP,AUTOFAC and so on.

When using a dependency injection framework, first register your interface/class to the dependency injection framework, and then you can resolve an object. In Castle Windsor, it is like this:

var container = new WindsorContainer ();

    Container. Register (
            component.for<ipersonrepository> (). Implementedby<personrepository> (). Lifestyletransient (),
            component.for<ipersonappservice> (). Implementedby<personappservice> (). Lifestyletransient ()
        );

    var personservice = container. Resolve<ipersonappservice> ();
    Personservice.createperson ("Yunus Emre", 19);

We first created the WindsorContainer. Then register the Personrepository and Personappservice and their interfaces. We then asked the container to create a Ipersonappservice instance. It creates the Personappservice object and its dependencies and returns. In this simple example, using the DI framework may not be so concise, but imagine that you will have many classes and dependencies in the actual enterprise application. Of course, registered dependencies are created only one time from the start of the program.

Notice that we are simply declaring the object as a temporary object (transient). This means that whenever we create an object of these types, a new instance is created. There are many different life cycles (e.g., singletion Singleton mode). 4. ABP relies on injected infrastructure

By following best practices and conventions when writing an application, the ABP makes it virtually invisible to use the dependency injection framework. registration (registering)

In ABP, there are many different ways to register your class to a dependency injection system. Most of the time, the conventional approach is sufficient. General Registration (conventional registrations)

By convention, ABP automatically registers all repositories, Domain services, application services, MVC controllers, and Web API controllers. For example, you might have a Ipersonappservice interface and implementation class Personappservice:

Public interface Ipersonappservice:iapplicationservice
{
    //...
}

public class Personappservice:ipersonappservice
{
    //...
}

The ABP automatically registers it because it implements the Iapplicationservice interface (it is just an empty interface). It will be registered as transient (each use creates an instance). When you inject (using constructor injection) The Ipersonappservice interface into a class, the Personappservice object is automatically created and passed to the constructor.

Note: Naming conventions are very important here. For example, you can change the name Personappservice to Mypersonappservice or another name that contains the "personappservice" suffix, because ipersonappservice contains the suffix. But you can not follow Peopleservice to name your service class. If you do this, it will not automatically register for Ipersonappservice (it requires self-registration (self-registration) to the DI framework instead of the interface), so if you want you should manually register it.

The ABP registers the Assembly as agreed. So, you should tell the ABP to register your assembly as agreed. This is easy:

Iocmanager.registerassemblybyconvention (assembly.getexecutingassembly ());

Assembly.getexecutingassembly () Gets a reference to the assembly that includes this code. You can register other assemblies by using the Registerassemblybyconvention method. This is done with the initialization of your module (Abpmodule.initialize ()). Please see the ABP module system for more information.

You can write your own contract registration class by implementing the Iconventionalregisterer interface and calling the iocmanager.addconventionalregisterer method. You should add it to the module's Pre-initialize method. Help Interface (helper interfaces)

You can register a specific class and not follow the traditional convention rules. The ABP provides a quick way to implement the itransientdependency and isingletondependency interfaces. For example:

Public interface Ipersonmanager
{
    //...
}

public class Mypersonmanager:ipersonmanager, isingletondependency
{
    //...
}

In this way, you can easily register Mypersonmanager for transient. Mypersonmanager is used when a ipersonmanager is required. Note that the dependency is declared as a singleton. Therefore, the Mypersonmanager created by the same object is passed to all the required classes. Created only on first use, the entire life cycle of the application is using the same instance. Custom/Direct registration (Custom/direct registration)

If the previously described method is still not sufficient to handle your situation, you can use Iocmanager or castle Windsor to register your own class. using Iocmanager

You can use Iocmanager to register dependencies (this is usually implemented in the Preinitialize method of the module):

Iocmanager.register<imyservice, myservice> (dependencylifestyle.transient);
using the castle Windsor API

You can also use the Iiocmanager.ioccontainer property to access the Windsor container and register dependencies. As shown below:

IocManager.IocContainer.Register (classes.fromthisassembly (). Basedon<imyspecialinterface> (). Lifestyleperthread (). Withserviceself ());

For more information, please read the Windsor documentation.

There is no removal of the translation before implementing the Iwindsorinstaller interface, because I feel this is very useful.

You can also implement the Iwindsorinstaller interface for registration. You can create a class in your application that implements the Iwindsorinstaller interface:

public class Myinstaller:iwindsorinstaller
{public
    void Install (IWindsorContainer container, Iconfigurationstore store)
    {
        container. Register (classes.fromthisassembly (). Basedon<imyspecialinterface> (). Lifestyleperthread (). Withserviceself ());
    }
}

The ABP automatically discovers and executes this class. Finally, you can get windsorcontainer by using the Iiocmanager.ioccontainer property. parsing (resolving)

Register to notify the IOC (Control reversal) container about your classes, their dependencies, and their lifecycles. When your application needs to create an object using an IOC container, ASP. NET provides a number of ways to resolve dependencies. Constructors & Attribute Injection (Constructor & property injection)

As a best practice, you should use constructors and attribute injection to get the dependency of the class. Example:

public class Personappservice
{public
    ILogger Logger {get; set;}

    Private IPersonRepository _personrepository;

    Public Personappservice (ipersonrepository personrepository)
    {
        _personrepository = personrepository;
        Logger = nulllogger.instance;
    }

    public void Createperson (string name, int.)
    {
        logger.debug ("Inserting a new person to database with name =" + name);
        var person = new Person {name = name, age = age};
        _personrepository.insert (person);
        Logger.debug ("Successfully inserted!");
    }
}

IPersonRepository is injected from the constructor, and the ILogger instance is injected from the public attribute. In this way, your code will not reflect the dependency injection system. This is the most appropriate way to use the DI system. Iiocresolver,iiocmanager and Iscopediocresolver interface

Sometimes it may be necessary to create the required dependencies directly, rather than constructors and property injection. (This situation should be avoided as much as possible). ABP provides a number of services that make such injections easy to implement. Example:

public class Mysampleclass:itransientdependency {private ReadOnly iiocresolver _iocresolve

    R
    Public Mysampleclass (Iiocresolver iocresolver) {_iocresolver = Iocresolver; } public void DoIt () {//parsing, using and manually releasing var PersonService1 = _iocresolver.resolve<personappservic
        E> ();
        Personservice1.createperson (new Createpersoninput {Name = "Yunus", Surname = "Emre"});

        _iocresolver.release (PersonService1);
        Parse and use using syntax sugar to release resource using (var PersonService2 = _iocresolver.resolveasdisposable<personappservice> ())
        {PersonService2.Object.CreatePerson (new Createpersoninput {Name = "Yunus", Surname = "Emre"}); }
    }
}

Mysampleclass is an example class for an application. Iicresolver is injected through a constructor, which is then used to create and release objects. There are several workarounds for overloads that can be used as needed. The release method is used to release the component (object). If you are creating an object manually, it is important to call the release method to dispose of the object. Otherwise, your application will have a memory leak problem. To ensure that the object is freed, use resolveasdisposable as much as possible (as shown in the example above). It automatically calls the release method at the end of the using code block.

Iiocresolver (and Iiocmanager) have a createscope extension method (defined in the Abp.dependency namespace) to safely release all of the parsed dependent resources. As shown below:

using (var scope = _iocresolver.createscope ())
{
    var simpleObj1 = scope. Resolve<simpleservice1> ();
    var simpleObj2 = scope. Resolve<simpleservice2> ();
    //...
}

At the end of the using statement block, all the parsed dependent resources are freed automatically. You can also use the iscopediocresolver interface to do this. You can inject the interface and parse the dependencies. When you use a class that is freed, all dependent resources that are parsed are also automatically freed. However, be careful with it, for example: If the class has a long life cycle (such as singleton mode) and needs to parse many objects, they will remain in memory until the class is freed.

If you want to handle dependency entries directly using the IOC container (Castle Windsor), you can inject iiocmanager through the constructor and use it to Iiocmanager.ioccontainer properties. If you are in a static context or cannot inject iiocmanager, there is also the last method that you can use to iocmanager.instance the Singleton object, which you can get anywhere and everywhere. However, in this case your code will become less susceptible to testing. 3.1.3 Other 1. Ishouldinitialize interface

Some classes need to be initialized before they can be used for the first time. Ishouldinitialize has a initialize () method. If you implement it, your initialize () method is automatically called after the object is created (before it is used). Of course, in order to use this feature, you should inject/create this object. 2. asp. NET MVC & ASP Web API integration

Of course, we have to invoke the root object that relies on the injection system to process the dependency graph. In an ASP. NET MVC application, it is usually a controller class. We can inject the controller using the constructor injection pattern. When a request comes into our application, the controller and all dependencies are created recursively by the IOC container. So, who did this. This is done automatically by the ASP. NET MVC default controller factory that is extended by the ABP. The ASP. NET Web API is similar. You don't have to care about object creation and release. 3. ASP. NET Core Integration

The ASP. NET core has built-in dependency injection: Microsoft.Extensions.DependencyInjection. ABP uses Castle.Windsor.MsDependencyInjection to implement dependency injection in ASP. So you don't have to think about it. 4. Final notes (last notes)

The

ABP simplifies and automatically uses dependency injection, as long as you follow the rules and use the structure above. Most of the time this is enough. However, if you do not meet your needs, you can directly use Castle Windsor all the ability to perform any task (such as custom registration, injection hooks, interceptors, etc.).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.