🦖

[.NET Core MVC] RepositoryPatternとDI

2022/01/12に公開

.NET Core MVCのControllerのアクションメソッド内で、データアクセスするロジックが多数含まれており、ビジネスロジックと混在している好ましくない現状がありました。そこでControllerからCRUD操作を切り離しリポジトリクラスに集約していくリポジトリーパターンに沿って、リファクタリングを行いました。

リポジトリパターン

リポジトリパターンとはデータアクセスするロジックをControllerから切り離し集約する考え方です。アクションメソッドはリポジトリクラスを介してデータアクセスするため、CRUD操作に関してビジネスロジックを考慮する必要がなくなります。このパターンの実装は自動化された単体テストやTDDを円滑にし、またアプリケーション開発でも各クラスの目的を明確にします。

今回はエンティティごとにリポジトリクラスを実装ていきます。UsersControllerでユーザーIDの存在確認をしていたロジックを例にリポジトリクラスで書き直していきます。リポジトリクラスはインターフェイスを介してアクセスするのが一般的な為、IUsersRepository(インターフェイス)とUsersRepository(実装クラス)を作成します。

DbContext

MyAppDbContext.cs
public class MyAppDbContext : DbContext
{
   public MyAppDbContext(DbContextOptions<MyAppDbContext> options): base(options)
   { }
   
   public DbSet<Users> Users { get; set; }
}

リポジトリクラス

リポジトリクラスにUserを取得するメソッドやCRUDメソッドが作成していきます。

IUsersRepository.cs
public interface IUsersRepository
{
    IEnumerable<Users> GetUsers();
    bool Exists(int userId);
}
UsersRepository.cs
public class UsersRepository: IUsersRepository
{
   private readonly MyAppDbContext _context;

   public UsersRepository(MyAppDbContext context)
   {
       _context = context;
   }
   
   public IEnumerable<Users> GetUsers()
   {
       return _context.Users.ToList();
   }
   
   public bool Exists(int userId)
   {
       return _context.Users.Any(u => u.UserId == userId);
   }
   
}

Controllerにリポジトリクラスをインジェクションする

Controllerにリポジトリを渡す為には明示的にUsersRepositoryクラスをインスタンス化する必要があります。オブジェクト指向ではクラス同士はインターフェイスで依存し合う事が推奨されます。そこでDI(DependencyInjection)機能を利用します。これにより特定の具象クラスに依存せずにクラス同士の依存関係を橋渡しする事ができます。
今回はUsersControllerにUsersRepositoryクラスをインジェクションする事になります。

DIサービスコンテナ

DIによる依存関係の管理はDIコンテナーが提供します。DIコンテナーでインターフェイスと実装クラスの紐付けを登録し、インジェクションすべきインスタンスを管理します。これによりUsersController型に対してUsersRepositoryのインスタンスを返されます。今回はIUsersRepository型の要求に対してUsersRepositoryオブジェクトをインジェクションする定義をAddTransientメソッドを利用し依存関係を設定します。

Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IUsersRepository, UsersRepository>();
}

Controllers

UsersController.cs
public class UsersController : Controller
{
    private readonly IUsersRepository _repository;

    public UsersController(IUsersRepository repository)
    {
        _repository = repository;
    }
    
    public IActionResult Index()
    {
        return View(_repository.GetUsers().ToList());
    }
    
    private void Exists (int userId)
    {
       return  _repository.Exists(userId);
    }
}

.NET Core MVCアプリケーションのController内のビジネスロジックとデータアクセスロジックの分離をリポジトリーパターンに沿って実装し、DIを使用してControllerにリポジトリをインジェクトしました。今回の例ではエンティティに1つのリポジトリを作成するものでしたが、全てのエンティティに対して1つのリポジトリクラスを実装する事もできます。実際にリファクタリングを行った際は後者で実装しました。

Discussion