[Asp.net core series] 14. IOC in .net core

Posted Jun 28, 20206 min read

  1. Introduction

Through the previous articles, we learned how to implement the basic architecture of the project:data source, routing settings, encryption and authentication. Then in the implementation, we will also encounter such a problem:when our business classes and data sources are more and more, we can not assign values for each instance through the ordinary method of constructing objects. At the same time, when assignment in the traditional sense encounters low-level switching or other modifications, a large amount of code needs to be modified, which is not friendly to changes. In order to change this situation, we are based on interface-oriented programming, and then use some DI functions and IOC framework.

1. IOC and DI

Let me explain a few concepts to you first. IOC stands for Inversion of Control, which translates to control inversion. It is a design principle of object-oriented programming that is used to reduce the coupling between codes. The so-called inversion of control is simply to leave the initialization of attributes or other parameters in the class to other parties, rather than directly using the constructor.

public class Demo1
{
}

public class Demo2
{
    public Demo1 demo;
}

For the above simple sample code, an instance of Demo1 is held in the Demo2 class. According to the previous situation, we will assign values to the demo through the following methods:

//method one
public Demo1 demo = new Demo1();
//Method Two
public Demo2()
{
    demo = new Demo1();
}

At this time, if Demo1 becomes the following:

public class Demo1
{
    public Demo1(Demo3 demo3)
    {
        //hide
    }
}
public class Demo3
{
}

Then, if Demo2 does not hold an instance object of Demo3, you need to construct an additional Demo3 when creating Demo1 at this time. If Demo3 needs to hold an object of another class, then Demo2 needs to create an additional object. Finally, you will find that you are caught in a construction "hell"(the word I invented refers to this kind of object but you have to construct a lot of other types of objects).

In fact, Demo2 does not care how Demo1's instance object is obtained, or even whether it is a subclass or interface implementation class of Demo1. I used the class in the example, but here can be replaced with Interface synchronously. After the replacement, Demo2 needs to know that Demo1 has an implementation class and the information of the implementation class when calling Demo1.

In order to solve this problem, some smart programmers have proposed that the process of object creation should be handed over to a third party to operate instead of calling a class to create it. So, the above code becomes:

public class Demo2
{
    public Demo1 Demo {get;set;}
    public Demo2(Demo1 demo)
    {
        Demo = demo;
    }
}

Doesn't seem to change? For Demo2, Demo2 is no longer responsible for the creation of Demo1, this step is handed over to the caller of Demo2 to create, Demo2 is free from the big trouble of maintaining the object of Demo1.

But in fact, the problem of constructing hell is still not solved, but this step is moved back through the design of IOC. At this time, those great gods thought about it, it is better to develop a framework of these physical objects. So there have been many IOC frameworks:AutoFac, Sping.net, Unity, etc.

Speaking of IOC, I have to mention DI(Dependency Injection) dependency injection. The so-called dependency injection is that the corresponding instance of the attribute is assigned by a third party through the constructor or using the attribute. That is how it was written in the demo code of Demo2.

Early IOC and DI refer to a technology, and later began to determine that this is a different description. IOC describes a design pattern, while DI is a behavior.

2. Use the default IOC of asp.net core

In the previous ASP.NET framework, Microsoft did not provide default IOC support. In the latest asp.net core, Microsoft provides a set of IOC support, which is in the namespace:

Microsoft.Extensions.DependencyInjection

Here, just quote in the code.

Mainly achieved through the following groups of methods:

public static IServiceCollection AddScoped<TService>(this IServiceCollection services) where TService:class;
public static IServiceCollection AddSingleton<TService>(this IServiceCollection services) where TService:class;
public static IServiceCollection AddTransient<TService>(this IServiceCollection services) where TService:class;

Only one overloaded version of these three sets of methods is listed here.

These three groups of methods represent three life cycles:

  • AddScored indicates that the life cycle of the object is the entire Request request
  • AddTransient means that it is created every time a request is made from the service container, suitable for a lightweight, stateless service
  • AddSingleton indicates that the object is obtained after the first request from the service container, and will not be initialized again afterwards

Each group of methods here only introduces one version, but in fact each method has the following versions:

public static IServiceCollection AddXXX<TService>(this IServiceCollection services) where TService:class;
public static IServiceCollection AddXXX(this IServiceCollection services, Type serviceType, Type implementationType);
public static IServiceCollection AddXXX(this IServiceCollection services, Type serviceType, Func<IServiceProvider, object> implementationFactory);
public static IServiceCollection AddXXX<TService, TImplementation>(this IServiceCollection services)
            where TService:class
            where TImplementation:class, TService;
public static IServiceCollection AddXXX(this IServiceCollection services, Type serviceType);
public static IServiceCollection AddXXX<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService:class;
public static IServiceCollection AddXXX<TService, TImplementation>(this IServiceCollection services, Func<IServiceProvider, TImplementation> implementationFactory)
            where TService:class
            where TImplementation:class, TService;

Among them:implementationFactory represents the factory method of implementing TService/TImplementation through a Provider. When the method specifies a generic type, it will automatically obtain the type information to be injected according to the generic parameter. If the generic type is not used, the parameter type must be manually passed in.

If asp.net core uses dependency injection, it needs to be set in the Startup method. For details, please refer to the following:

public void ConfigureServices(IServiceCollection services)
{
    //Omit other code
    services.AddScoped<ISysUserAuthRepository,SysUserAuthRepository>();
}

asp.net core provides different IOC support for DbContext, AddDbContext:

public static IServiceCollection AddDbContext<TContext>(
      this IServiceCollection serviceCollection,
      Action<DbContextOptionsBuilder> optionsAction = null,
      ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
      ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
      where TContext:DbContext;

The method of use is as follows:

services.AddDbContext<DefaultContext>();

3. AutoFac use

In theory, the IOC of asp.net core is good enough, but I still forgive my greed. If there are two or three hundred business classes that I need to set up, I would rather not use IOC. Because that configuration is an extremely painful process. However, the good news is that AutoFac can save me from this part of the trouble.

Here is a brief introduction to how to use AutoFac as IOC management:

cd Web # Change directory to Web project
dotnet package add Autofac.Extensions.DependencyInjection # add AutoFac reference

Because the asp.net core version 3 changed some logic, the reference method of AutoFac has changed. Now the content of the previous version is not introduced, mainly 3. To use AutoFac, you need to set the following code in the Program class:

public static IHostBuilder CreateHostBuilder(string[]args) =>
            Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //add this line of code
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

Enable AutoFac a Service provider factory class in the Program class. Then add the following method in the Startup class:

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<DefaultContext>().As<DbContext>()
                .WithParameter("connectStr","Data Source=./demo.db")
                .InstancePerLifetimeScope();


    builder.RegisterAssemblyTypes(Assembly.Load("Web"))
        .Where(t => t.BaseType.FullName.Contains("Filter"))
        .AsSelf();

    builder.RegisterAssemblyTypes(Assembly.Load("Domain"),
                    Assembly.Load("Domain.Implements"), Assembly.Load("Service"), Assembly.Load("Service.Implements"))
                .AsSelf()
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .PropertiesAutowired();
}

modify:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
            {
                options.Filters.Add<UnitOfWorkFilterAttribute>();
            }).AddControllersAsServices();//This line adds
    //omit others
}

4. Summary

This article briefly introduces how to enable IOC support in Asp.net Core, and provides two methods, which can be said to have their own advantages and disadvantages. Friends choose according to their needs. The follow-up will give you a detailed insight into the core secrets of IOC frameworks such as AutoFac.

For more content, please pay attention to My Blog "Mr. Gao's Cabin"

file