かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

UWPでEntity Framework OneToMany

単体のテーブルの定義ではなく、テーブル間のリレーションの定義の仕方です。これもOnModelCreatingで指定します。例えば以下のような感じのクラスがあったとして

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int Age { get; set; }

    public List<Address> Addresses { get; set; }
}

public class Address
{
    public int Id { get; set; }

    public string Country { get; set; }

    public string City { get; set; }

    public int PersonId { get; set; }

    public Person Person { get; set; }
}

このクラス間の関連を定義するには以下のようになります。Person側でHasManyってやってもいいけど、Address側だけで定義するだけでも動いてます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .ToTable("People")
        .Property(x => x.Name)
        .HasMaxLength(512)
        .IsRequired();

    var address = modelBuilder.Entity<Address>();
    address.ToTable("Addresses");
    address.Property(x => x.City)
        .IsRequired();
    // 多対1
    address.HasOne(x => x.Person)
        // 反対側はMany
        .WithMany(x => x.Addresses)
        // 外部キーを指定
        .HasForeignKey(x => x.PersonId)
        // 削除時の動作を指定
        .OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Restrict);
}

リレーションのあるデータの投入は以下のように、データを素直に突っ込んでContextに渡してSaveChangesでOKです。

using (var ctx = new SampleContext())
{
    ctx.People.Add(new Person
    {
        Name = "Tanaka",
        Addresses = new List<Address>
        {
            new Address { City = "Tokyo" },
            new Address { City = "Saitama" },
        }
    });
    ctx.SaveChanges();
}

データを取り出すときはInclude拡張メソッドを使って明示的に一緒に読み込むことを指定します。

// 以下のusingが必要
// using Microsoft.Data.Entity;

using (var ctx = new SampleContext())
{
    var p = ctx.People.Include(x => x.Addresses).First();
    Debug.WriteLine(p.Name);
    foreach (var item in p.Addresses)
    {
        Debug.WriteLine(item.City);
    }
}
Debug.WriteLine("---");
using (var ctx = new SampleContext())
{
    var addresses = ctx.Addresses.Include(x => x.Person).ToArray();
    foreach (var a in addresses)
    {
        Debug.WriteLine($"{a.City} {a.Person.Name}");
    }
}

余談

OneToManyがよく使うと思うのでそれだけ紹介してますが、OneToOneもばっちりあります。似たような感じで使えるので迷うことはないと思います。

後、ManyToManyはサポートされてないので関連表みたいなのに対応するクラスを自分で作って、それとOneToManyで繋ぎ合わせることでやらないといけません。要は、Entity Framework 6が裏でやってくれてたことを自分でやれと。将来的にはサポートされるのかな?