ABPフレームワークにおけるデータアクセスインフラストラクチャ

EF Coreの統合

EF Coreはマイクロソフトが提供するORMであり、SQL Server、Oracle、MySQL、PostgreSQL、Cosmos DBなどの主要なデータベースプロバイダーと連携できます。ABPコマンドラインインターフェース(CLI)を使用して新しいABPソリューションを作成すると、これはデフォルトのデータベースプロバイダーとして設定されます。

デフォルトでは、スタートアップテンプレートはSQL Serverを使用します。他のデータベース管理システム(DBMS)を希望する場合、新規ソリューション作成時に-dbmsパラメーターを指定できます。

abp new DemoApp -dbms MySQL

最新サポートされているデータベースオプションや、他の既存のデータベースプロバイダーへの切り替え方法については、ABPのドキュメントを参照してください。

3.1 DBMSの設定

モジュールのConfigureServicesメソッドでAbpDbContextOptionsを使用してDBMSを設定します。以下は、SQL Serverを使用した例です。

Configure<AbpDbContextOptions>(options =>
{
    options.UseSqlServer();
});

接続文字列は、appsettings.jsonファイルから自動的に取得されるため、手動で設定する必要はありません。

3.2 DbContextの定義

DbContextはEF Coreにおけるデータベースとの相互作用に使用される主なオブジェクトです。ABPフレームワークでは、AbpDbContextを継承して独自のDbContextを作成します。

using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;

namespace SampleApp
{
    public class SampleDbContext : AbpDbContext<SampleDbContext>
    {
        public DbSet<Item> Items { get; set; }

        public SampleDbContext(DbContextOptions<SampleDbContext> options)
            : base(options)
        {
        }
    }
}

3.3 DIシステムへのDbContextの登録

DIシステムにDbContextを登録するために、AddAbpDbContext拡張メソッドを使用します。

public override void ConfigureServices(ServiceConfigurationContext context)
{
    context.Services.AddAbpDbContext<SampleDbContext>(options =>
    {
        options.AddDefaultRepositories(true);
    });
}

3.4 エンティティのマッピング設定

エンティティをデータベーステーブルにマップする際には、Fluent APIを使用することをお勧めします。

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<Item>(b =>
    {
        b.ToTable("Items");
        b.ConfigureByConvention();
        b.Property(x => x.Title).HasMaxLength(200).IsRequired();
        b.HasIndex(x => x.Title);
    });

    builder.Entity<Category>(b =>
    {
        b.ToTable("Categories");
        b.ConfigureByConvention();
        b.Property(x => x.Name).HasMaxLength(100).IsRequired();
        b.HasOne<Item>().WithMany(x => x.Categories).HasForeignKey(x => x.ItemId).IsRequired();
    });
}

3.5 カスタムリポジトリの実装

カスタムリポジトリを実装するには、以下のコードを参考にしてください。

public class ItemRepository : EfCoreRepository<SampleDbContext, Item, Guid>, IItemRepository
{
    public ItemRepository(IDbContextProvider<SampleDbContext> dbContextProvider)
        : base(dbContextProvider) { }

    public async Task<List<Item>> GetListAsync(string title, bool includeInactive = false)
    {
        var dbContext = await GetDbContextAsync();
        var query = dbContext.Items.Where(i => i.Title.Contains(title));
        if (!includeInactive)
        {
            query = query.Where(i => !i.IsInactive);
        }
        return await query.ToListAsync();
    }
}

public class ItemService : ITransientDependency
{
    private readonly IItemRepository _itemRepository;

    public ItemService(IItemRepository itemRepository)
    {
        _itemRepository = itemRepository;
    }

    public async Task<List<Item>> GetItemsAsync(string title)
    {
        return await _itemRepository.GetListAsync(title, includeInactive: true);
    }
}

3.7 データロード

関連エンティティをロードする方法には、明示的ロード、遅延ロード、および即時ロードがあります。

明示的ロード

public async Task<IEnumerable<Category>> GetCategoriesAsync(Item item)
{
    await _itemRepository.EnsureCollectionLoadedAsync(item, i => i.Categories);
    return item.Categories;
}

遅延ロード

遅延ロードを有効にするには、以下のように設定します。

Configure<AbpDbContextOptions>(options =>
{
    options.PreConfigure<SampleDbContext>(opts =>
    {
        opts.DbContextOptions.UseLazyLoadingProxies();
    });
    options.UseSqlServer();
});

即時ロード

public async Task<Item> GetWithCategories(Guid itemId)
{
    var dbContext = await GetDbContextAsync();
    return await dbContext.Items
        .Include(i => i.Categories)
        .SingleAsync(i => i.Id == itemId);
}

UoWの理解

UoWは、ABPでデータベース接続やトランザクションを開始、管理、処理するための主要システムです。HTTPリクエストごとにUoWが開始され、成功時には変更が保存され、失敗時にはロールバックされます。

UoWオプションの設定

public override void ConfigureServices(ServiceConfigurationContext context)
{
    Configure<AbpUnitOfWorkDefaultOptions>(options =>
    {
        options.TransactionBehavior = UnitOfWorkTransactionBehavior.Enabled;
        options.Timeout = 300000; // 5分
        options.IsolationLevel = IsolationLevel.Serializable;
    });
}

手動でのUoW制御

特性を使用した例:

[UnitOfWork(isTransactional: true)]
public async Task ExecuteAsync()
{
    await _itemRepository.InsertAsync(new Item() { ... });
    await _itemRepository.InsertAsync(new Item() { ... });
}

サービスを使用した例:

public async Task ExecuteAsync()
{
    using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true, timeout: 15000))
    {
        await _itemRepository.InsertAsync(new Item() { ... });
        await _itemRepository.InsertAsync(new Item() { ... });
        await uow.CompleteAsync();
    }
}

タグ: EFCore ABP UoW

8月1日 23:37 投稿