asp.netcore依赖注入高级玩法——如何注入多个服务实现类(代码片段)

老周 老周     2022-11-01     506

关键词:

依赖注入在 ASP.NET Core 中起中很重要的作用,也是一种高大上的编程思想,它的总体原则就是:俺要啥,你就给俺送啥过来。服务类型的实例转由容器自动管理,无需我们在代码中显式处理。

因此,有了依赖注入后,你的编程思维就得变一变了。在过去,许多功能性的类型(比如一个加密解密的类),我们都喜欢将其定义为静态(static),而有了依赖注入,你就要避免使用静态类型,应该交由服务容器帮你管理,只要你用好了,你会发现依赖注入是很方便的。

依赖注入的初级玩法,也是比较标准的玩法,此种玩法有两种模式:

1、十代单传模式:一个接口对应一个类,比如先定义接口 IA、IB,随后,类A实现 IA,类B 实现 IB。一对一。也可以是抽象类(或基类)E,然后 F 继承 E 类。

2、断子绝孙模式:直接就写一个类,不考虑派生,直接就添加到服务容器中。

 

来,看个例子。

我先定义个接口。

    public interface IPlayGame
    
        void Play();
    

然后,写一个类来实现它。

    public class NBPlayGame : IPlayGame
    
        public void Play()
        
            Console.WriteLine("全民打麻药。");
        
    

 

我们知道,所谓服务类,其实就是普通类,这些类一般用于完成某些功能,比如计算 MD5 值。接着呢,还记得 Startup 类有个 ConfigureServices 方法吧,对,就在这厮里面把我们刚刚那个服务进行注册(就是添加到 ServiceCollection 集合中)。

        public void ConfigureServices(IServiceCollection services)
        
            services.AddTransient<IPlayGame, NBPlayGame>();
        

添加的时候很简单,类型一对一,IPlayGame 接口与 NBPlayGame 类对应。添加时有三种方法你可以调用,实际上对应着,服务类在容器中的生命周期。

AddSingleton:单个实例,这是寿命最长的,与天同寿。整个应用程序中仅用一个实例。

AddTransient:这个是最短命的,可能是天天晚上加班熬夜,死得很快。此种情况下,服务类的实例是用的时候创建,用完后直接销毁。

AddScoped:这个比较难理解。它的生命周期在单个请求内,包括客户端与服务器之间随后产生的子请求,反正只要请求的会话结束了,就会清理。

 

然后,你就可以进行注入了,比如在中间件,在控制器,或者在其他服务类的构造函数上(中间件是在 Invoke / InvokeAsync 方法上)进行实例接收。

现在来用一下,写一个中间件。

    public class TestMiddleware
    
        public TestMiddleware(RequestDelegate next)  

        public Task InvokeAsync(HttpContext context, IPlayGame game)
        
            game.Play();
            return Task.CompletedTask;
        
    

已注册的服务会注入到 InvokeAsync 方法的参数中。注意第一个参数是 HttpContext,这是必须参数,后面的是注入的参数。

最后,在 Startup 类的 Configure 方法中就可以 use 这个中间件了。

        public void Configure(IApplicationBuilder app)
        
            app.UseMiddleware<TestMiddleware>();
        

 

运行后,Play 方法调用,在控制台中输出以下结果。

 

 “断子绝孙”模式,不使用接口规范,直接写功能类。

    public class DoSomething
    
        public string GetMessage() => "你好,刚才 Boss 找你。";
    

注册服务时更简单。

        public void ConfigureServices(IServiceCollection services)
        
            services.AddScoped<DoSomething>();
        

在 Configure 方法中进行注入。

        public void Configure(IApplicationBuilder app, DoSomething thing)
        
            Console.WriteLine(thing.GetMessage());
        

运行后,输出结果如下。

 

 在容器中,使用 ServiceDescriptor 类来存储服务类型相关的信息。其中,ServiceType 表示的是服务的类型,如果服务是有接口与实现类的,那么这个属性指的是接口的类型,实现类的类型信息由 ImplementationType 属性存储。如果没有接口,直接只定义类型,那么这个类型的信息就存到 ServiceType 属性上,ImplementationType 属性不使用。

上面这些例子中,ServiceType 是 IPlayGame 接口相关信息,ImplementationType 是 NBPlayGame 类的信息。如果像上面 DoSomething 类的情况,则 ServiceType 为 DoSomething 相关的信息,ImplementationType 为空。

 

 

接下来,咱们看高级玩法。

定义一个接口。

    public interface IDemoService
    
        string Version  get; 
        void Run();
    

然后,有两个类实现这个接口。

    public class DemoService1 : IDemoService
    
        public string Version => "v1";

        public void Run()
        
            Console.WriteLine("第一个服务实现类。");
        
    

    public class DemoService2 : IDemoService
    
        public string Version => "v2";

        public void Run()
        
            Console.WriteLine("第二个服务实现类。");
        
    

 

然后,我们注册服务。

        public void ConfigureServices(IServiceCollection services)
        
            services.AddTransient<IDemoService, DemoService1>();
            services.AddTransient<IDemoService, DemoService2>();
        

然后我们照例,接收注入,咱们依旧使用中间件的方法参数接收。

    public class DemoMiddleware
    
        public DemoMiddleware(RequestDelegate next)
        
            // 由于程序约定,此构造函数必须提供。
        

        public async Task InvokeAsync(HttpContext context, IDemoService sv)
        
            await context.Response.WriteAsync(sv.Version);
        
    

然后,在 Startup.Configure 方法中使用该中间件。

        public void Configure(IApplicationBuilder app, DoSomething thing)
        
            app.UseMiddleware<DemoMiddleware>();
        

运行之后,你发现问题了,看看输出。

 

 出事了,参数仅能接收到最后注册的实现类型实例,也就是 DemoService2 类。所以就看到网上有不少朋友发贴问了,.NET Core 是不是不支持多个服务实现类的注入?这难倒了很多人。

实话告诉你,Core Core 兄是支持注入多个实现类的实例的。

下面,老周介绍两种解决方法(其实有三种,还有一种不太好弄,尤其是你对 Core 兄不熟的时候,所以我说两种,基本够用)。

方法一、接收 IServiceProvider 类型的注入。

        public async Task InvokeAsync(HttpContext context, IServiceProvider provider)
        
            StringBuilder sb = new StringBuilder();
            foreach (var sv in provider.GetServices<IDemoService>())
            
                sb.Append($"sv.Version<br/>");
            
            await context.Response.WriteAsync(sb.ToString());
        

只要能接收到 IServiceProvider 所引用的实例,就能通过 GetServices 方法获取多个服务实例。

 

方法二,这种方法老周很推荐,更简单,直接注入 IEnumerable<T> 类型,本例中就是 IEnumerable<IDemoService>。

        public async Task InvokeAsync(HttpContext context, IEnumerable<IDemoService> svs)
        
            StringBuilder sb = new StringBuilder();
            foreach (var sv in svs)
            
                sb.Append($"sv.Version<br/>");
            
            await context.Response.WriteAsync(sb.ToString());
        

IEnumerable<T> 的妙处就是可以 foreach ,这样你也能访问多个实例,而且必要时还可以联合 LINQ 一起耍。

运行结果如下。

 

 

不要问我是怎么发现的,反正我告诉你了,你用就是了。

好了,今天的话题就到这儿了,3166。

 

历数依赖注入的n种玩法

历数依赖注入的N种玩法在对ASP.NETCore管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程层面。在ASP.NETCore应用中基于依赖注入的编程主要涉及到两个方面,它... 查看详情

asp.netcore依赖注入基本用法(代码片段)

ASP.NETCore依赖注入ASP.NETCore从框架层对依赖注入提供支持。也就是说,如果你不了解依赖注入,将很难适应ASP.NETCore的开发模式。本文将介绍依赖注入的基本概念,并结合代码演示如何在ASP.NETCore中使用依赖注入。什么是依赖注入... 查看详情

asp.netcore依赖注入(di)(代码片段)

原文:ASP.NETCore依赖注入(DI)  ASP.NETCore的底层设计支持和使用依赖注入。ASP.NETCore应用程序可以利用内置的框架服务将服务注入到启动类的方法中,并且应用程序服务也可以配置注入。由ASP.NETCore提供的默认服务容器提供了最小... 查看详情

asp.netcore依赖注入最佳实践——提示与技巧(代码片段)

在这篇文章,我将分享一些在ASP.NETCore程序中使用依赖注入的个人经验和建议。这些原则背后的动机如下:高效地设计服务和它们的依赖。预防多线程问题。预防内存泄漏。预防潜在的BUG。这篇文章假设你已经基本熟悉依赖注入... 查看详情

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

】ASP.NetCore使用啥依赖注入框架?【英文标题】:WhatDependencyInjectionFrameworkDoesASP.NetCoreUse?ASP.NetCore使用什么依赖注入框架?【发布时间】:2017-08-3119:27:30【问题描述】:我开始涉足ASP.NetCore,发现依赖注入是ASP.NetCore框架中的一等... 查看详情

如何在 ASP.NET CORE 中使用具有依赖注入的动作过滤器?

】如何在ASP.NETCORE中使用具有依赖注入的动作过滤器?【英文标题】:HowtouseActionFilterswithDependencyInjectioninASP.NETCORE?【发布时间】:2017-01-0810:35:47【问题描述】:我在我的ASP.NETCORE应用程序中到处使用基于构造函数的依赖注入,我... 查看详情

asp.netcore依赖注入问题

   最近.netcore可以跨平台了,这是一个伟大的事情,为了可以赶上两年以后的跨平台部署大潮,我也加入到了学习之列。今天研究的是依赖注入,但是我发现一个问题,困扰我很久,现在我贴出来,希望可以有人帮忙... 查看详情

[asp.netcore3框架揭秘]依赖注入:一个mini版的依赖注入框架(代码片段)

...论的角度对依赖注入进行了深入论述,我们接下来会对.NETCore依赖注入框架进行单独介绍。为了让读者朋友能够更好地理解.NETCore依赖注入框架的设计与实现,我们按照类似的原理创建了一个简易版本的依赖注入框架,也就是我... 查看详情

asp.netcore自定义依赖注入容器,替换自带容器

依赖注入    在asp.netcore程序中,众所周知,依赖注入基本上贯穿了整个项目,以通用的结构来讲解,控制器层(Controller层)依赖业务层(Service层),业务层依赖于仓储层(Repository层),而其他层级中也或多或少的使用了依赖... 查看详情

《asp.netcore6框架揭秘》实例演示[04]:自定义依赖注入框架

ASP.NETCore框架建立在一个依赖注入框架之上,已注入的方式消费服务已经成为了ASP.NETCore基本的编程模式。为了使读者能够更好地理解原生的注入框架框架,我按照类似的设计创建了一个简易版本的依赖注入框架,并... 查看详情

《asp.netcore3框架揭秘》博文汇总

在过去一段时间内,写了一系列关于ASP.NETCore3相关的文章,其中绝大部分来源于即将出版的《ASP.NETCore3框架揭秘》(博文只能算是“初稿”,与书中相应章节具有一定差异),先将它们汇总在这里。如果对《ASP.NETCore3框架揭秘》... 查看详情

asp.netcore使用autofac依赖注入(代码片段)

 实现代码1、新建接口类:IRepository.cs,规范各个操作类的都有那些方法,方便管理。usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Linq.Expressions;usingSystem.Text;namespaceCMS.Entity.Interfacespublicint 查看详情

netcore,你必须了解无处不在的“依赖注入”

NETCore,你必须了解无处不在的“依赖注入”ASP.NETCore的核心是通过一个Server和若干注册的Middleware构成的管道,不论是管道自身的构建,还是Server和Middleware自身的实现,以及构建在这个管道的应用,都需要相应的服务提供... 查看详情

asp.netcore依赖注入和管道方式的异常处理及日志记录(代码片段)

...常信息。搭建框架????首先,创建一个WebApi项目,选择Asp.NetCoreW 查看详情

2.1依赖注入

...esolver附加的IShouldInitialize接口ASP.NETMVC和ASP.NETWebAPI集成ASP.NETCore 查看详情

使用 .NET Core 进行 Hangfire 依赖注入

】使用.NETCore进行Hangfire依赖注入【英文标题】:Hangfiredependencyinjectionwith.NETCore【发布时间】:2017-06-0908:36:12【问题描述】:如何在Hangfire中使用.NETCore的默认依赖注入?我是Hangfire的新手,正在寻找一个适用于ASP.NETCore的示例。【... 查看详情

用工厂模式解决asp.netcore中依赖注入的一个烦恼

这是最近在实际开发中遇到的一个问题,用asp.netcore开发一个后端webapi,根据指定的key清除2台memcached服务器上的缓存。背景是我们在进行.netcore迁移工作,asp.net项目与asp.netcore项目并存,为了避免两种类型项目的缓存冲突,我们... 查看详情

抽象类中的依赖注入,ASP.NET Core

】抽象类中的依赖注入,ASP.NETCore【英文标题】:DependencyInjectioninabstractclass,ASP.NETCore【发布时间】:2018-09-1303:10:27【问题描述】:在我的ASP.NETCore2项目中,我创建了继承RazorPage类的新类,以在视图中添加一些额外的属性。publicabs... 查看详情