Blazor基础之依赖注入
分类: Blazor入门 ◆ 标签: #Asp.Net core基础 #.Net #Web #Web Client #Blazor ◆ 发布于: 2023-05-25 20:59:14

Blazor是基于ASP.net Core的框架来开发的,因此Blazor的依赖注入当然也是在ASP.net Core的依赖注入的基础上开发而来的,如果你想深入的了解依赖注入的基本理论,目前你可以在网上找一些专业点的资料来学习一下,之后我也会在重新学习ASP.net Core的框架的时候重新在回顾一下依赖注入的理论知识,不过如果你等不及,你可以先看一下ASP.net Core的依赖注入:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0
不过对于依赖注入有几个基本的知识点需要有概念:
依赖注入的使用范围:SingleTon, Scope, Transient. 即表明你注册的到容器中的类或者服务它的存活期,实例产生和Dispose的时间等等。
不同的场景如何注册自己需要的类实例或者服务实例。
我们之前已经学习过了,Blazor应用目前有两种主要的部署方式WebAssembly和Server, 这两种部署模式在对于依赖注入的支持和使用上有很多不同,同时基于Blazor应用的设计,和原生的ASP.net Core的依赖注入也是有一些不同的。
WebAssembly部署严格意义上来讲实际上没有依赖注入范围的概念的,所以如果在WebAssembly部署中将服务或者类以Scope的范围注册到WebAssembly应用中,实际上是会被当成Singleton的模式来使用的。
Blazor Server部署由于组件渲染是在服务端完成,因此Blazor Server是支持Scoped, Singleton, 以及Transient这几个注入范围的。不过由于Blazor Server部署模式主要是使用SignalR的协议和Host进行链接的,我们前面有讲到基于Blazor Server的部署中有一个概念叫Circurs(环路)的概念,如果对这个部分不是很理解的,您可以参考前面的这篇文章:
深入理解Blazor Server技术
由于我们的Blazor Server是基于SignalR协议保持链接,因此一般意义上的Scoped和Transient 这两种注入范围比起我们常见的基于ASP.net Core的注入范围的存活时间要长得多。在基于Razor Pages或者MVC框架中得注入范围,scoped是每次http request结束后就会被Dispose掉,transient每次使用完成之后就会被Dispose, 但是在Blazor Server部署中完成不是这样,Blazor Server只有在如下几种情况下才会将Scoped / transient范围得服务或者类disposed:
用户关闭浏览器的窗口,打开一个新窗口重新链接。
用户关闭浏览器的Tab, 打开新的tab 重新链接。
用户刷新浏览器页面。
Blazor框架中默认已经注册了如下的服务:
注意(Blazor Server没有注册HttpClient服务,WebAssembly中注册HttpClient服务和我们原有理解的不太一样,这个HttpClient服务是直接使用浏览器的功能进行访问。
服务名 | 注册范围 | 说明 |
HttpClient | Scoped | 只有WebAssembly注册该服务,并且虽然是scope, 但是使用singleton范围对待 |
IJSRuntime | Blazor Server是Scoped范围 Blazor Webassembly是singleton范围
| |
NavigationManager | Blazor Server是Scoped范围 Blazor Webassembly是singleton范围 |
如何将服务或者类注册到容器中
一般的基本原则是用接口进行注册,但是针对于WebAssembly的注册和基于Server的注册是不同的。
基于Webassembly的注册,是在Program.Main的进行注册, 如下图。
另外注册之后,还可以将这个Service再次取出,从而可以做一些服务的初始化工作,如下图:
同时需要注意host默认注册了一个配置的服务,可以用如下的形式来使用:
在Blazor Server部署模型注册服务或者是类,和ASP.net Core基本一致,也是从Startup.CS的ConfigureService中进行注册:
如何将注册的服务注入到需要的组件中
在组件中只需要使用指令@inject即可,如下图:
这是在组件中注入并使用自己需要的服务或者类,那么在你自己编写服务的时候,如果你需要其他的服务,该如何将已经注册的服务注入到自己的服务类中来呢?答案是通过构造函数进行注入,如下图:
Blazor组件针对于依赖注入专有的一些概念
前面我们讲过我们有哪些依赖注入的范围,由于Blazor是基于组件的系统,那么有没有一种依赖注入范围是随着组件的生命期而变化的依赖注入范围呢?例如说组件初始化到结束,这个服务就会被释放?答案是有的,我们可以称之为组件范围的依赖注入。
为了达到这个目的我们需要将组件从ComponentBase继承改到从OwningComponentBase类基础,如下图:
OwningComponentBase类支持泛型的,所以你可以从它的泛型版本进行继承,如下图:
OwningComponentBase这个类是从ComponentBase这个类继承来的,它有一个ScopedServices的属性,我们正是利用这个属性来达到我们的目的。
关于Transient 依赖注入范围
我们前面讲到由于Blazor应用的不同,Transient依赖注入范围比起普通的Transient范围要长很多,这样会带来一个问题,会不会在组件中无意中使用到已经Dispose掉的transient范围的组件?这个是很有可能的,为了规避这个问题,要么使用上述的方法,基于OwningComponentBase的子类来使用注册的服务或者类,要么采用如下微软提供的扩展方法探测已经Dispose的服务。如果怕麻烦直接使用owningcomponentbase的方法即可,下面的代码做一个参考,该代码的出处在这里:
https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-5.0&pivots=server#detect-transient-disposables
码截图如下:
将名称空间Microsoft.Extensions.DependencyInjection加入到Program.cs中去:
这样当你在组件使用了disposed了的tranisent组件,会爆出异常,并建议使用OwningComponentBase类,所以还是乖乖的使用这个方案吧。