依赖注入是一种在类及其依赖项之间实现控制反转(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