为什么要使用依赖注入框架

  • 借助依赖注入框架,将对象的创建交由容器管理,确保代码的可维护性和可扩展性。
  • .NET Core 的整个架构中,依赖注入框架提供了对象创建和生命周期管理的核心能力,各个组件相互协作,也是由依赖注入框架的能力来实现的。

.Net Core DI

.Net Core实现依赖注入两个包:

Microsoft.Extensions.DependencyInjection.Abstractions
Microsoft.Extensions.DependencyInjection 

Microsoft.Extensions.DependencyInjection.Abstractions 为抽象包,Microsoft.Extensions.DependencyInjection 为具体实现包

核心类

IServiceCollection
ServiceDescriptor
IServiceProvider
IServiceScope

IServiceCollection 负责注册,作为Startup类中ConfigureServices方法的参数,使用其对对象进行注册。
ServiceDescriptor 负责注入服务元素的描述
IServiceProvider 负责提供实例
IServiceScope 负责实例的生命周期

三种生命周期

  • 瞬时 Transient:每次请求,都获取一个新的实例;即使同一个请求获取多次也会是不同的实例。
  • 作用域 Scoped: 每次请求,都获取一个新的实例;同一个请求获取多次会得到相同的实例,即一个http请求内唯一。
  • 单例 Singleton:每次都获取同一个实例。

服务注册

单例注册

 services.AddSingleton<IMyService, MyService>();

作用域注册

 services.AddScoped<IMyService, MyService>();

瞬时注册

 services.AddTransient<IMyService, MyService>();

直接注入实例

 services.AddScoped<IMyService>(new MyService());

工厂模式注册

 services.AddScoped<IMyService>(serviceProvider =>
 {
    return new MyService();
 });

注册不同实例

 services.AddScoped<IMyService>(new MyService());
 services.AddScoped<IMyService>(new MyService2());

MyService和MyService2均实现了IMyService接口,并且进行了注册;使用的时候,默认是用的最后注册的(MyService2);如果要使用MyService, 需要遍历,根据实际类型来判断。

[HttpGet]
public async Tash GetServiceList([FromServices] IEnumerable<IMyService> services)
{
    foreach (var service in services)
    {
        if (service is MyService) //  if (service.GetType() == typeof(MyService))
        {
			// do sth
        }      
    }
}

尝试注册

 services.TryAddScoped<IMyService>(new MyService());
 services.TryAddEnumerable(ServiceDescriptor.Scoped<IMyService, MyService>());

尝试注册会先判断实例是否已经注册,若注册了则不再进行注册;尝试注册可以有效的避免服务重复注册。

移除和替换注册

 services.AddScoped<IMyService, MyService>();
 services.Replace(ServiceDescriptor.Singleton<IMyService, MyService2>());
 services.RemoveAll<IMyService>();

替换注册可以替换原有实例注册MyService为MyService2,RemoveAll则是移除所以实现了IMyService的注册实例。

注册泛型模板

 services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));

使用依赖注入注意点

  • 避免通过静态属性的方式访问容器对象
  • 避免在服务内部使用 GetService 方式来获取实例
  • 避免使用静态属性存储单例,应该使用容器管理单例对象
  • 避免在服务中实例化依赖对象,应该使用依赖注入来获得依赖对象
  • 避免向单例的类型注入范围的类型

实现 IDisposable 接口类型的释放

  • DI 只负责释放由其创建的对象实例
  • DI 在容器或子容器释放时,释放由其创建的对象实例
  • 避免手动创建实现了 IDisposable 对象,应该使用容器来管理其生命周期

Autofac

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();

这里实现了基于接口的拦截,也可以实现基于类的拦截;基于类的拦截时,方法需要定义为虚方法。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐