Net Core依赖注入

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Net Core依赖注入相关的知识,希望对你有一定的参考价值。

参考技术A 利用Startup类中的configuration读取appsettings.json中的配置

1.container.AddTransient为瞬时生命周期,每次创建都是一个全新的实例

2.container.AddSingleton单例:全容器都是一个

3.container.AddScoped请求单例,一个请求代表一个作用域

1.瞬时,即时构造,即时销毁

2.单例,永远只构造一次

3.作用域单例,一次请求只构造一个

第一次请求解析

1.开始构造A类,因为是瞬时生命周期那么第一次请求就会被构造,然后销毁

2.在构造B类时需要A类,那么会首先构造A类(因为A类瞬时上一次已经被销毁),然后构造B类为单例

3.开始构造C,在构造时需要B类,因为B类全局单例,那就会直接构造C为作用域单例

4.直接构造瞬时D类

5.开始构造E类,构造式需要C类,因为C类为作用域单例,那么就会直接构造E类为瞬时

第一次请求结论

第二次请求解析

1.开始构造A类,因为是瞬时生命周期那么第二次请求就会重新被构造,然后销毁

2.在构造B类时因为在第一次请求时已经构造为单例,所以不再被构造

3.开始重新构造C,因为C在第二次请求为新的作用域,在构造时需要B类,因为B类全局单例,那就会直接构造C为作用域单例

4.直接构造瞬时D类

5.开始构造E类,构造式需要C类,因为C类为作用域单例,那么就会直接构造E类为瞬时

第二次请求结论

1.输出A
2.输出C
3.输出D
4.输出E

如果不想通过构造全部自动注入,能自定义注入

1.using Microsoft.Extensions.DependencyInjection;

2.首先注入 IServiceProvider serviceProvider,利用serviceProcider.GetService();生成需要的实例

1.下载Autofac.Extensions.DependencyInjection和Autofac包

2.在Program网站启动时替换默认IOC工厂实例为Autofac,UseServiceProviderFactory(new AutofacServiceProviderFactory())

3.在Startup类中添加方法public void ConfigureContainer(ContainerBuilder services)services.RegisterType().As().SingleInstance();

1.NuGet安装引用Autofac.Extras.DynamicProxy

2.引用using Autofac.Extras.DynamicProxy;

3.创建类库继承自IInterceptor实现接口

4.自定义的Autofac的AOP扩展

.NET CORE 依赖注入 实践总结

知识点回顾

  • 依赖包。 Microsoft.Extensions.DependencyInjection.Abstractions
  • 核心对象和方法。
    • IServiceCollection。注入对象的容器。注意它只存储对象的元数据,并不保存实例对象。
    • IServiceProvider。注入对象的提供者。它负责提供具体的对象实例。在架构中,IServiceProvider有2种身份,一种是Root ServiceProvider,由service.BuildServiceProvider()创建,生命周期贯穿整个应用程序,AddSingleton对象保存在这里。另外一种则是普通IServiceProvider,由IServiceScope创建,生命周期即为AddScoped的生命周期。AddScope 的对象保存在这里。普通ServiceProvider由Root ServiceProvider创建的IServiceScope创建。
    • IServiceScope。表某一个生命周期范围。由ServiceProvider.CreateScope()创建。
    • 注入方式,知识点一
      • 注入功能默认在Startup类中的ConfigureServices方法中。
    • 注入方式,知识点二。支持以下三种方式
      • AddScoped。生命周期为Scoped类型。例如在web框架中,即表示一次Request请求范围内。
      • AddSingleton。单例,应用程序全局使用同一个实例。
      • AddTransient。即时的,即对象每次使用都会重新实例化。  
    • 注入方式,知识点三。提供多种注入技巧,以Transient为例
      • 实例注入。AddTransient<TService>(this IServiceCollection services)。
      • 泛型注入。AddTransient<TService, TImplementation>(this IServiceCollection services)。
      • 工厂注入。AddTransient<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory)。
      • TryAddXXX。仅当XXX尚未注册实现时,注册该服务。此方法用来避免在容器中注册一个实例的两个副本。
    • 获取实例的方法 GetServiceGetRequiredService的区别,前置如果service不存在会返回NULL,后者会直接抛出异常。根据需要选择GetRequiredService,可能会让你的代码变得简洁一点。

WHY 依赖注入

这里只谈益处。

  • 使用接口或基类抽象化依赖关系实现,明确各个类之间的依赖关系。
  • 生命周期的统一管理,尤其对于某些类被多处依赖,关系会变得分散难以管理,依赖注入容器可以解决这点。
  • 非常利于单元测试。

最佳实践

部分来自官方文档的一些建议

  • 对于需要注入为单例的实例,不要依赖Scoped实例。会触发 .NET CORE作用域验证失败。
  • 不要从Root IServiceProvider解析有作用域的实例,这样会导致该作用域的实例变成单一实例。同样会触发作用域验证失败。
  • 对于Asp.Net Core,尽量通过构造函数而不是HttpContext.RequestServices获取实例,这样更易于单元测试。
  • 需要对某个组件服务或是一些服务集合(包括其依赖注入时),使用约定的 Add{SERVICE_NAME} 扩展方法来注册该服务所需的所有服务。
  • 若必须要从IServiceProvider中解析实例(如在单元测试中),请通过using (var scope = ServiceProvider.CreateScope()){ }方式创建作用域来获取实例。
  • 代码中避免设计有状态的、静态类和成员。可以考虑设计注入为单一实例。
  • 代码中避免在服务中直接实例化以来类。尽量采用依赖注入的方式
  • 注意以下两种方式注入的区别,后者的实例化不是服务容器创建的,所有容器不会处理实例的Dispose !!
    public class Service1 : IDisposable {}
    public class Service2 : IDisposable {}
    
    //方式一
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<Service1>();
        services.AddSingleton<IService2>(sp => new Service2());
    }
    
    //方式二
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<Service1>(new Service1());
        services.AddSingleton(new Service2());
    }
  • 延伸上一点,对于复杂对象的创建,尽量采用提供的工厂注入方式。注意工厂注入的参数是IServiceProvider,可以通过它获取你需要的实例对象。
  • 继续延伸上一点,不要在ConfigureServices方法中 通过collection.BuildServiceProvider()来获取IServiceProvider。这个创建的是一个Root IServiceProvider。单例会实例化一次,然后ConfigureServices方法结束后框架会再次调用collection.BuildServiceProvider(),单例又会重新实例化一次。
  • 不支持基于async/await和Task的服务解析。
  • 避免在容器中直接存储数据和配置。配置应使用NET CORE的选项模型。
  • 避免使用服务定位器模式。例如直接注入IServiceProvider来获取多个需要的服务。PS,如果你的服务依赖项过多,应该考虑分割成几个小功能服务了。

引入第三方IOC框架

.NET CORE 3.x版本后,引入第三方IOC框架的方式变更了,这里不再贴出2.x的方式。以Autofac框架为例。

Program.cs

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>()
                            .ConfigureLogging((hostingContext, logging) =>
                            {
                                logging.ClearProviders();
                                logging.AddConsole();
                                logging.AddNLog();
                            });
                });

Startup.cs

     
     //原来的
ConfigureServices保留,也可以使用原来的框架继续注入
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddMemoryCache();
            services.Configure<List<string>>(Configuration.GetSection("BlackPhoneList"));
            services.Configure<Dictionary<string, string>>(Configuration.GetSection("BusinessMessages"));
        }
        //增加ConfigureContainer(ContainerBuilder builder) 方式,使用Autofac框架注入
        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterType<PhoneBlackListValidator>().Named<IPhoneValidator>("PHONE_BLACKLIST").SingleInstance();
            builder.RegisterType<PhonePerDayCountValidator>().Named<IPhoneValidator>("PHONE_PERDAYCOUNT").SingleInstance();
            builder.RegisterType<UniqueIdPerDayCountValidator>().Named<IUniqueIdValidator>("UNIQUEID_PERDAYCOUNT").SingleInstance();

            //可遍历类型注入,注意 只支持IEnumerableIListICollection 类型
            builder.RegisterType<MessageSendValidator>().As<IMessageSendValidator>().SingleInstance();
        }

3.x 主要是在IServiceCollection和IServiceProvider之间增加了一个 ContainerBuilder 容器适配,使得第三方IOC框架引入更加合理了。具体实现原理可以网上源码查找。

特别关注-线程安全

  • 创建线程安全的单一实例服务。 如果单例服务依赖于一个Transient服务,那么Transient服务可能也需要线程安全,具体取决于单例使用它的方式。
  • 工厂注入方式的Func<IServiceProvider,TService>不需要是线程安全的,框架保证它由单个线程调用一次。

以上是关于Net Core依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 依赖注入基本用法

ASP.NET Core中的依赖注入:依赖注入(DI)

ASP.NET Core 依赖注入(DI)

依赖注入循环依赖.NET Core 2.0

ASP.Net Core 使用啥依赖注入框架?

[ASP.NET Core 3框架揭秘] 依赖注入:一个Mini版的依赖注入框架