題名が長いですね。
Entity Frameworkで、Code Firstでデータベースを作成する際に、例えば次の図のようなリレーションシップを構成したいときどうするか、という話です。
これは、モデルクラスの定義だけではできません。多分。
ただ参照を作っただけでは、当然、どのプロパティがどのリレーションに対応するのかEntity Frameworkは認識できないので、エラーが出るかとんでもないリレーションが作成されます。
こういった、モデルクラスの定義だけで表現できないリレーションを構成するには、Fluent APIを用います。
ではFluent APIの説明をする前に、モデルクラスの定義だけで構築した場合のリレーションを見てみましょう。
クラス定義はこんな感じ。
public class Block { public int Id { get; set; } public virtual Block Parent { get; set; } public virtual ICollection<Block> Children { get; set; } public virtual Block Next { get; set; } public virtual Block Previous { get; set; } }
他のクラスも似たような感じなので省略します。
属性は全くつけていません。この定義でデータベースを構築すると…?
なんということでしょう。ナビゲーションプロパティ毎にリレーションが作成されてしまいました。
新しいグループに所属したら、そのGroupのUsersにも勝手に追加されてほしいのに、これではそんなこと全然してくれないわけです。
ナビゲーションプロパティ、GroupsとUsers間には関係があるんだということを、Entity Frameworkに教える必要があります。
ではどう教えるか。Fluent APIを使います。
Fluent APIは、ContextクラスでOnModelCreatingメソッドをオーバーライドして記述します。
今回の場合どうすればよいのか見てみましょう。
class TestContext : DbContext { static TestContext() { Database.SetInitializer(new DropCreateDatabaseAlways<TestContext>()); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<User>().HasMany(model => model.MyGroups).WithOptional(model => model.Owner); modelBuilder.Entity<User>().HasMany(model => model.Groups).WithMany(model => model.Users); modelBuilder.Entity<Block>().HasMany(model => model.Children).WithOptional(model => model.Parent); modelBuilder.Entity<Block>().HasOptional(model => model.Next).WithOptionalDependent(model => model.Previous); } public DbSet<User> Users { get; set; } public DbSet<Group> Groups { get; set; } public DbSet<Block> Blocks { get; set; } }
これで、各リレーションが、意図通りに作成されます。