読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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が裏でやってくれてたことを自分でやれと。将来的にはサポートされるのかな?