web-dev-qa-db-ja.com

Entity Framework Coreで多対多の流APIなAPI

EF Core、Code First、Fluent APIを使用して、多対多関係を生成するための適切なソリューションを探して、stackoverflowを検索しました。

簡単なシナリオは次のとおりです。

public class Person
{
    public Person() {
        Clubs = new HashSet<Club>();
    }
    public int PersonId { get; set; }
    public virtual ICollection<Club> Clubs { get; set; }
}

public class Club
{
    public Club() {
        Persons = new HashSet<Person>();
    }
    public int ClubId { get; set; }
    public virtual ICollection<Person> Persons { get; set; }
}

間違っている場合は修正してください。しかし、説明されたツールを使用してこれを行う方法についての詳細な説明を含む質問を見つけることができませんでした。誰もこれがどのように行われるか説明できますか?

28
Anonymous

これは、結合に明示的なクラスを使用しない限り、EF Coreではまだ不可能です。その方法の例については、 here を参照してください。

Githubには、明示的なクラスを必要とせずにこれを行う機能を要求する issue がありますが、まだ完了していません。

シナリオを使用して、私がリンクした例では、以下のエンティティークラスを推奨します。

public class Person
{
    public int PersonId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class Club
{
    public int ClubId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class PersonClub
{
    public int PersonId { get; set; }
    public Person Person { get; set; }
    public int ClubId { get; set; }
    public Club Club { get; set; }
}

次のOnModelCreatingがセットアップに使用されます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PersonClub>()
        .HasKey(pc => new { pc.PersonId, pc.ClubId });

    modelBuilder.Entity<PersonClub>()
        .HasOne(pc => pc.Person)
        .WithMany(p => p.PersonClubs)
        .HasForeignKey(pc => pc.PersonId);

    modelBuilder.Entity<PersonClub>()
        .HasOne(pc => pc.Club)
        .WithMany(c => c.PersonClubs)
        .HasForeignKey(pc => pc.ClubId);
}

必要に応じて、リンクした未解決の問題に行き、フラストレーションを表明してください。

編集:未解決の問題は、このやや面倒な階層をナビゲートするために単純なSelectを使用することを提案しています。 PersonIdからClubsのコレクションに到達するには、 SelectMany を使用できます。例えば。:

var clubs = dbContext.People
    .Where(p => p.PersonId == id)
    .SelectMany(p => p.PersonClubs);
    .Select(pc => pc.Club);

これが本当に「ベストプラクティス」であるかどうかを保証することはできませんが、確かにトリックを実行する必要があります。

30
Kirk Larkin

このための正しい「セットアップ」は次のとおりです。

public class Person
{
    public int PersonId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class Club
{
    public int ClubId { get; set; }
    public virtual ICollection<PersonClub> PersonClubs { get; set; }
}

public class PersonClub
{
    public int PersonId { get; set; }
    public Person Person { get; set; }
    public int ClubId { get; set; }
    public Club Club { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PersonClub>()
        .HasKey(pc => new { pc.PersonId, pc.ClubId });
}

したがって、「グルーテーブル」を設定するためのこのブロックは、@ Kirkの例のようにnot必要です。

modelBuilder.Entity<PersonClub>()
    .HasOne(pc => pc.Person)
    .WithMany(p => p.PersonClubs)
    .HasForeignKey(pc => pc.PersonId);

modelBuilder.Entity<PersonClub>()
    .HasOne(pc => pc.Club)
    .WithMany(c => c.PersonClubs)
    .HasForeignKey(pc => pc.ClubId);
16
paul van bladel

したがって、すべてのPersonには0個以上のClubsがあり、すべてのClubには0個以上のPersonsがあります。正しく述べたように、これは適切な多対多の関係です。

おそらく、この多対多の関係を実装するには、リレーショナルデータベースに追加のテーブルが必要であることをご存知でしょう。エンティティフレームワークの良いところは、この関係を認識し、この追加のテーブルを作成することです。

一見、この余分なテーブルがdbSetDbContextではないという問題があるようです。「DbSetそれ?"。

幸いなことに、クエリでこの余分なテーブルについて言及する必要はありません。

「...「すべての「クラブ」」という...「すべての「人」から...」というクエリが必要な場合は、結合に考えません。代わりにICollectionsを使用してください!

参加するすべてのカントリークラブを持つすべての「ジョンドー」の人を取得します。

var result = myDbContext.Persons
    .Where(person => person.Name == "John Doe")
    .Select(person => new
    {
        PersonId = person.Id,
        PersonName = person.Name,
        AttendedCountryClubs = person.Clubs
            .Where(club => club.Type = ClubType.CountryClub),
    };

Entity Frameworkは、余分な多対多テーブルとの結合が必要であることを認識し、この余分なテーブルに言及せずにこの結合を実行します。

逆の方法:すべてのカントリークラブを「John Doe」Personで取得する:

var result = myDbContext.Clubs
    .Where(club => club.Type = ClubType.CountryClub)
    .Select(club => new
    {
         ClubId = club.Id,
         ClubName = club.Name,
         AnonymousMembers = club.Persons
             .Where(person => person.Name == "John Doe"),
    }

これらのコレクションを取得するために必要な結合の代わりに、必要な結果のコレクションで考え始めると、結合をほとんど使用しないことがわかりました。これは、1対多の関係と多対多の関係の場合です。エンティティフレームワークは内部で適切な結合を使用します。

0