Control inversion IOC dependency injection di Autofac note

Posted by bluntster on Thu, 13 Jan 2022 17:55:56 +0100

Theoretical basis of DI

rely on

What is dependency? In short, it is the relationship between classes. Class dependency occurs when a class needs another class to work together

This dependency between classes is coupling. We can't eliminate the coupling (unavoidable), but we can make this coupling dependency clear and easy to manage through DI

Example: Classic three-tier architecture

UI: Interface

BLL: business logic processing

DAL: data access

Code of data access DAL layer:

 /// <summary>
 ///Storage
 /// </summary>
 public class TestRepository
 {
     public string GetName(long id)
     {
         return "Zhang San";
     }
 }

Business layer (BLL) code:

    /// <summary>
    ///Logical processing
    /// </summary>
    public class TestService
    {
        private readonly TestRepository m_testRepository;
​
        public TestService()
        {
            m_testRepository = new TestRepository();
        }
​
        public string GetName(long id)
        {
            var name = m_testRepository.GetName(id);
            return name.Name;
        }
    }

TestService needs to rely on TestRepository, which is a tight coupling. Once the TestRepository is changed, the code of TestService will also need to be changed. If the amount of changes is large, it will explode

Interface oriented programming

Facing is to realize a design principle: rely on abstraction rather than concrete implementation.

For example, the above example adds an interface to the DAL layer, such as ITestRepository, and the implementation code is as follows:

     /// <summary>
    ///Warehousing interface
    /// </summary>
    public interface ITestRepository
    {
        string GetName(long id);
    }

At this time, the TestService of BLL layer can rely on ITestRepository, for example:

    /// <summary>
    ///Logical processing
    /// </summary>
    public class TestService
    {
        private readonly ITestRepository m_testRepository;
​
        public TestService()
        {
            m_testRepository = new TestRepository();
        }
​
        public string GetStuName(long id)
        {
            var name = m_testRepository.GetName(id);
            return name.Name;
        }
    }

There are two benefits of this, one is low coupling and the other is clear responsibility.

For example, those who write the storage layer only need to implement the interface of ITestRepository, regardless of who calls me. Those who write the BLL layer Service only need to call the interface in ITestRepository, regardless of how the interface is implemented. Another example:

If the person who writes the TestRepository writes badly, it's better to rewrite it. At this time, you can rewrite an itesrepository interface to implement newtestrepository. After rewriting, the Service layer call only needs to replace the corresponding implementation:

    public TestService()
    {
       m_testRepository = new NewTestRespository();
    }

But in fact, this problem is also very big. For example, if more than N services are used in a huge system, it is still troublesome to modify more than N places.

The reason is that as mentioned above, this is a dependency relationship. A Service depends on a Repository. Is there a way to reverse this control relationship? When a Service needs to use a Repository, is there any way to inject the Repository I need into me?

Of course, this is the dependency injection we will implement.

After using dependency injection, you will find that after rewriting the new warehouse newtestrepository, the BLL business logic layer (TestService) does not need to change any code. All services do not need to be changed one by one. Modify the rules directly during injection. Do not inject the old warehouse. Just inject the new warehouse directly

Interface Oriented Architecture:

namedutygive an example
Interface layer (UI)Responsible for displaying dataTestController
Business logic abstraction layer (InterfaceBLL)Business logic operation abstract interfaceITestService
Business logic layer (BLL)Responsible for business logic operationTestService
Data access abstraction layer (InterfaceDAL)Data access abstract interfaceITestRepository
Data access layer (DAL)Responsible for providing dataTestRepository

What is IoC

IoC, full name of Inversion of Control, namely "control reversal", is a design principle proposed by Martin Fowler

What is DI

DI, fully known as Dependency Injection, that is, Dependency Injection, is one of the design methods to realize IoC.

It is characterized by injecting dependent objects into callers through some techniques. (for example, injecting Repository into Service)

At present, the skills mentioned here mainly refer to the container. First, add all objects that will generate dependencies to the container, such as TestRepository and TestService, and give the allocation permission to the container. When TestService needs to use TestRepository internally, it should not be allowed to create a new one by itself, but inject TestRepository into TestService through the container.

This is the origin of the name "dependency injection".

What is the difference between DI and IoC

Are DI and IoC the same thing? Answer: not a thing.

The difference is also very simple: IOC is a concept, DI is one of the methods to realize IOC, an IOC implementation technology.

IoC is a very broad design concept. Changing a dead variable in the program to read from the configuration file is also a control inversion (from program control to framework control). Changing this configuration to an input text box in the user UI interface is also a control inversion (from framework control to user control).

Therefore, IOC and DI are not the same thing. IOC is a concept and DI is one of the means

AutoFac

Write an AutoFac console application to illustrate how to simply use this container

Program startup process: Main ------ > service ------ > repository

First, establish corresponding interfaces and classes:

Repository:    

    /// <summary>
    ///Storage layer interface
    /// </summary>
    public interface ITestRepository
    {
        /// <summary>
        ///Get name by ID
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        string GetNameById(string id);
    }
​
    public class TestRepository : ITestRepository
    {
        public string GetNameById(string id)
        {
            return "Zhang San";
        }
    }

Service:

    /// <summary>
    ///Service layer interface
    /// </summary>
    public interface ITestService
    {
        /// <summary>
        ///Get name by ID
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        string GetNameById(string id);
    }
​
    public class TestService : ITestService
    {
        private ITestRepository m_testRepository;
        
        //Injection interface
        public TestService(ITestRepository testRepository)
        {
            m_testRepository = testRepository;
        }
​
        public string GetNameById(string id)
        {
            return m_testRepository.GetNameById(id);
        }
    }

Autofac container initialization and registration

    public static class Container
    {
        public static Autofac.IContainer Instance;
​
        public static void Init()
        {
            //Create a new container builder to register components and services
            var builder = new ContainerBuilder();
            Register(builder);
            //Creating containers with builder
            Instance = builder.Build();
        }
​
        private static void Register(ContainerBuilder builder)
        {
            builder.RegisterType<TestRepository>().As<ITestRepository>();
            builder.RegisterType<TestService>().As<ITestService>();
        }
    }
  • public static IContainer Instance is a singleton container

  • ContainerBuilder is a container constructor defined for AutoFac, through which objects are registered into the container.

  • RegisterType is the most basic registration method encapsulated by AutoFac. The passed in generic type (TestRepository) is the object to be added to the container; As is responsible for binding the type of the registered object, which is generally exposed by the interface type it implements. From the outside of the container, the container object can only be found through the exposed type

Main function

	class Program
    {
        static void Main(string[] args)
        {
            Container.Init();
            GetName();
            Console.ReadKey();
        }

        private static void GetName()
        {
            //Resolve objects from container
            ITestService service = Container.Instance.Resolve<ITestService>();
            string name = service.GetNameById("123");
            Console.WriteLine($"name is {name}");
        }
    }

Here is the container Instance. Resolve<ITestService>(); The implementation steps are as follows

1. The container uses ITestService to find the corresponding class (TestService) in the container. After finding it, it will try to instantiate this class. At this time, it is found that the constructor of this class has parameters

2. When the container finds that this parameter is ITestRepository, it goes to the container to find the class corresponding to this type, and then instantiates it and automatically injects it into the TestService class

3. At this point, a simple dependency injection is completed

We can find the registered objects in the Instance container through the breakpoint, which is not shown in the figure above

Topics: C# .NET