0. Preface
Through the previous articles, we learned how to implement the basic architecture of the project: data source, routing settings, encryption and authentication. In the implementation, we will encounter such a problem: when we have more and more business classes and data sources, we can't assign values to each instance through common methods of constructing objects. At the same time, when the assignment in the traditional sense encounters the bottom switch or other changes, it needs to modify a lot of code, which is not friendly to change. 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
First of all, I'll explain some concepts to you. IOC is inversion of control, which translates into inversion of control. It's a design principle of object-oriented programming to reduce the coupling between codes. The so-called inversion of control is simply to give the initialization of properties 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 simple example code above, an instance of Demo1 is held in the Demo2 class. If we follow the previous situation, we will assign values to the demo through the following methods:
// Method 1 public Demo1 demo = new Demo1(); // Method 2 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, an additional Demo3 needs to be constructed when creating Demo1. If Demo3 needs to hold an object of another class, then one more object needs to be created in Demo2. In the end, you'll find that you're in a construction hell (I invented the word for constructing a lot of other types of objects for one object).
In fact, for Demo2, we don't care how the instance object of Demo1 is obtained, or even whether it is a subclass or Interface implementation class of Demo1. I used the class in the example, but here I can synchronously replace it with Interface. After replacement, when Demo2 calls Demo1, it also needs to know that Demo1 has the implementation class and the information of the implementation class.
In order to solve this problem, some brilliant programmers put forward that the creation of objects should be done by a third party instead of calling classes. As a result, the above code becomes:
public class Demo2 { public Demo1 Demo {get;set;} public Demo2(Demo1 demo) { Demo = demo; } }
Nothing seems to have changed? For demo2, demo2 is no longer responsible for the creation of Demo1. This step is left to the caller of demo2 to create. Demo2 is free from the big trouble of maintaining Demo1.
But in fact, the problem of constructing hell hasn't been solved. It's just that the IOC design moves this step backward. At this time, those great gods think about it. It's better to develop a framework for these entity objects. So there are many IOC frameworks: AutoFac Sping.net , Unity, etc.
When it comes to IOC, we have to mention DI (Dependency Injection). The so-called dependency injection is that the corresponding instance of the property is assigned by the third party through the constructor or using the property. That is, the last Demo2 example code.
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 asp.net Default IOC for core
Before ASP.NET In the framework, Microsoft does not provide default IOC support. In the latest asp.net In core, Microsoft provides a set of IOC support, which is available in namespace:
Microsoft.Extensions.DependencyInjection
You can reference it in the code.
It is mainly realized by the following 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 groups of methods is listed here.
These three groups of methods represent three life cycles:
- AddScored indicates that the life cycle of an object is the entire Request request
- AddTransient indicates that it is created every time a request is made from a service container, which is suitable for lightweight and stateless services
- AddSingleton means that the object is obtained after the first request from the service container, and will not be initialized again
Each group of methods here only introduces one version, but actually 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;
Where: implementationFactory refers to a factory method that implements TService/TImplementation through a Provider. When the method specifies generics, the type information to be injected will be automatically obtained according to the generic parameters. If no generics are used, the parameter type must be passed in manually.
asp.net If the 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 codes 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. Use of Autofac
theoretically, asp.net The IOC of core is good enough, but still forgive my greed. If there are two or three hundred business classes that need to be set by me, 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 of how to use AutoFac as IOC Management:
cd Web # Switch directory to Web project dotnet package add Autofac.Extensions.DependencyInjection # Add reference to autoface
because asp.net Core version 3 has changed some logic, and the reference mode of AutoFac has changed. The content of the previous version will not be introduced now, mainly 3. To use autoface, 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 a Service provider factory class of AutoFac in the Program class. Then add the following methods 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(); }
Modification:
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(options => { options.Filters.Add<UnitOfWorkFilterAttribute>(); }).AddControllersAsServices();// New in this line // Omit others }
4. Summary
This article briefly introduces how to Asp.net IOC support is enabled in the core, and two methods are provided, which can be said to have advantages and disadvantages. My friends choose according to their own needs. Later, we will go into the core secrets of the IOC framework such as AutoFac in detail.
Please pay attention to more My blog Mr. Gao's Cabin