ABP – Dependency Injection (1)
Dependency injection realizes the decoupling of dependencies between systems, modules and objects, and is basically an essential component of modern application frameworks.
ABP’s dependency injection system is based on Microsoft’s dependency injection extension library (Microsoft.Extensions.DependencyInjection), so it is fully compatible with the usage of dependency injection in .net Core. At the same time, Autofac is used to replace the internal container in .net Core. , taking advantage of some features in Autofac.
Abp dependency injection configuration method
Manually register dependency injection relationship
With Asp.Net Core, in the ConfigureServices()
method in the Startup
class, there is no dependency injection relationship with the container through IServiceCollection
The difference is that the Abp framework is also compatible with this method.
However, the configuration of these dependencies is generally configured in their respective modules, rather than all being written in the Startup class. This makes each module more independent and ready to use out of the box.
[DependsOn(typeof(AbpAspNetCoreModule))]
[DependsOn(typeof(SucereAbpDataModule))]
public class SuncereAbpFreeSqlModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddFreeSql();
}
}
The code in the example is the dependency injection configuration of an ORM module in our project. This module is grouped based on the ORM framework of FreeSql and is compatible with Abp’s work unit and default warehousing mode. As mentioned in the previous article, when the Abp framework is initialized, the container object will be passed in each module class. context.Services
is the passed IServiceCollection
.
Automatically register as agreed
In addition to manual registration, a very convenient point of the Abp framework dependency injection system is that it provides automatic dependency injection relationship registration according to the agreement. The most basic, when only the Volo.Abp core library is used, classes that implement specific interfaces will be registered. According to the life cycle of dependency injection, Abp provides three interfaces.
• ITransientDependency is registered for the transient life cycle.
• ISingletonDependency is registered for the singleton life cycle.
• IScopedDependency is registered for scoped lifecycle.
These interfaces are all empty interfaces and serve as markers. This design method is very common in Microsoft code and many frameworks.
Through research on the source code, it can be found that Abp actually provides a convention register interface IConventionalRegistrar, and Abp provides a default implementation. And when the Abp engine is initialized, all module assemblies are traversed, and the classes that meet the agreement are registered for dependency injection.
View the entry point of the automatic dependency injection source code. In fact, during the initialization process of the Abp engine mentioned in the previous article, the AddCoreAbpServices()
called in the AbpApplicationBase
constructor >In the method, services.AddAssemblyOf()
is the key.
As you can see, a default reservation register is added to the collection and dependency injection registration is performed through rules. So how does the rule register find the classes in the assembly and register the dependencies?
Here you can see the three interfaces mentioned above, but it can be seen from here that these three interfaces only provide life cycle information, but the registration of dependency injection, in addition to the life cycle, also has classes and interfaces corresponding relationship.
By viewing the codes of DefaultConventionalRegistrar
, ConventionalRegistrarBase
, ExposedServiceExplorer
, ExposeServicesAttribute
, you can understand the default dependencies of Abp What relationship registration looks like and how it is implemented.
Default rule registration:
1) Public, non-generic classes that implement three interfaces including ITransientDependency will be registered in the container.
2) If this class implements other interfaces, and the naming between this interface and the class complies with the rules, the relationship between the interface and the class will be registered in the container.
For example
public class BookRepository: IRepository, IBookRepository, IBookStore, ITransientDependency
{
}
In the above code, BookRepository
can find three dependency configurations in the container.
In addition to the default rule registration, Abp also provides other registration rules in other modules, such as AbpAspNetCoreMvcConventionalRegistrar
and Volo.Abp in the Volo.Abp.AspNetCore.Mvc module.
, etc. AbpWebAssemblyConventionalRegistrar
in AspNetCore.Components
Abp framework inherent registration type
Some specific types will be registered for dependency injection by default. Example:
• Module classes are registered as singletons.
• MVC controllers (inheriting Controller or AbpController) are registered as transient.
• MVC page models (inheriting PageModel or AbpPageModel) are registered as transient.
• MVC view components (inheriting ViewComponent or AbpViewComponent) are registered as transient.
• Application services (implementing the IApplicationService interface or inheriting the ApplicationService class) are registered as transient.
• The repository (implementing the IRepository interface) is registered as transient.
• Domain services (implementing the IDomainService interface) are registered as transient.
We can also implement our own dependency injection registration rules. We only need to implement the IConventionalRegistrar
interface and add it to the PreConfigureServices()
method in the module class. in ConventionalRegistrarList
.
Register via feature
It can also be seen from the above source code that the Abp framework also supports declaring dependency injection relationships through features, and the declaration method of features will take precedence over the default agreement.
We can use DependencyAttribute
to declare the life cycle and injection method of dependency injection, and ExposeServicesAttribute
to declare the correspondence between classes and interfaces. ExposeServicesAttribute
is the implementation class of IExposedServiceTypesProvider
.
Special attention should be paid to the fact that when ExposeServicesAttribute
is declared and the values of IncludeDefaults
and IncludeSelf
are not set, due to the default value , these two values will be false, that is, the default convention does not work, and the class is registered as an implementation of the interface specified in ExposeServicesAttribute
. Of course, ExposeServicesAttribute
can specify multiple interfaces.
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(typeof(IBookRepository))]
public class BookRepository: IRepository, IBookRepository, IBookStore
{
}
Registration of generic classes
From the source code, we can know that whether it is automatically registered according to the agreement or registered through features, the dependency injection relationship of a generic class cannot be registered. The dependency injection relationship of a generic class can only be registered manually. injection.
context.service.AddTransient<IRepository, Repository>();
You can register the dependency injection relationship of a generic class through the above method. One of indicates that the class has two generic types.
The above is the content of the dependency injection configuration part. It is split into two articles to avoid the article being too long and uncomfortable for everyone to read
ABP series summary:
Table of Contents: ABP Series Summary
Previous article: ABP – Module loading mechanism
Next article: ABP – Dependency Injection (2)