Blazor 将依赖项注入 Blazor 组件

[删除(380066935@qq.com或微信通知)]

将依赖项注入 Blazor 组件


定义我们的依赖关系

在注入依赖项之前,我们需要创建一个依赖项。 我们将使用古老的 ToDo 示例,但别担心,我们不会创建待办事项应用程序。

首先创建一个基本类。ToDo

public class ToDo
{
  public int Id { get; set; }
  public string Title { get; set; }
  public bool Completed { get; set; }
}

接下来,我们将创建一个 Blazor 页面或组件可能需要的类。 在这种情况下,它将是一个从服务检索项的 API。 我们不会真正调用服务器,我们只会返回一些模拟数据。ToDo

using System.Collections.Generic;
using System.Threading.Tasks;

namespace BasicDependencyInjection
{
  public interface IToDoApi
  {
    Task<IEnumerable<ToDo>> GetToDosAsync();
  }

  public class ToDoApi : IToDoApi
  {
    private readonly IEnumerable<ToDo> Data;

    public ToDoApi()
    {
      Data = new ToDo[]
      {
        new ToDo { Id = 1, Title = "To do 1", Completed = true},
        new ToDo { Id = 2, Title = "To do 2", Completed = false},
        new ToDo { Id = 3, Title = "To do 3", Completed = false},
      };
    }

    public Task<IEnumerable<ToDo>> GetToDosAsync() => Task.FromResult(Data);
  }
}

我们通过实现接口来抽象服务。 这是一个很好的做法,以防我们希望在单元测试我们的类时通过模拟。

注册可注入依赖项

当 Blazor 应用程序运行其启动代码时, 它为我们做的一件事是配置依赖注入容器。 依赖注入容器负责构建类的实例 (及其依赖项的实例,依此类推)。

在此引导过程中, 我们应该注册我们希望将哪些类作为自动注入的依赖项提供。 我们可以将类本身注册为可注入的,如下所示:

services.AddSingleton<ToDoApi>();

或者我们可以将接口注册为可注入的,只要我们另外指定实现该接口的类即可。

service.AddSingleton<IToDoApi, ToDoApi>();

注意:同样,建议使用后一种方法,以使单元测试更简单。 它还允许在配置文件中指定实现类 - 例如, 我们可以指定不同的IEmailService,具体取决于部署的平台是开发/测试/生产。

注册依赖项的其他模式包括:

// Register an existing object instance
services.AddSingleton(existingObject);

// Register an existing object instance, injected via an interface
services.AddSingleton<ISomeInterface>(implementingInstance);

// Lazy created instance, with manual build process and access to the current IServiceProvider
services.AddSingleton<ISomeInterface>(serviceProvider => new ImplementingType(.......));

引导代码在 Blazor Server 和 Blazor WASM 应用程序中并不相同,因此, 虽然服务注册相同, 我们必须去哪里注册我们的可注入依赖项略有不同。

在 Blazor 服务器应用中注册注射对象

在 Blazor 服务器应用中,有一个带有方法的类。 这是我们需要执行注册的地方。StartupConfigureServices

public void ConfigureServices(IServiceCollection services)
{
  ... default Blazor registrations omitted ...
  // Register our own injectables
  services.AddSingleton<IToDoApi, ToDoApi>();
}

当我们在创建应用程序时选中 ASP.NET Core 托管复选框时,WASM 应用程序也是如此。 这是因为服务器负责引导整个应用程序。

在 Blazor WASM 应用中注册注射剂

当我们的 Blazor 项目是独立的 WASM 应用程序(不是 ASP.NET 核心托管)时, 应用程序必须有自己的引导程序类。 在这种类型的应用程序中,类被命名为 , 并命名引导方法 - 就像在控制台应用程序中一样。ProgramMain

public static async Task Main(string[] args)
{
  var builder = WebAssemblyHostBuilder.CreateDefault(args);
  builder.RootComponents.Add<App>("app");

  builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

  // Register our own injectables
  builder.Services.AddSingleton<IToDoApi, ToDoApi>();

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

注入依赖项

对于非 Blazor 类(如其他可注入服务),可以通过类的构造函数注入依赖项。

public class NewsletterService : INewsletterService
{
  private readonly IEmailService EmailService;

  public NewsletterService(IEmailService emailService)
  {
    EmailService = emailService;
  }
}

但是,对于 Blazor 组件,情况并非如此。 目前不支持构造函数注入。 有两种方法可以指示组件使用哪些依赖项;一个在 Razor 标记中,一个在 C# 代码中。

@inject IToDoApi ToDoApi
@inject ISomeServiceType AnotherService

@code
{
  [Inject]
  private IYetAnotherServiceType PropertyInjectedDependency { get; set; }
}

InjectAttribute 只能应用于具有属性资源库的属性,该属性的封装级别无关紧要。

注意:这两种方法是相同的。 实际上,语法只是语法的简写。 生成应用时,Blazor 将首先将 Puror 标记转译或转换为 C# 源代码。 若要查看语法是如何转换的,请打开文件夹 \obj\Debug\netcoreapp3.1\Razor 并查找与 razor 文件对应的文件。@inject[Inject]@inject.cs

使用注入的依赖项

依赖项是在创建 Blazor 组件实例之后和执行 或 生命周期事件之前注入的。 这意味着我们不能覆盖组件的构造函数并从那里使用这些依赖项, 但是我们可以在方法中使用它们。OnInitializedOnInitializedAsyncOnInitialized*

要使用我们的服务, 我们只需使用语法将其注入到我们的页面中,然后在页面初始化时调用它。IToDoApiIndex@inject

@page "/"
@inject IToDoApi ToDoApi

<h1>To do</h1>
@if (Data.Any())
{
  <table class="table">
    <thead>
      <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Completed</th>
      </tr>
    </thead>
    <tbody>
      @foreach (ToDo item in Data)
      {
        <tr>
          <td>@item.Id</td>
          <td>@item.Title</td>
          <td>@item.Completed</td>
        </tr>
      }
    </tbody>
  </table>
}

@code
{
  private IEnumerable<ToDo> Data = Array.Empty<ToDo>();

  protected override async Task OnInitializedAsync()
  {
    await base.OnInitializedAsync();
    Data = await ToDoApi.GetToDosAsync();
  }
}
  • 第 2
    行 一个实例被注入到我们的页面中,我们使用名称 ToDoApi 来引用注入的依赖项。
    IToDoApp
  • 第 35
    行 该方法在注入的服务上调用。等待该方法,并将结果存储在数据中。
    GetDoToAsync
  • 第 16-23
    行 Data 中的项使用循环进行迭代,输出呈现为视图的一部分。
    @foreach