第二章 基础知识(3) (基础知识点)

第二章 基础知识(3) 依赖注入是一种设计模式,在类及其依赖关系之间实现控制反转(IOC)的技术。如果想要彻底理解依赖注入的设计模式,则先要清楚如下几个概念:
依赖倒置

依赖注入是一种在类及其依赖项之间实现控制反转(IOC) 的设计模式。如果想要完全理解依赖注入设计模式,首先必须理解以下概念:

依赖反转原理(简称DIP控制反转)、IOC依赖注入(简称DIP控制反转)容器(简称IOC容器)

现在,依赖注入设计模式已添加到Blazor Web App 项目中,依赖注入发生在整个框架的各处。框架本身有许多内置的组件服务可供开发人员使用,包括配置组件、日志记录组件和可选组件。您还可以注册自己定义的服务。

在Blazor 项目中,默认情况下以编程方式完成服务注册。

服务的注册与注入

一、服务的生存期限

在Blazor Web App 项目中,服务注册是通过IServiceCollection 接口执行的。该接口提供了三种主要的注册方法:AddScoped、AddSingleton、AddTransient。这三种方法对应着一个服务的三个生命周期。

有范围

作用域:当请求发生时创建一个新的服务实例。这也称为范围服务,因为它仅在此请求内有效。

请注意,Blazor 的客户端(Blazor WebAssembly) 没有范围的概念,并且注册为Scoped 的服务的行为类似于单例。由于与服务器的通信是通过用户线路上的SignalR 连接而不是HTTP 请求进行的,因此在客户端上的组件之间移动时不会重建作用域服务。因此,只有当出现以下情况时,客户端才会重建作用域服务:

用户关闭浏览器窗口,打开一个新窗口,然后返回到应用程序。用户在浏览器窗口中关闭了应用程序的一个选项卡,打开了一个新选项卡,然后返回到应用程序。用户选择浏览器的重新加载/刷新按钮。

单例

Singleton:服务的单个实例。从项目内的IOC容器获取的服务是同一个实例。

瞬态现象

Transient:每次组件从服务容器获取Transient 服务的实例时,它都会收到该服务的新实例。

二、注册服务

与任何其他IOC 容器一样,Blazor 中的服务注册是使用builder.Services 对象(IServiceCollection) 以编程方式注册的。

Microsoft.Extensions.DependencyInjection 用于Blazor 中的依赖注入,但这里不讨论其具体用法。

对于新服务注册,服务器(Blazor Server) 和客户端(Blazor WebAssembly) 是相同的。

程序.cs

.

builder.Services.AddSingletonIMyService, MyService();

.

在Blazor Web App Auto 项目中,如果使用客户端渲染并支持预渲染的组件想要通过注入使用IOC 容器中的服务,则它必须在服务器和客户端上注册该服务。此服务不可用于预渲染。因此,如果客户端和服务端需要注册多个相同的服务,建议将注册方法提取出来封装在静态方法中。

静态方法

公共静态无效ConfigureCommonServices(IServiceCollection服务)

{

服务.添加.

}

客户端程序.cs

var builder=WebAssemblyHostBuilder.CreateDefault(args);

.

配置CommonServices(builder.Services);

服务器程序.cs

var builder=WebApplication.CreateBuilder(args);

.

Client.Program.ConfigureCommonServices(builder.Services);

三、注入服务

一旦您的服务成功注册,您就可以将其注入到您的组件或服务类中。无需解释如何在服务类中使用注入。可以通过构造函数注入直接使用。组件的依赖服务。

成分

组件必须通过@inject注入服务。对于组件隐藏类(C# 内部类),请使用[Inject] 属性。

成分

@Page“/the-sunmakers”

@inject IMyService MyService

.

组件隐藏类

公共部分类Sunmakers

{

[Inject] //[Inject(Key=\’my-service\’)] 可以指定一个命名服务

受保护的IMyService MyService { 设置}=默认!

}

组件基类和继承

另外,对于组件内部类(IComponent的子类),还可以使用[Inject]属性来注入与服务类型属性对应的服务实例。

一般情况下,只有当组件需要继承基类,并且基类还需要注入服务时,才会使用该功能。

组件的基类

使用Microsoft.AspNetCore.Components。

公共类ComponentBase : IComponent

{

[Inject] //[Inject(Key=\’my-service\’)] 可以指定一个命名服务

protected IDataAccess DataRepository { get }=默认!

.

}

组件子类

@page \’/demo\’

@inherits ComponentBase //暂时使用@inherits

h1演示组件/h1

四、组件共存亡的Scoped服务

服务器端开发支持跨HTTP 请求的范围生命周期,但不支持跨客户端加载的组件之间的SignalR 连接/有线消息。 在页面或视图之间导航,或者从页面或视图导航到组件时,应用程序的Razor Pages 或MVC 部分照常处理作用域服务,为每个HTTP 请求重新创建服务。

在服务器上,对于HTTP 请求,注入的范围服务照常在请求开始时创建,并在请求结束时销毁。在服务器端,对于在服务器上交互式呈现的组件,注入的作用域服务与连接相关联,并且当您在页面内移动和切换组件时,其SignalR 具有的组件将被收回。插入的作用域服务的生命周期比预期更长,因为它在一定时间内没有断开连接。

例如,在服务器项目中创建MyTime 类型,在Program.cs 中将其注册为作用域服务,然后在Pages 文件夹中创建新的ScopeServer 组件。

我的时间.cs

公开课范围测试

{

公共DateTime CreateTime { set }=DateTime.Now;

}

程序.cs

.

builder.Services.AddScopedMyTime();

.

ScopeServer.razor

@page“/范围服务器”

@rendermodeInteractiveServer

@注入MyTime_myTime

p

@_myTime.CreateTime

/p

然后,如果您启动该服务,访问ScopeServer 组件,从ScopeServer 组件导航到其他组件页面,然后每隔一段时间回来,您将看到上述时间没有改变。也就是说,始终使用相同的实例。

如果要将作用域服务生命周期与组件生命周期同步,可以使用两种方法。继承OwningComponentBase 类型并使用其ScopedServices.GetRequiredServiceTService() 方法创建服务,或者直接继承OwningComponentBaseTService 类型并使用该类型。服务属性。

OwningComponentBaseTService 是OwningComponentBase 的子类,因此您还可以使用ScopedServices.GetRequiredServiceT() 方法。

继承自OwningComponentBase

ScopeServer.razor

@page“/范围服务器”

@rendermodeInteractiveServer

@inheritsOwningComponentBase

p

@_myTime.CreateTime

/p

@代码{

私人我的时间?

受保护的重写void OnInitialized()

{

_myTime=ScopedServices.GetRequiredServiceMyTime();

}

}

继承自OwningComponentBaseTService

ScopeServer.razor

@page“/范围服务器”

@rendermodeInteractiveServer

@inheritsOwningComponentBaseMyTime

p

@Service.CreateTime

/p

客户项目

对于客户项目,没有范围服务的概念。如果你希望它和组件的生命周期一样,那么处理方法就和服务器一样。

默认服务

一、HttpClient

HttpClient 服务提供发送HTTP 请求和从URI 标识的资源接收HTTP 响应的方法。

1、服务端使用

在服务器端项目中,HttpClient服务默认不会自动注册,因此如果需要在服务器端使用它,则必须使用Program.cs中的AddHttpClient()自行注册。

请注意,注册的HttpClient 提供了一个范围而不是单个实例。

基本用法

如果运行HttpClient,则可以直接使用AddHttpClient() 来注册IHttpClientFactory,无需任何配置。

注册程序.cs

var builder=WebApplication.CreateBuilder(args);

builder.Services.AddRazorComponents()

.AddInteractiveServerComponents()

.AddInteractiveWebAssemblyComponents();

//注册HttpClient服务

builder.Services.AddHttpClient();

var app=builder.Build();

.

如果需要在组件中使用HttpClient,可以通过@inject注入工厂对象IHttpClientFactory来创建HttpClient。

注射

@Page“/http-test”

@使用Microsoft.Net.Http.Headers

@rendermodeInteractiveAuto

@inject IHttpClientFactory _httpClientFactory

h3HttpClient测试/h3

@代码{

protected 覆盖异步任务OnInitializedAsync()

{

//封装http请求信息

var httpRequestMessage=new HttpRequestMessage(HttpMethod.Get,

\’https://api.github.com/repos/dotnet/AspNetCore.Docs/branches\’)

{

标头=

{

{ HeaderNames.Accept, \’application/vnd.github.v3+json\’ },

{ HeaderNames.UserAgent, \’HttpRequestsSample\’ }

}

};

var httpClient=_httpClientFactory.CreateClient() //创建一个HttpClient。

var httpResponseMessage=wait httpClient.SendAsync(httpRequestMessage); //发送请求。

//……

}

}

HttpClient 命名用法(推荐)

如果您的项目中需要使用多个不同配置的HttpClient,或者您创建的HttpClient对象已经具有基本配置,则可以使用命名注册方法。

命名注册- 计划

builder.Services.AddHttpClient(\’GitHub\’, httpClient=

{

httpClient.BaseAddress=new Uri(\’https://api.github.com/\’);

httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, \’application/vnd.github.v3+json\’);

httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, \’HttpRequestsSample\’);

});

接下来,我们在组件中通过@inject注入工厂对象IHttpClientFactory,并创建不同名称的不同HttpClient对象。

注射

@Page“/http-test”

@使用Microsoft.Net.Http.Headers

@rendermodeInteractiveAuto

@inject IHttpClientFactory _httpClientFactory

h3HttpClient测试/h3

@代码{

protected 覆盖异步任务OnInitializedAsync()

{

var httpClient=_httpClientFactory.CreateClient(\’GitHub\’);

var result=等待httpClient.GetAsync(\’repos/dotnet/AspNetCore.Docs/branches\’);

//.

}

}

为什么使用IHttpClientFactory?

HTTP 客户端管理

当IHttpClientFactory 调用CreateClient 时,会返回一个新的HttpClient 实例。 每个指定的客户端都会创建一个HttpMessageHandler,工厂管理HttpMessageHandler 实例的生命周期。 IHttpClientFactory 池化工厂创建的HttpMessageHandler 实例,以减少资源消耗。 创建新的HttpClient 实例时,可以重用池中的HttpMessageHandler 实例(如果它们尚未过期)。 IHttpClientFactory 跟踪并销毁HttpClient 实例使用的资源,因此HttpClient 实例一般可以视为不需要销毁的.NET 对象。

这里的discard是指丢弃或者取消请求等操作。

日志管理

通过IHttpClientFactory 创建的客户端记录所有请求的日志消息。 通过在日志配置中启用适当的信息级别可以显示默认日志消息。 仅在跟踪级别包含附加日志记录(例如请求标头日志记录)。

2、客户端使用

登记

在客户端项目中使用HttpClient 与在服务器项目中使用略有不同。客户端项目没有内置的HttpClient 服务,因此未提供AddHttpClient() 方法。因此,您必须使用AddScoped 方法注册HttpClient 服务。

注册程序.cs

builder.Services.AddScoped(sp=

新的HTTP 客户端

{

BaseAddress=new Uri(builder.HostEnvironment.BaseAddress)

});

注射

作用域HttpClient服务直接在客户端项目中注册,因此在注入时直接注入HttpClient对象就足够了。

注射

@page \’/http-test-client\’

@rendermodeInteractiveAuto

@注入HttpClient_httpClient

h3Hp 测试/h3

@代码{

protected 覆盖异步任务OnInitializedAsync()

{

var data=wait _httpClient.GetFromJsonAsyncData(\’api/GetData\’);

}

类数据

{

您想设置一个名字吗?

公共int 年龄{ 设置;

}

}

二、IJSRuntime

IJSRuntime 服务用于直接调用Blazor 项目中的JS 方法。在Blazor Web App Auto 项目中,服务器或客户端默认注册IJSRuntime 服务,因此当您使用它时,会直接注入IJSRuntime 对象。

生命周期

客户端:单实例

服务器端:范围服务

使用示例

成分

@Page“/js-test-server”

@rendermodeInteractiveServer

@注入IJSRuntime JS

h3Hp 测试/h3

@代码{

protected 覆盖异步任务OnAfterRenderAsync(bool firstRender)

{

if(第一次渲染)

{

wait JS.InvokeVoidAsync(\’确认\’, \’欢迎访问网站\’);

}

}

}

善良

公共类JSHandler

{

私有IJSRuntime _js;

公共JSHandler(IJSRuntime jSRuntime)

{

_js=jSRuntime;

}

公共异步任务confirmAsync()

{

var result=wait _js.InvokeAsyncbool(\’confirm\’, \’欢迎来到该页面\’);

}

}

三、NavigationManager

NavigationManager 服务用于处理URI 和导航。在Blazor Web App Auto 项目中,服务器或客户端默认会注册NavigationManager 服务,因此如果要使用它,只需直接插入NavigationManager 对象即可。

生命周期

客户端:单实例

服务器端:范围服务

使用示例

成分

@page \’/导航\’

@rendermodeInteractiveAuto

@inject NavigationManager 导航

h1 导航示例/h1

按钮类=\’btn btn-primary\’@onclick=\’NavigateToCounterComponent\’

转到计数器组件

/按钮

@代码{

私有无效NavigateToCounterComponent()

{

Navigation.NavigateTo(\’计数器\’);

}

}

善良

公共类NavHandler

{

私有导航管理器_navigationManager;

public NavHandler(NavigationManager 导航管理器)

{

_navigationManager=导航管理器;

}

公共无效NavTo(字符串uri)

{

_navigationManager.NavigateTo(uri);

}

}

四、IWebAssemblyHostEnvironment

1、组件中读取环境

如果您希望客户端组件获取应用程序的环境信息,可以通过注入IWebAssemblyHostEnvironment 并读取Environment 属性来实现。

@page \’/读取环境\’

@使用Microsoft.AspNetCore.Components.WebAssembly.Hosting

@inject IWebAssemblyHostEnvironment 环境

h1环境示例/h1

pEnvironment: @HostEnvironment.Environment/p

对于Blazor 网络,

App Auto应用,IWebAssemblyHostEnvironment仅在客户端项目(.Client)中进行了服务的注册,如果客户端项目中的组件允许预渲染,那么当组件在服务器上预渲染时,会由于找不到IWebAssemblyHostEnvironment而在注入该服务时出现异常。

针对这个问题,可以在服务器项目上创建IWebAssemblyHostEnvironment的自定义服务,然后进行注册:

ServerHostEnvironment.cs
public class ServerHostEnvironment(IWebHostEnvironment env, NavigationManager nav) : IWebAssemblyHostEnvironment
{
public string Environment => env.EnvironmentName;
public string BaseAddress => nav.BaseUri;
}
Program.cs
……
builder.Services.TryAddScoped<IWebAssemblyHostEnvironment, ServerHostEnvironment>();
……

2、启动时读取客户端环境

在启动过程中,WebAssemblyHostBuilder会通过HostEnvironment属性公开 IWebAssemblyHostEnvironment,因此可以在客户端项目的Program中,通过builder.HostEnvironment来获取环境信息。

if (builder.HostEnvironment.Environment == \”Development\”)
{
……
};

类似HostEnvironmentEnvExtensions为IHostEnvironment(服务端项目中Program所使用的主机环境类型)提供了一系列便捷的扩展方法一样,WebAssemblyHostEnvironmentExtensions则是为IWebAssemblyHostEnvironment(客户端项目中Program所使用的主机环境类型)提供了一系列便捷的扩展方法,可在当前环境中检查 Development、Production、Staging 和自定义环境名称

bool IsDevelopment()bool IsProduction()bool IsStaging()bool IsEnvironment(envName)
if (builder.HostEnvironment.IsStaging())
{
……
};
if (builder.HostEnvironment.IsEnvironment(\”Custom\”))
{
……
};

#以上关于第二章 基础知识(3) 的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92880.html

Like (0)
CSDN的头像CSDN
Previous 2024年7月4日
Next 2024年7月4日

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注