zukucode
主にWEB関連の情報を技術メモとして発信しています。

ASP.NETにMediatRを導入する

ASP.NET(.NET6)WebAPIのプロジェクトにMediatRのライブラリを導入したときの作業メモです。

プロジェクト構成

プロジェクト構成です。

Api層とApplication層の2つの層があります。

ApiWebAPIのプロジェクトで、Applicationはクラスライブラリのプロジェクトです。

ApiプロジェクトはApplicationプロジェクトを参照しています。

  • ソリューション
    • Api
      • Controllers
      • Api.csproj
      • Program.cs
    • Application
      • Application.csproj

WebAPIで受け取ったリクエストに応じて、Applicationでいろんな処理を行うため、ApplicationプロジェクトにMediatRをインストールします。

パッケージのインストール

以下の2つのパッケージをApplicationプロジェクトにインストールします。

Visual Studioの場合は「NuGetパッケージの管理」からインストールできます。

VSCodeの場合は以下のURLからdotnetコマンドなどを確認できます。

アプリケーション層の実装

IRequestというインターフェースを継承した入力クラスを作成します。

ここではユーザーIDをパラメータにユーザー情報を返す処理を作成します。

IRequest<T>Tには出力クラスを指定します。今回はユーザー情報のクラスを設定します。

検索処理は...Query、更新処理は...Commandと表現することが多いです。

まずは出力クラスを定義します。

UserFindByIdQueryResult.cs
public class UserFindByIdQueryResult
{
    public UserFindByIdQueryResult(string id, string  name)
    {
        Id = id;
        Name = name;
    }

    public string Id { get; }
    public string Name { get; }
}

次に、入力クラスを定義します。

UserFindByIdQuery.cs
using MediatR;

public class UserFindByIdQuery : IRequest<UserFindByIdQueryResult>
{
    public UserFindByIdQuery(string id)
    {
        Id = id;
    }

    public string Id { get; }
}

UserFindByIdQueryクラスに応じた処理を行うHandlerクラスを実装します。

IRequestHandler<入力クラス, 出力クラス>のインターフェースを継承したクラスを実装します。

このクラスにはHandleメソッドを実装する必要があります。

public 出力クラス Handler(入力クラス, CancellationToken)の形でHandleメソッドを定義します。

UserFindByIdQueryHandler.cs
using MediatR;

public class UserFindByIdQueryHandler : IRequestHandler<UserFindByIdQuery, UserFindByIdQueryResult>
{
    private readonly IUserQueryService _queryService;

    public UserFindByIdQueryHandler(IUserQueryService queryService)
    {
        _queryService = queryService;
    }

    public async Task<UserFindByIdQueryResult> Handle(UserFindByIdQuery query, CancellationToken cancellationToken)
    {
        return new UserFindByIdQueryResult("1", "ユーザーテスト");
        // 実際は以下のようにDBなどから取得することになります。
        // return await _queryService.FindById(query.Id);
    }
}

作成したHandlerをDIコンテナへ登録します。

ApplicationプロジェクトにDependencyInjection.csというファイルを作成し、DIの処理を追記します。

Handlerクラスを1つづつ登録する方法もありますが、以下のようにすれば、Applicationプロジェクト内のすべてのHandlerクラスを登録できます。

DependencyInjection.cs
using System.Reflection;
using MediatR;
using Microsoft.Extensions.DependencyInjection;

public static class DependencyInjection
{
    public static IServiceCollection AddApplication(this IServiceCollection services)
    {
        services.AddMediatR(Assembly.GetExecutingAssembly());
        return services;
    }
}

Api層の実装

ApiプロジェクトのProgram.csに、アプリケーション層のDependencyInjectionの処理を実行するように以下の処理を追加します。

Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddApplication();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// 以下省略

コントローラーでMediatRを参照できるようにします。

各コントローラーでコンストラクタインジェクションで参照してもよいですが、今回はコントローラーの基底クラスを作成し、そこでMediatRを定義します。

ApiControllerBase
using MediatR;
using Microsoft.AspNetCore.Mvc;

[ApiController]
public abstract class ApiControllerBase : ControllerBase
{
    private ISender? _mediator;
    protected ISender Mediator => _mediator ??= HttpContext.RequestServices.GetService<ISender>()!;
}

各コントローラーでは上記の基底クラスを継承し、MediatRの処理を実行します。

MediatR.Send(入力クラス)の形式でメソッドをコールすると、入力クラスに対応するHandlerの処理が実行されます。

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class UserController : ApiControllerBase
{
    [HttpGet("{id}")]
    public async Task<UserFindByIdQueryResult> FindById(string id)
    {
        return await Mediator.Send(new UserFindByIdQuery(id));
    }
}

以上で導入は完了です。

以下のフォルダ構成になりました。

  • ソリューション
    • Api
      • Controllers
        • ApiControllerBase.cs
        • UserController.cs
      • Api.csproj
      • Program.cs
    • Application
      • Users
        • UserFindByIdQuery
          • UserFindByIdQuery.cs
          • UserFindByIdQueryHandler.cs
          • UserFindByIdQueryResult.cs
      • Application.csproj
      • DependencyInjection.cs

MediatRを使えば、Api層で受け取ったリクエストを、Application層に処理をさばくようなイメージで実装できます。



関連記事