.Net Core依赖注入
往期文章:
- .ner Core实现接口限流
- .net Core程序发布到IIS(Window Server 2019)
文章目录
- .Net Core依赖注入
- 前言
- 一、ICO 和DI和DL
- ICO [控制反转]
- DI [依赖注入]
- DL [依赖查找]
- 二、.net Core 中的依赖注入【Autofac】
- 瞬时模式
- 作用域模式
- 单例模式
- 尝试注册
- 移除和替换注册
- 注册泛型模板
- 三、使用依赖注入注意点:
- 四、实现 IDisposable 接口类型的释放
- 五、Autofac与.netCore自带的依赖注入对比
- 基于名称的注入
- 属性注入
- 子容器
- 基于动态代理的 AOP
- 六、.netCore 6.0 中依赖注入的使用
前言
相信大家在学习.net Core的时候都听说过依赖注入,和控制反转等概念。随着.net Core 6.0的到来,相比于之前的.net Core 3.1 做了不小的改变,依赖注入的方式也和之前的方式有些许不同,但是万变不离其宗,如果你会用.net Core 3.1那.net Core 6.0应该也能很快上手,不会.net Core 3.1的同学也不用担心,相信你看了下面的教程也能学会如果依赖注入。
一、ICO 和DI和DL
学习.net Core 的同学都应该听说过这两个东西。那么他们分别是什么呢?他们呢各自的作用又是什么呢?接下来我就来给大家讲解一下。
ICO [控制反转]
ICO:它的全称是【Inversion of Control】翻译过来就是控制反转。
大佬是这样说的:
所谓控制反转,反转的是类与类之间依赖的创建。类型A依赖于类型B时,不依赖于具体的类型,而是依赖于抽象,不在类A中直接 new 类B的对象,而是通过外部传入依赖的类型对象,以实现类与类之间的解耦
好像不太好理解,给大家举个例子:
比如你去你妹妹家串门,而她做的饭巨难吃,你就自己带着一同泡面去串门,吃饭的时候就吃自己带的这桶泡面。此时你就实现了控制反转,不用吃你妹妹做的黑暗料理了。
DI [依赖注入]
DI:它的全称是【Dependency Injection】翻译过来就是依赖注入。
大佬的解释是:
所谓依赖注入,就是由一个外部容器对各种依赖类型对象进行管理,包括对象的创建、销毁、状态保持等,并在某一个类型需要使用其他类型对象的时候,由容器进行传入。
好像还是不太好理解,再给大家举个例子:
比如你要出去旅行,把牙刷,毛巾,出行所需的衣物和鞋子等东西装入背包,这些物品都是你此次旅行可能需要的东西也就是依赖。你把它装入背包的过程就可以理解为依赖注入。
DL [依赖查找]
DL:它的全称是【Dependency Lookup】翻译过来是依赖查找。
大佬是这样描述它的:
它是控制反转设计原则的一种实现方式。它的大体思路是:容器中的受控对象通过容器的 API 来查找自己所依赖的资源和协作对象。这种方式虽然降低了对象间的依赖,但是同时也使用到了容器的 API,造成了我们无法在容器外使用和测试对象。依赖查找是一种更加传统的 IOC 实现方式。
依赖查找也有两种方式:
- 依赖拖拽:注入的对象如何与组件发生联系,这个过程就是通过依赖拖拽实现;
- 上下文依赖查找:在某些方面跟依赖拖拽类似,但是上下文依赖查找中,查找的过程是在容器管理的资源中进行的,而不是从集中注册表中,并且通常是作用在某些设置点上;(JNDI)
这个就很好理解了就不用给大家举例子了。
二、.net Core 中的依赖注入【Autofac】
Autofac 中依赖的三种生命周期:
瞬时模式
- Transient:瞬时模式,每次请求,都获取一个新的实例。即使同一个请求获取多次也会是不同的实例,它会在这个请求处理结束后被释放。
示例:
services.AddTransient<IWebService, WebService>();
作用域模式
- Scoped:作用域模式,每个客户端请求(连接)创建一次。同一个请求获取多次会得到相同的实例,它也会在这个请求处理结束后被释放。
示例:
services.AddScoped<IWebService, WebService>();
单例模式
- Singleton:单例模式,每次都获取同一个实例,它会在根容器释放的时候释放。
示例:
services.AddSingleton<IWebService, WebService>();
权重大小对比:Singleton>Transient>Scoped
注入形式展示:
- 直接注入实例
services.AddScoped<IMyService>(new MyService());
- 工厂模式注册
services.AddScoped<IMyService>(serviceProvider =>
{
return new MyService();
});
- 注册不同示例
services.AddScoped<IMyService>(new MyService());
services.AddScoped<IMyService>(new MyService2());
尝试注册
尝试注册会先判断实例是否已经注册,若注册了则不再进行注册;尝试注册可以有效的避免服务重复注册。
services.TryAddScoped<IMyService>(new MyService());
services.TryAddEnumerable(ServiceDescriptor.Scoped<IMyService, MyService>());
移除和替换注册
替换注册可以替换原有实例注册MyService为MyService2,RemoveAll则是移除所以实现了IMyService的注册实例。
services.AddScoped<IMyService, MyService>();
services.Replace(ServiceDescriptor.Singleton<IMyService, MyService2>());
services.RemoveAll<IMyService>();
注册泛型模板
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
三、使用依赖注入注意点:
- 避免通过静态属性的方式访问容器对象
- 避免在服务内部使用 GetService 方式来获取实例
- 避免使用静态属性存储单例,应该使用容器管理单例对象
- 避免在服务中实例化依赖对象,应该使用依赖注入来获得依赖对象
- 避免向单例的类型注入范围的类型
四、实现 IDisposable 接口类型的释放
- DI 只负责释放由其创建的对象实例
- DI 在容器或子容器释放时,释放由其创建的对象实例
- 避免手动创建实现了 IDisposable 对象,应该使用容器来管理其生命周期
五、Autofac与.netCore自带的依赖注入对比
Autofac的功能比.Net Core 的原生依赖注入更丰富,具体表现在:
- 基于名称的注入
- 属性注入
- 子容器
- 基于动态代理的 AOP
基于名称的注入
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<MyService>().Named<IMyService>("s1");
builder.RegisterType<MyService2>().Named<IMyService>("s1");
}
从IoC容器中使用ResolveNamed获取:
var s1 = container.ResolveNamed<IMyService>("s1");
var s2 = container.ResolveNamed<IMyService>("s2");
属性注入
用PropertiesAutowired方法支持属性注入:
builder.RegisterType<MyService>().As<IMyService>().PropertiesAutowired();
子容器
builder.RegisterType<MyService>().InstancePerMatchingLifetimeScope("myscope");
子容器的使用:
var autofacContainer = app.ApplicationServices.GetAutofacRoot();
using (var myscope = autofacContainer.BeginLifetimeScope("myscope"))
{
var service1 = myscope.Resolve<MyService>();
using (var scope = myscope.BeginLifetimeScope())
{
var service2 = scope.Resolve<MyService>();
Console.WriteLine($"service1=service2:{service1 == service2}");
}
}
基于动态代理的 AOP
需要引用包:Autofac.Extras.DynamicProxy
先实现一个拦截器继承 IInterceptor
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Intercept before,Method:{invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"Intercept after,Method:{invocation.Method.Name}");
}
}
注册
builder.RegisterType<MyService>().As<IMyService>().PropertiesAutowired().InterceptedBy(typeof(MyInterceptor)).EnableInterfaceInterceptors();
这里实现了基于接口的拦截,也可以实现基于类的拦截;基于类的拦截时,方法需要定义为虚方法。
示例代码搬运自:Aoss丶的博客->.Net Core 依赖注入
六、.netCore 6.0 中依赖注入的使用
-
第一步、添加依赖注入的两个关键包:
Autofac.Extensions.DependencyInjection 和 Autofac.Extras.DynamicProxy
-
第二步、重写load方法:
这种方法式为了配合程序集注入的
这里注意:根据名称约定 查询到的程序集 的命名规则必须规范 否则可能读取不到 对应的数据集 从而报错 -
第三步,在Program.cs中注册:
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
//第一种方式 实现和接口方式注入
builder.RegisterType<LoginServices>().As<ILoginServices>();
//第二中调用程序集注入(引用式)
builder.RegisterModule(new AutofacModuleRegister());
//第二种注入方式 程序集注入(反射式)
var assemblysServices = Assembly.Load("AutofacModuleRegister");
builder.RegisterAssemblyTypes(assemblysServices)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
});
- 第四步,在构造函数中注入: