Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

11 Aug 2021

Blazor文档阅读【五】

Blazor文档阅读 》 Fundamentals 。

ASP.NET Core Blazor configuration

BlazorWASM默认从下面设置文件中读取设置:

  • wwwroot/appsettings.json
  • wwwroot/appsettings.{ENVIRONMENT}.json

App settings configuration

appsettings.json示例:

{
  "h1FontSize": "50px"
}

使用到上述参数的razor文件:

@page "/configuration-example"
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<h1 style="font-size:@Configuration["h1FontSize"]">
    Configuration example
</h1>

注意到@inject IConfiguration Configuration这一行所提供的Configuration例现。

略过关于如何使用HttpClient来读取服务端的数据部分。

Memory Configuration Source

如何从内存读取数据,首先要使用到以下命名空间:

using Microsoft.Extensions.Configuration.Memory;

然后在Program.cs的Program.Main中:

var vehicleData = new Dictionary<string, string>()
{
    { "color", "blue" },
    { "type", "car" },
    { "wheels:count", "3" },
    { "wheels:brand", "Blazin" },
    { "wheels:brand:type", "rally" },
    { "wheels:year", "2008" },
};

var memoryConfig = new MemoryConfigurationSource { InitialData = vehicleData };

builder.Configuration.Add(memoryConfig);

具体使用也是通过IConfiguration。

可以使用IConfiguration.GetSection只获取配置中的某个分区:

@code {
    protected override void OnInitialized()
    {
        var wheelsSection = Configuration.GetSection("wheels");

        ...
    }
}

Authentication configuration

略。

Logging configuration

配置所依赖的NuGet包是:

<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="{VERSION}" />

在appsettings.json中设置录定:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

添加到当前配置集中:

builder.Logging.AddConfiguration(
    builder.Configuration.GetSection("Logging"));

Host builder configuration

从WebAssemblyHostBuilder.Configuration 重新加载配置,在Program.Main中加入:

var hostname = builder.Configuration["HostName"];

Cached configuration

PWA的配置更新只能在重新部署PWA的时候执行,因为:

  • 用户已经缓存了配置文件
  • PWA的service-worker.js以及service-worker-assets.js必须在编译的时候重建

ASP.NET Core Blazor dependency injection

Server和WASM的方式是不一样的。

DI是集中的方式来访问服务的一种方式:

  • 框架注册的服务可以直接注入组件
  • 应用可以自定的服务用于注入组件

Default services

Service Lifetime Description
HttpClient Scoped
IJSRuntime 对于WASM来说是Singleton,对于Server来说是Scoped
NavigationManager 同上

Add services to an app

下面的例子在服务中添加一个IMyDependency:

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        ...
        builder.Services.AddSingleton<IMyDependency, MyDependency>();
        ...

        await builder.Build().RunAsync();
    }
}

若需要初始化服务的逻辑,需要在build.Build()之后:

        var host = builder.Build();

        var weatherService = host.Services.GetRequiredService<WeatherService>();
        await weatherService.InitializeWeatherAsync(
            host.Configuration["WeatherServiceUrl"]);

        await host.RunAsync();

Service lifetime

  • Scoped,WASM类没有此存活时长类型,scoped会表现得像Singleton。
  • Singleton,DI创建单例现的服务。所有组件共享一个服务。
  • Transient,每次组件都需要创建一个新例现,才能使用该服务。

Blazor的DI是基于ASP.NET的DI的:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0

Request a service in a component

在组件中需要使用@inject指示来请求服务注入,额外两个参数:

  • 注入服务的类型
  • 接收注入的辖属

如果有需要,可以显示指定接收注入的辖属:

using Microsoft.AspNetCore.Components;

public class ComponentBase : IComponent
{
    [Inject]
    protected IDataAccess DataRepository { get; set; }

    ...
}

上述ComponentBase的派生类型就不需要使用@inject来注入了:

@page "/demo"
@inherits ComponentBase

<h1>Demo Component</h1>

Use DI in services

Service之间可能会依赖,比如下面的DataAccess依赖于HttpClient:

using System.Net.Http;

public class DataAccess : IDataAccess
{
    public DataAccess(HttpClient http)
    {
        ...
    }
}

Service中不能使用@inject或者[inject]。而只能使用构造函数初始化,并且有一定要求:

  • 必须存在这样的构造函数,它的前几个参数能够被DI用于注入。可以有额外的入参,但必须指定默认值。
  • 上述构造函数必须是public可访问的,并且只有一个候选

Utility base component classes to manage a DI scope

限制服务存活时长的另一个办法是使用OwningComponentBase,这是从ComponentBase派生出来的。OwningComponentBase所管理的服务的存活时长跟组件自身的存活时长一致。这有如下好处:

  • 比起transient,服务可在一定范围内复用
  • 不与其他组件共享,所以singleon的存活管理方式不适用

OwningComponentBase有两个版本:

第一版本,OwningComponentBase是抽象的,提供了一个proctected的ScopedServices辖属,其类型为IServiceProvider。

使用@inject或者[inject]创建的组件不在组件的存活时长范围内,必须通过GetRequiredService或者GetService或来获取。

@page "/preferences"
@using Microsoft.Extensions.DependencyInjection
@inherits OwningComponentBase

```razor
@code {
    private IUserService UserService { get; set; }
    private ISettingService SettingService { get; set; }

    protected override void OnInitialized()
    {
        UserService = ScopedServices.GetRequiredService<IUserService>();
        SettingService = ScopedServices.GetRequiredService<ISettingService>();
    }

...
}

第二版本是从OwningComponentBase派生出来的OwningComponentBase<TService>,添加了一个额外的Service辖属,用于返回DI提供的T的现例。也就是为访问应用的主要服务提供快捷方式:

@page "/users"
@attribute [Authorize]
@inherits OwningComponentBase<AppDbContext>

<h1>Users (@Service.Users.Count())</h1>

<ul>
    @foreach (var user in Service.Users)
    {
        <li>@user.UserName</li>
    }
</ul>

Use of an Entity Framework Core (EF Core) DbContext from DI

Detect transient disposables

举了一个例子DetectIncorrectUsagesOfTransientDisposables.cs

(本篇完)

Categories