• 如何在.net6webapi中实现自动依赖注入


    IOC/DI

    IOC(Inversion of Control)控制反转:控制反正是一种设计思想,旨在将程序中的控制权从程序员转移到了容器中。容器负责管理对象之间的依赖关系,使得对象不再直接依赖于其他对象,而是通过依赖注入的方式来获取所需的资源。

    DI(Dependency Injection)依赖注入:他是IOC的具体实现方式之一,使用最为广泛,DI通过在运行时动态地将某个依赖关系抽象为独立的组件,提交到容器之中,需要使用时再由容器注入,提升组件重用的频率,为系统搭建一个灵活,可扩展的平台。

    IOC/DI是一种设计模式,用于解耦组件之间的依赖关系。在传统的编程模式中,组件之间的依赖关系是硬编码在代码中的,这样会导致代码的耦合度很高,难以维护和发展。而IOC/DI模式则是通过将组件之间的依赖关系交给容器来管理,组件不再直接依赖其他组件,而是通过容器来获取所依赖的对象。这样可以使组件之间的依赖关系更加灵活,容器可以根据需要动态地创建和管理组件,从而实现更好的可维护性和可扩展性。

    如何在.net6webapi中使用依赖注入?

    首先我们定义一个服务接口及对应的实现

        public interface ITestServices
        {
            int return123();
        }
    复制代码
        public class TestServices : ITestServices
        {
            public int return123()
            {
                return 123;
            }
        }
    复制代码

    然后我们在Program.cs注入服务实现

    复制代码
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    builder.Services.AddTransient();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    
    app.UseHttpsRedirection();
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    复制代码

    值得注意的是依赖注入有三种生命周期

    • 作用域(Scoped):在应用程序启动时创建,并在应用程序关闭时销毁。这种类型的服务实例会被容器管理,但是只会被当前请求使用。当请求结束时,该服务实例会被销毁。
    • 单例(Singleton):在应用程序启动时创建,并在整个应用程序运行期间保持不变。这种类型的服务实例会被容器管理,并且可以被多个请求共享。
    • 瞬时(Transient):在应用程序启动时创建,并在应用程序关闭时销毁。这种类型的服务实例不会被容器管理,也不会被其他服务引用。

    最后在需要使用的控制器中构造函数注入就可以使用了

    复制代码
        [Route("[controller]/[action]")]
        [ApiController]
        public class TestController : ControllerBase
        {
            private readonly ITestServices _testServices;
            public TestController(ITestServices testServices)
            {
                _testServices= testServices;
            }
    
            [HttpGet]
            public int Get123() => _testServices.return123();
        }
    复制代码

     怎么实现自动注入?

    依赖注入好归好,就是每个服务都得在Program.cs注入服务实现,一但服务多起来,麻烦不说,Program.cs中的代码更是会变得凌乱不堪,可能会有小伙伴说,可以开一个扩展函数单独做注入,但私以为,既然有一种方法可以一劳永逸,何乐而不为呢?

    其实现便是利用.net的高级特性之一,反射

    首先我们定义三个生命周期接口,其对应依赖注入的三种生命周期

    复制代码
        //瞬时注入服务接口
        public interface ITransient
        { }
    
        //作用域注入服务接口
        public interface IScoped
        { }
    
        //单例注入服务接口
        public interface ISingleton
        { }
    复制代码

    然后我们定义自动注入的扩展方法,其为核心实现

    复制代码
            public static IServiceCollection RegisterAllServices(this IServiceCollection services)
            {
                //获取当前程序集
                var entryAssembly = Assembly.GetEntryAssembly();
    
                //获取所有类型
                //!. null包容运算符,当你明确知道表达式的值不为null 使用!.(即null包容运算符)可以告知编译器这是预期行为,不应发出警告
                //例: entryAssembly!.GetReferencedAssemblies() 正常
                //entryAssembly.GetReferencedAssemblies() 编译器判断entryAssembly有可能为null,变量下方出现绿色波浪线警告
    
                var types = entryAssembly!.GetReferencedAssemblies()//获取当前程序集所引用的外部程序集
                    .Select(Assembly.Load)//装载
                    .Concat(new List() { entryAssembly })//与本程序集合并
                    .SelectMany(x => x.GetTypes())//获取所有类
                    .Distinct();//排重
    
                //三种生命周期分别注册
                Register(types, services.AddTransient, services.AddTransient);
                Register(types, services.AddScoped, services.AddScoped);
                Register(types, services.AddSingleton, services.AddSingleton);
    
                return services;
            }
    
            /// 
            /// 根据服务标记的生命周期interface,不同生命周期注册到容器里面
            /// 
            /// 注册的生命周期
            /// 集合类型
            /// 委托:成对注册
            /// 委托:直接注册服务实现
            private static void Register(IEnumerable types, Func register, Func registerDirectly)
            {
                //找到所有标记了Tlifetime生命周期接口的实现类
                var tImplements = types.Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Any(tinterface => tinterface == typeof(TLifetime)));
    
                //遍历,挨个以其他所有接口为key,当前实现为value注册到容器中
                foreach (var t in tImplements)
                {
                    //获取除生命周期接口外的所有其他接口
                    var interfaces = t.GetInterfaces().Where(x => x != typeof(TLifetime));
                    if (interfaces.Any())
                    {
                        foreach (var i in interfaces)
                        {
                            register(i, t);
                        }
                    }
    
                    //有时需要直接注入实现类本身
                    registerDirectly(t);
                }
            }
    复制代码

    其核心逻辑便是通过反射扫描程序集,当扫描到实现了我们定义的生命周期接口时,为其实现对应的生命周期注入。

    注册这个服务

    builder.Services.RegisterAllServices();

    然后我们就可以通过继承生命周期接口来实现自动服务注入

        public interface ITestServices
        {
            int return123();
        }
    复制代码
        public class TestServices : ITestServices, ITransient
        {
            public int return123()
            {
                return 123;
            }
        }
    复制代码

    接下来无需在Program.cs注入服务实现

     调用成功。

    自动注入代码参考自:【NetCore】依赖注入的一些理解与分享 - wosperry - 博客园 (cnblogs.com)

  • 相关阅读:
    【附源码】计算机毕业设计SSM社区老人健康服务跟踪系统
    吃透这份“Java进阶核心手册”再战字节,直接把面试官按在地上摩擦
    Git的基本使用
    硅谷课堂笔记
    使用极狐GitLab的VS Code插件 GitLab Workflow 来进行项目的DevOps管理,助力研发效能
    Python 3 正则表达式
    【VIM】VIm初步使用
    医学主题词表(Medical Subject Headings, MeSH)
    Industrial resilience
    [Android N]MediaRecorder系列之StagefrightRecorder录制TS流flow
  • 原文地址:https://www.cnblogs.com/SaoJian/p/17462782.html