The foreword 0.

In the previous articles, we learned how to implement the basic architecture of the project: data sources, routing Settings, encryption, and authentication. Then in the implementation, we will also encounter such a problem: when we have more and more business classes and data sources, we can not use the ordinary method of constructing objects to assign values to each instance. At the same time, assignment in the traditional sense is not change-friendly because it requires a lot of code to change when there are low-level switches or other changes. To change this, we programed on an interface oriented basis, then used some DI functionality and an IOC framework.

1. The IOC and DI

IOC is an object-oriented programming design principle used to reduce coupling between code. Inversion of control simply means that the initialization of a property or other parameter in a class is handled by another party rather than using the constructor directly.

public class Demo1
{
}

public class Demo2
{
    public Demo1 demo;
}Copy the code

For the simple example code above, an instance of Demo1 is held in the Demo2 class. As before, we would assign the demo as follows:

Public Demo1 demo = new Demo1(); Public Demo2() {demo = new Demo1(); }Copy the code

At this point, if Demo1 looks like this:

Public class Demo1 {public Demo1(Demo3 Demo3) {// hide}} public class Demo3 {}Copy the code

Therefore, if Demo2 does not hold an instance of Demo3, we need to construct an additional Demo3 when creating Demo1. If Demo3 needs to hold an object of another class, then Demo2 needs to create an additional object. You end up in a kind of construction hell (a term I coined for having to build a whole bunch of other types of objects for one object).

In fact, for Demo2, it doesn’t matter how the instance object of Demo1 is obtained, or even whether it is a subclass of Demo1 or an interface implementation class. I used a class in my example, but I can replace it with Interface synchronously. After that, Demo2 needs to know that Demo1 has an implementation class and information about the implementation class when it calls Demo1.

To solve this problem, some clever programmers have proposed handing over the creation of objects to a third party rather than calling classes. As a result, the above code becomes:

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

Doesn’t seem to have changed? In the case of Demo2, Demo2 is no longer responsible for creating Demo1. This step is left to the caller of Demo2, and Demo2 is freed from the big trouble of maintaining Demo1.

In reality, however, the problem of constructing hell has not been solved, but it has been postponed by the IOC’s design. At this point, the gods thought, why not develop a framework for these solid objects? As a result, there are many IOC frameworks: AutoFac, Sping.net, Unity, etc.

Talking about IOC, we have to mention DI (Dependency Injection). Dependency injection is the assignment of an instance of an attribute by a third party, either through a constructor or by using the attribute. This is how the last example code for Demo2 was written.

Early IOC and DI referred to one technique, but later it was decided that these were different descriptions. IOC describes a design pattern, while DI is a behavior.

2. Use the default IOC for 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 in the namespace:

Microsoft.Extensions.DependencyInjectionCopy the code

In the code reference.

Mainly 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;Copy the code

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

These three groups of methods represent three life cycles:

  • Addmagician indicates that the life cycle of the object is the entire Request
  • AddTransient is created each time a request is made from a service container and is suitable for lightweight, stateless services
  • AddSingleton indicates that the object is retrieved after the first request from the service container and will not be initialized again

Only one version of each group of methods is presented here, but there are actually several versions of each method:

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;Copy the code

The implementationFactory method implements the TService/TImplementation factory method via a Provider. When a method specifies a generic type, information about the type to be injected is automatically obtained based on the generic parameter. If a generic parameter is not used, the parameter type must be passed in manually.

Asp.net core requires dependency injection (di) to be set in the Startup method.

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

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;Copy the code

The usage method is as follows:

services.AddDbContext<DefaultContext>();Copy the code

3. The AutoFac use

In theory, IOC for ASP.NET Core is good enough, but forgive me for being greedy. If I have 200 or 300 business classes to set up, I’d rather not use IOC. Because it’s an extremely painful process to configure. Thankfully, AutoFac spared me that part of the problem.

Here’s a quick look at how to use AutoFac for IOC management:

CDS switch # Web directory to the Web project dotnet package add Autofac. Extensions. DependencyInjection # add Autofac referencesCopy the code

Because asp.net Core version 3 has changed some of the logic, the way AutoFac references are changed. To use AutoFac, 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. The ConfigureWebHostDefaults (webBuilder = > { webBuilder.UseStartup<Startup>(); });Copy the code

A Service that enables AutoFac in the Program class provides the factory class. Then add the following method to 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();
}
Copy the code

Modification:

public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(options => { options.Filters.Add<UnitOfWorkFilterAttribute>(); }).AddControllersAsServices(); // this line is new // omit other}Copy the code

4. To summarize

This article provides an overview of how to enable IOC support in Asp.net Core and provides two approaches, each with its own pros and cons. Friends choose according to their own needs. We’ll delve into the core secrets of IOC frameworks like AutoFac later.

For more information, please check out my blog Mr. Gao’s Cabin.