Entity Frameworkコアで効率的に作業するには?

c# entity-framework entity-framework-core

質問

簡単なクラスの例を見てみましょう:

public class Book
{
    [Key]
    public string BookId { get; set; }
    public List<BookPage> Pages { get; set; }
    public string Text { get; set; }
} 

public class BookPage
{
    [Key]
    public string BookPageId { get; set; }
    public PageTitle PageTitle { get; set; }
    public int Number { get; set; }
}

public class PageTitle
{
    [Key]
    public string PageTitleId { get; set; }
    public string Title { get; set; }
}

ですから、PageTitilesをすべて取得したい場合は、BookIdのみを知っていれば、以下のようにいくつかのインクルードを書く必要があります:

using (var dbContext = new BookContext())
{
    var bookPages = dbContext
    .Book
    .Include(x => x.Pages)
    .ThenInclude(x => x.PageTitle)//.ThenInclude(x => x.Select(y => y.PageTitle)) Shouldn't use in EF Core
    .SingleOrDefault(x => x.BookId == "some example id")
    .Pages
    .Select(x => x.PageTitle);
}

PageTitlesを他の本とつなげたいのであれば、もう一度このメソッドを書き直す必要があり、BookId以外は何も変わりません!これはデータベースで作業するのに非常に非効率的です。この例では3つのクラスがありますが、非常に深いレベルにネストされた何百ものクラスがあれば、作業が非常に遅く、不快になります。

多数のインクルードと冗長なクエリを避けるために、データベースの作業をどのくらい正確に行う必要がありますか?

受け入れられた回答

問題1:私は一度に複数のインクルードを追加する必要がありIncludes

EFに関連するデータを明示的に組み込む必要があるので、その方法についてはありませんが、簡単に拡張メソッドを作成してよりクリーンなものにすることができます:

public static IQueryable<Book> GetBooksAndPages(this BookContext db)
{
    return db.Book.Include(x => x.Pages);
}

public static IQueryable<Book> GetBooksAndPagesAndTitles(this BookContext db)
{
    return GetBooksAndPages(db).ThenInclude(p => p.PageTitle)

}

次に、あなたは単に行うことができます:

var bookPages = dbContext
    .GetBooksAndPagesAndTitles()
    .SingleOrDefault(x => x.BookId == "some example id")
    .Pages
    .Select(x => x.PageTitle);

問題2:異なるIDに対してこのクエリを複数回記述する必要があります。

なぜbookIdパラメータを持つメソッドにリファクタリングするだけではないのですか?

public IEnumerable<PageTitle> GetPageTitlesForBook(BookContext dbContext, int bookId)
{
    return dbContext
        .GetBooksAndPagesAndTitles()
        .SingleOrDefault(x => x.BookId == bookId)
        .Pages
        .Select(x => x.PageTitle);
}

結論 - 何度も同じことを書いている場合、コードを再利用できる小さなメソッドにリファクタリングする絶好の機会です。


人気のある回答

与えられた例の中には、すべてのインクルード文が必要なものはありません。クエリの最後にselectを使用していて、DbSetなどのIQueryableで操作している場合、Entity Frameworkは「プロジェクション」と呼ばれる処理を実行し、必要なすべてのフィールドを含むクエリを実行します自動的に。

たとえば、元のコード:

using (var dbContext = new BookContext())
{
    var bookPages = dbContext
        .Book
        .Include(x => x.Pages)
        .ThenInclude(x => x.PageTitle)//.ThenInclude(x => x.Select(y => y.PageTitle)) Shouldn't use in EF Core
        .SingleOrDefault(x => x.BookId == "some example id")
        .Pages
        .Select(x => x.PageTitle);
}

あなたはこれを次のように書き換えることができます:

using (var dbContext = new BookContext())
{
    var bookPages = dbContext
        .Book
        .Where(x => x.BookId == "some example id")
        .SelectMany(x => x.Pages.Select(y => y.PageTitle))
        .ToList();
}

これを解決するためにEntity Frameworkが行うことは次のとおりです。

  1. Entity Frameworkにbooksテーブルのエントリを見てみましょう
  2. 次にEntity Frameworkに対して、特定のIDを持つ書籍のみが必要であることを伝えます(これはもちろん単一のレコードでなければなりません)
  3. そこから、各本のためにEntity Frameworkに、その書籍のすべてのページのリストが必要であることを伝えます(ここでは、Whereステートメントのために1つのブックのページになります)
  4. 次に、Entity Frameworkに対して、各ページからPageTitleだけを欲していることを伝えます
  5. 最後に、Entity Frameworkに、クエリを生成して実行するために提供したすべての情報を使用するように指示します

Entity Frameworkがどのように機能するかを理解したい場合は、最後のステップが重要です。あなたの例では、 SingleOrDefaultを呼び出すときに、クエリを実行するようにEntity Frameworkに指示しています。そのため、インクルードが必要です。あなたの例では、クエリを実行するときにページが必要であることをEntity Frameworkに実際通知していないので、 Includeを使用して手動で要求する必要がありInclude

私が投稿した例では、クエリを実行するまでに( ToListはクエリの実行をトリガーする)、Entity FrameworkはSelect式からページとタイトルが必要であることを認識しています。さらに良いことに、Entity Frameworkは、生成されるSELECT文に未使用の列も含めません。

私は投影を調べることを強くお勧めします。これはおそらく、手作業を継続的に手動で含めるという要件を取り除くために私が知っている最良の方法です。



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ