Blazor University (49)依赖注入 —— 比较依赖范围

原文链接:https://blazor-university.com/dependency-injection/dependency-lifetimes-and-scopes/comparing-dependency-scopes/

比较依赖范围

源代码[1]

在本节中,我们将创建一个 Blazor 应用程序来演示各种依赖注入作用域的不同生命周期。

为此,我们将创建三个不同的服务(每个范围一个)。每个服务都将跟踪它的创建时间,以及一个递增的 InstanceNumber,以便我们可以跟踪已经创建了多少个该类型的实例。

首先,创建一个新的 Blazor 服务器端应用程序并添加一个静态类来跟踪应用程序启动的 DateTime,并计算自启动以来经过了多少时间。

public static class AppLifetime
{public static DateTime StartTimeUtc { get; } = DateTime.UtcNow;public static TimeSpan ElapsedTime => DateTime.UtcNow - StartTimeUtc;
}

服务接口

接下来,创建三个接口:IMyTransientService``、IMyScopedServiceIMySingletonService。每个接口都是相同的。

注意:有一些方法可以编写不需要重复的代码,但为了简单起见,此示例将重复代码。

public interface IMyTransientService
{public TimeSpan DeltaCreationTime { get; }public int InstanceNumber { get; }
}public interface IMyScopedService
{// As above
}public interface IMySingletonService
{// As above
}

服务实现

同样,这些服务的代码将被复制以避免复杂性。创建后,我们的服务将从 AppLifetime 类中获取 ElapsedTime,因此我们可以判断该实例是否是最近创建的。它还将从静态字段中为自己分配一个 InstanceId

public class MyTransientService : IMyTransientService
{public TimeSpan DeltaCreationTime { get; }public int InstanceNumber { get; }private static volatile int PreviousInstanceNumber;public MyTransientService(){DeltaCreationTime = DateTime.UtcNow - AppLifetime.StartTimeUtc;InstanceNumber = System.Threading.Interlocked.Increment(ref PreviousInstanceNumber);}
}public class MyScopedService : IMyScopedService
{//As above
}public class MySingletonService : IMySingletonService
{// As above
}

注册我们的服务

编辑 Startup.cs 文件,并在 ConfigureServices 方法中注册我们的服务,如下所示。

services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddScoped<IMyScopedService, MyScopedService>();
services.AddTransient<IMyTransientService, MyTransientService>();

用户界面

在我们的页面中,我们想要展示实例是如何被多个组件共享的。我们将通过创建一个使用我们所有三个服务的组件来简化这一点,然后让我们的组件的两个实例同时显示在我们的页面中。

4cd6a3fc9b3c7c441dcbb9ceeb6e5531.jpeg

创建我们的服务消费组件

首先我们需要注入我们的服务,然后我们将显示每个实例的 InstanceNumber 以及注入的服务是否是最近创建的。为此,如果服务是在最后 500 毫秒内创建的,我们将在 UI 中包含一个 CSS 类。

@inject IMySingletonService MySingletonService
@inject IMyScopedService MyScopedService
@inject IMyTransientService MyTransientService
<dl><dt>@Caption</dt><dd><ul><li><span class="scope-name">Singleton</span><span class="@GetNewIndicatorCss(MySingletonService.DeltaCreationTime)">Instance #@MySingletonService.InstanceNumber</span></li><li><span class="scope-name">Scoped</span><span class="@GetNewIndicatorCss(MyScopedService.DeltaCreationTime)">Instance #@MyScopedService.InstanceNumber</span></li><li><span class="scope-name">Transient</span><span class="@GetNewIndicatorCss(MyTransientService.DeltaCreationTime)">Instance #@MyTransientService.InstanceNumber</span></li></ul></dd>
</dl>@code
{[Parameter]public string Caption { get; set; }private string GetNewIndicatorCss(TimeSpan time){if (AppLifetime.ElapsedTime - time < TimeSpan.FromMilliseconds(500)){return "instance-info new-instance";}return "instance-info";}
}
  • 第 1-3 行

    指示 Blazor 将我们的服务实例注入此组件。

  • 第 10、14 和 18 行

    显示每个注入依赖项的 InstanceNumber,并通过调用 GetNewIndicatorCss 设置 HTML 元素的类属性。

  • 第 31 行

    如果服务的 DeltaCreationTime(应用程序启动和实例创建之间的时间)在 AppLifetime.ElapsedTime(应用程序启动和当前时间之间的时间)的 500 毫秒内,则组件将使用 CSS 类 instance-info new-instance 呈现,否则它只有 CSS 类实例信息。这将让我们以不同的方式显示新实例的 UI。

在页面上显示我们的组件

编辑 Pages/Index.razor 并更改标记如下

@page "/"
<MyStandardComponent Caption="Component 1" />
<MyStandardComponent Caption="Component 2" />

运行应用程序,您将看到以下输出。

906d6490181676f9737a912ebf31ba8a.jpeg

当我们的 Scoped Instance 应该是第一个实例时,它却是 #2,这可能会让人感到惊讶。这是因为在浏览器和服务器之间建立 SignalR 连接以实际启动用户会话之前,服务器端 Blazor 应用程序会预渲染我们的页面以发回完整的 HTML 响应。我们现在可以通过执行以下操作来禁用它。

  • 编辑 /Pages/_Host.cshtml

  • 找到文本 render-mode="ServerPrerendered"

  • ServerPrerendered 更改为 Server

现在重新运行应用程序会给我们预期的结果。

cf9f1f8513f3affd25316021afc284a7.jpeg

Singleton Instance 将始终为 #1,因为它由所有用户共享。Scoped Instance 将是应用程序的第一个用户的 #1,第二个用户的 #2,依此类推。Transient Instance 对于第一个组件将是 #1,对于第二个组件将是 #2,因为它们是为每个组件创建的。如果用户离开页面然后返回,唯一会改变的实例编号是 Transient 实例,它将递增到 #3#4,并且在下次访问页面时将递增到 #5#6

交互式示例

为了使生命周期更加明显,我们将修改我们的 Index.razor 页面,以便它有条件地呈现我们的组件。我们还将展示实际的页面刷新如何影响我们的范围服务。

我们将通过以下步骤创建一个简单的向导式 UI

  1. 网站启动

  2. 更新的 UI – 重新创建了组件

  3. 在浏览器中重新加载页面

  4. 更新的 UI – 重新创建了组件

确保在每一步都重新创建组件

为了确保我们的组件在每个导航中创建,我们将有一个 CurrentStep 字段并根据 CurrentStep 是奇数还是偶数来显示一个或另一组组件。

@if (CurrentStep % 2 == 1)
{<MyStandardComponent Caption="Component 1" /><MyStandardComponent Caption="Component 2" />
}
else
{<MyStandardComponent Caption="Component 1" /><MyStandardComponent Caption="Component 2" />
}

强制在浏览器中刷新页面

要强制刷新页面,我们将使用 NavigationManager 进行导航,并将 true 传递给 forceLoad 参数。为了让我们知道在页面刷新后我们正在继续,我们将导航到 /continue。此请求将由同一页面提供服务,但我们知道我们应该从第 3 步而不是第 1 步开始。

@page "/"
@page "/{Continue}"
@inject NavigationManager NavigationManager@code
{[Parameter]public string Continue { get; set; }private void GoToNextStep(){CurrentStep++;if (CurrentStep == 3)NavigationManager.NavigateTo("/continue", forceLoad: true);}protected override void OnInitialized(){base.OnInitialized();if (!string.IsNullOrWhiteSpace(Continue))CurrentStep = 3;}
}

完成示例

以下(已完成)代码基本上是到目前为止所概述的内容,并添加了以下内容。

  1. 添加了显示当前步骤名称的文本。

  2. 添加了一个按钮以单击以进行下一步。

  3. 添加了在没有下一步时禁用按钮的代码。

  4. 添加了一些 CSS 样式,以使新实例通过脉冲来吸引我们的注意力。

@page "/"
@page "/{Continue}"
@inject NavigationManager NavigationManager<h1>Step @CurrentStep: @CurrentStepName</h1>
@if (CurrentStep % 2 == 1)
{<MyStandardComponent Caption="Component 1" /><MyStandardComponent Caption="Component 2" />
}
else
{<MyStandardComponent Caption="Component 1" /><MyStandardComponent Caption="Component 2" />
}
<button @onclick=GoToNextStep disabled=@IsButtonDisabled>Next step</button><style>.scope-name {width: 5rem;display: inline-block;font-weight: bold;}.instance-info {color: white;background-color: #888;padding: 0 4px;margin: 2px;display: inline-block;}.instance-info.new-instance {background-color: #3f8f42;animation: flash-green 2s;}@@keyframes flash-green {from {background-color: #4cff00;}to {background-color: #3f8f42;}}
</style>@code
{[Parameter]public string Continue { get; set; }private int CurrentStep = 1;private string CurrentStepName => StepNames[CurrentStep - 1];private bool IsButtonDisabled => CurrentStep >= StepNames.Length;private string[] StepNames = new string[]{"Website started","Updated UI - Components recreated","Reloaded page in browser","Updated UI - Components recreated"};protected override void OnInitialized(){base.OnInitialized();if (!string.IsNullOrWhiteSpace(Continue))CurrentStep = 3;}private void GoToNextStep(){CurrentStep++;if (CurrentStep == 3)NavigationManager.NavigateTo("/continue", forceLoad: true);}
}
  • 第 6 行

    根据 CurrentStep 是奇数还是偶数,渲染一组或另一组组件实例。这可确保为向导的每个步骤重新创建生成 UI 的组件。

  • 第 16 行

    转到向导的下一步(如果有)。

  • 第 69 行

    如果 Continue route 参数不为 null,则从步骤 3 继续。

  • 第 76 行

    如果单击下一步按钮并在第 3 步结束,则强制重新加载页面。

运行应用程序

当我们的网站第一次运行时,我们获得了所有注入依赖项的第一个实例。除了 Transient 依赖,因为它们是按需创建的,而不是缓存起来以供重用,所以我们得到了实例 #1#2

40194c5152298faf1ddb55bae569ab52.jpeg

当用户单击 Next step 按钮时,CurrentStep 增加到 2,我们的第一对组件被丢弃,而第二对组件被创建用于渲染。因为这些是在同一个用户会话中运行的新实例,所以它们将收到相同的 Scoped 依赖项以及两个新的 Transient 依赖项。

34cbcb1e85d11ddffbd23d5c2f8a47c4.jpeg

当用户再次单击 Next step 按钮时,应用程序将强制在新路径 /continue 处重新加载应用程序。由于在重新加载页面时忘记了 SignalR 连接的 ID,因此将为用户设置一个新连接,从而设置一个新范围。所以现在当前几个组件被渲染时,它们是 Scoped 实例 #2。

4c07d0f551cd7d14e46022fe88a6cd5f.jpeg

最后,当用户最后一次单击 Next step 按钮时,将创建第二对组件以进行渲染,并将注入由共享 Singleton 容器(实例#1)中缓存的实例缓存的 IMySingletonService,与当前用户的注入容器(实例 #2)缓存的 IMyScopedService,以及 IMyTransientService 的两个新实例。

c6a94b5ed43f04b20724863bf7da5db7.jpeg

WebAssembly 依赖范围

源代码[2]

因为 WebAssembly 在用户的浏览器中运行,并且每个选项卡都是一个完全独立的进程,所以我们的输出将与服务器端 Blazor 应用程序产生的略有不同。

首先,应用程序在浏览器选项卡中启动,我们得到了我们期望在服务器端 Blazor 应用程序上看到的相同输出。

d9b19e1a5350fdcd7070c087e8451969.jpeg

第一次单击 Next step 也向我们展示了一个与我们的服务器端 Blazor 应用程序相同的屏幕,其中 SingletonScoped 实例保持不变,因为它们都是缓存实例,并且按需创建了两个 Transient 实例。

0fbe60093f619cf612d95c53f2d5b6cb.jpeg

当我们的应用程序执行强制重新加载时,情况就不同了。在服务器端应用程序中,用户获得一个新的 SignalR 连接 ID,因此在与该 ID 绑定的服务器上获得一个新的依赖注入容器。在 WebAssembly 中,页面没有可重新连接的应用程序状态。一旦页面重新加载,整个应用程序状态就会被销毁,然后重新创建。结果,我们的实例数从头开始。

7b9420a39a26c5132ea02599ce0a2c37.jpeg

然后最后。

dd00d4243bdeb7ef79b494c7c77d9ff5.jpeg

结论

由于用户界面和 UI 逻辑在 Blazor 应用程序中绑定在一起,因此没有每个请求的依赖注入范围。

Singleton 注册的依赖项在服务器端应用程序中的用户之间共享,但在 WebAssembly 应用程序中每个浏览器选项卡都是唯一的。

Scoped 依赖的行为与 Singleton 注册的依赖几乎相同,除了它们与其他用户/其他浏览器选项卡隔离。

Transient 依赖在服务器端和 WebAssembly 上的工作方式相同,并且与 ASP.NET MVC 中的工作方式相同——除了依赖注入容器在 ASP.NET MVC 中的页面请求之后被释放。请参阅 Transient 依赖项的避免内存泄漏[3]部分。

有一些方法可以为每个用户引入额外的范围。这种技术将在后面的部分中介绍。

参考资料

[1]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/DependencyInjection/ServerDependencyScopes

[2]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/DependencyInjection/WebAssemblyDependencyScopes

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS选择器的权重与优先规

我们把特殊性分为4个等级&#xff0c;每个等级代表一类选择器&#xff0c;每个等级的值为其所代表的选择器的个数乘以这一等级的权值&#xff0c;最后把所有等级的值相加得出选择器的特殊值。 4个等级的定义如下&#xff1a; 第一等&#xff1a;代表内联样式&#xff0c;如: st…

设计模式概论

此文转载于 http://blog.csdn.net/hguisu/article/details/74968191. 设计模式设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性…

只需要2个工具,百度云盘大文件就能用迅雷和IDM下载

不会代码&#xff0c;不懂脚本&#xff0c;没关系 &#xff0c;能找到一座通往它们的桥梁&#xff0c;照样能到达彼岸。 这里以360极速浏览器为例。 在浏览器地址框输入以下地址直接到达浏览器安装扩展插件的地方&#xff08;偷个懒&#xff0c;复制网址吧&#xff09;&#xf…

rsync服务器的配置

一、rsync 简介Rsync&#xff08;remote synchronize&#xff09;是一个远程数据同步工具&#xff0c;可通过LAN/WAN快速同步多台主机间的文件&#xff0c;也可以使用 Rsync 同步本地硬盘中的不同目录。 Rsync 是用于取代rcp的一个工具&#xff0c;Rsync使用所谓的 “Rsync 算法…

用ajax连接mysql_页面用ajax实现简单的连接数据库

(1) 写发送代码var myXmlHttpRequest "";myXmlHttpRequest getXmlHttpRequest();if (myXmlHttpRequest) { //xmlHttpRequest创建成功了&#xff0c;才能发送请求//地址一定要写正确var url "../zhuCe/zhuCeYanZheng.aspx?username" $(Text1).value;m…

Vue学习笔记入门篇——数据及DOM

本文为转载&#xff0c;原文&#xff1a;Vue学习笔记入门篇——数据及DOM 数据 data 类型 Object | Function 详细 Vue 实例的数据对象。Vue 将会递归将 data 的属性转换为 getter/setter&#xff0c;从而让 data 的属性能够响应数据变化。对象必须是纯粹的对象(含有零个或多个…

Thinkphp 3.2中控制页面不缓存

最近开发WAP网站时&#xff0c;最讨厌的就是back键&#xff0c;会造成些麻烦事。不过&#xff0c;问题总有办法解决。 有些页面&#xff0c;点击back键回退会加载缓存&#xff0c;这不是想要的&#xff0c;所以希望能够控制该页面不缓存&#xff0c;每次请求都需要从服务器获取…

能识别nvme的pe启动_PE系统纯净(可以识别nvme固态)

此前我一直用的微PE系统&#xff0c;纯净没有广告&#xff0c;但是我却发现不能识别nvme固态&#xff0c;于是就寻找可以识别nvme固态的PE&#xff0c;虽然知道老毛桃可以用&#xff0c;但是我并不想用。然后网上查到可以自动向PE系统中添加nvme的驱动&#xff0c;从而就可以识…

H3CNE认证

H3CNE&#xff08;H3C Certified Network Engineer&#xff0c;H3C认证网络工程师&#xff09;H3CNE认证主要定位于中小型网络的规划、设计、配置与维护。通过H3CNE认证&#xff0c;将证明您对数据通信网络有全面深入的了解&#xff0c;掌握面向中小型企业的网络通用技术&#…

BZOJ 3144 [Hnoi2013]切糕

3144: [Hnoi2013]切糕 Description Input 第一行是三个正整数P,Q,R&#xff0c;表示切糕的长P、 宽Q、高R。第二行有一个非负整数D&#xff0c;表示光滑性要求。接下来是R个P行Q列的矩阵&#xff0c;第z个 矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R)。 100%的数据…

java java 大端_Java 大小端转换

package nlp.nlp;/*** 小端数据&#xff0c;Byte转换**/public class ByteConvert {public static void main(String[] args) {ByteConvert c new ByteConvert();c.Int2Bytes_LE(126);}public static final int UNICODE_LEN 2;/*** int转换为小端byte[](高位放在高地址中)* p…

《ASP.NET Core 6框架揭秘》实例演示[18]:HttpClient处理管道

在《《ASP.NET Core 6框架揭秘》实例演示[17]&#xff1a;利用IHttpClientFactory工厂来创建HttpClient》之后&#xff0c;我们将关注点放到HttpClient对象上。我们知道ASP.NET的核心就是由中间件组成的请求处理管道&#xff0c;HttpClient也采用了类似的设计。HttpClient管道由…

腾讯云副总裁答治茜:移动互联网破局要借助“三张网”

5月24日&#xff0c;2018腾讯云未来峰会在广州召开。在互联网专场上&#xff0c;腾讯云副总裁答治茜就泛互联网行业云化的主题发表演讲。在演讲中答治茜表示&#xff0c;过去移动互联网的高速增长到现在已经遇到了一个天花板&#xff0c;需要借助马化腾提到的“人联网、物联网、…

雅诗兰黛天猫超级品牌日:未央唇膏、红装小棕瓶“当红不让”

随着年末圣诞季的临近&#xff0c;各大美妆品牌陆续推出了圣诞套装&#xff0c;红红火火的超豪华套装&#xff0c;算是对用户最实在的回馈。高端美妆品牌的“领头羊”雅诗兰黛&#xff0c;当然也“当红不让”&#xff0c;趁着圣诞季&#xff0c;与天猫超级品牌日联手打造了一场…

JAVA常见算法题(三十一)---冒泡排序

package com.jege.spring.boot.hello.world;/*** java算法之冒泡排序<br>* 将数组按照从大到小的顺序排列<br>* * * author Administrator**/ public class BubbleSort{public static void main(String[] args){int score[] {67, 69, 75, 87, 89, 90, 99, 100};fo…

java 1.7的新特性_[Java]  JDK 1.7版本的 新特性

在网上看到一些jdk1.7的新特性&#xff0c;现将我觉得比较实用的记录于下&#xff1a;(1)switch中可以使用字串了String s "test";switch (s) {case "test" :System.out.println("test");case "test1" :System.out.println("tes…

WPF实现物理效果 拉一个小球

原文:WPF实现物理效果 拉一个小球一直以来都对物理效果有神秘感,完全不知道怎么实现的.直到看到了周银辉在老早前写的一篇博客:http://www.cnblogs.com/zhouyinhui/archive/2007/06/23/793724.html 终于知道是怎么实现的了. CompositionTarget类的Rendering事件.在每一帧成功渲…

C# CM框架下一行代码实现多页面管理

概述之前我分享过一个wpf的项目实践&#xff0c;主页面左侧是个listbox&#xff0c;每次选择改变后呈现对应的页面&#xff0c;界面图如下&#xff1a;要实现这样一个功能&#xff0c;我之前是采用传统方式实现的&#xff0c;本节我采用CM框架下的Conductor<T>去实现&…

如何用DW设计界面 结合 VS设计后台代码

原文发布时间为&#xff1a;2008-11-02 —— 来源于本人的百度文章 [由搬家工具导入]问&#xff1a;在vs.net里有form标记&#xff0c;而dw里却没有&#xff0c;两个里面的标记代码都不一样&#xff0c;怎么能通用&#xff1f; 在.net里修改dw的文件&#xff0c;或在dw里修改a…

java中instanceof 详解_java中的instanceof用法详解

instanceof是Java的一个二元操作符(运算符),也是Java的保留关键字。它的作用是判断其左边对象是否为其右边类的实例&#xff0c;返回的是boolean类型的数据。用它来判断某个对象是否是某个Class类的实例。用法&#xff1a;boolean result object instanceof class参数&#xf…