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();
}
}