今回はEntity FrameworkのDbContextを動的に使おうというお話です。
Entity Frameworkを普通に使う時はDbContextを継承したクラスにDbSet<モデルクラス>なプロパティを実装しますよね。
public class AddressBookContext : DbContext { public AddressBookContext() : base("AddressBookContext") { } public DbSet Persons { get; set; } }
では、モデルクラスが実行時に決まる場合はどうすればよいか?
実行時にDbModelBuilder.Entity
public class DynamicDbContext : DbContext { private static readonly MethodInfo DbModelBuilderEntityMethod; static DynamicDbContext() { // DbModelBuilder.Entity() を取得 DbModelBuilderEntityMethod = typeof(DbModelBuilder).GetMethod("Entity"); } public DynamicDbContext(string nameOrConnectionString, params Type[] entityTypes) : base(nameOrConnectionString) { this.EntityTypes = entityTypes; } public Type[] EntityTypes { get; private set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { foreach (var entityType in EntityTypes) { // DbModelBuilder.Entity() を実行 _DbModelBuilderEntityMethod.MakeGenericMethod(entityType).Invoke(modelBuilder, new object[] { }); } } }
DbContextはインスタンスを生成する際の内部処理で、自身に実装されているDbSet
なのでOnModelCreatingをオーバーライドして任意のモデルクラスでDbModelBuilder.Entity()メソッドを実行すればDbSet
使い方
// 実行時に型が判明したとする var newEntities = new[] { new Person { FirstName = "Naida", LastName="Huber" }, new Person { FirstName = "Miriam", LastName="Dickson" }, new Person { FirstName = "Jermaine", LastName="Martin" }, }; var type = newEntities.GetType().GetElementType(); using (var db = new DynamicDbContext("DynamicDbContext", type)) { // DbSetプロパティは無いのでSet()メソッドでDbSetインスタンスを取得 var dbSet = db.Set(type); Console.WriteLine(dbSet.ElementType); // --> Person dbSet.AddRange(newEntities); db.SaveChanges(); }
で、これ何が嬉しいのか?
Entity Frameworkはデータベースにテーブルを作成してくれるので、一時的にデータを格納したい場合とか(謎)
定義された型(上例だとPersonクラス)ではなく動的に生成した型と組み合わせると活きてきます。