簡體   English   中英

如何在EF中使用多對多關系有效地加載數據?

[英]how to efficiently load data with many-to-many relationships in EF?

我有以下模型:

public class Person
{
    public int Id { get; set; }
    public virtual ICollection<Category> Categories { get; set; }
}

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

基本上一個人可以屬於多個類別。 該代碼導致EF創建了CategoryPerson { PersonId, CategoryID }表。 現在,我想在列表中顯示所有類別的所有人。 天真的方法:

var people = context.People.ToList();
foreach (var p in people)
{
    Console.WriteLine("Person {0}, categories: {1}", p.Id, string.Join("|", p.Categories.Select(x => x.Name)));
}

導致對數據庫的1 + N個請求。

如果我按如下方式使用“ 包含 ”:

var people = context.People.Include(x => x.Categories).ToList();
foreach (var p in people)
{
    Console.WriteLine("Person {0}, categories: {1}", p.Id, string.Join("|", p.Categories.Select(x => x.Name)));
}

我的確只有1個請求,但是它是2個表的聯接,如果Person記錄很重,並且有多個關聯的類別,則相同的重數據將多次返回:

{ person1, category1 } 
{ person1, category2 } 
{ person1, category3 } 

等等

理想情況下,我希望對數據庫發出2個請求-一個請求獲取所有類別,另一個請求獲取所有人。 然后,理想情況下,這兩個數組應該連接到內存中-因此,當我枚舉Person.Categories時,它不會進入數據庫,而是會獲取預加載的數據。 EF可以實現嗎?

EF將無法為您執行此操作。 但是它將期望/在表的架構中的Category上創建一個類似於Person_Id的外鍵。 如果將其添加到“ Category則可以在內存中進行聯接:

public class Person
{
    public int Id { get; set; }
    public virtual ICollection<Category> Categories { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public int Person_Id { get; set; }
    public string Name { get; set; }
}

var people = context.People.ToList();
var categories = context.Categories.ToList();

foreach (var p in people)
{
    p.Categories = categories.Where(a => a.Person_Id == a.Id);
}

首先,我強烈建議您在模型中包含外鍵。 建議您避免盲目導航。 您需要在與實體相關的Category包括PersonId

其次,EF 5.0(我不確定較舊的版本)支持通過Load方法將DBSet<T>完全Load到上下文中。 填充DBSet之后,可以使用Local屬性來指定所需的內存中實體。

context.People.Load();
context.Categories.Load();

var q = (from p in context.People.Local
        join c in context.Categories.Local
        on a.PersonId equals c.PersonId
        select p
        ).ToList(); //--> No round trip to DataBase

您的想法適用於一對多(或一對一)關系,因為它們在表之一中具有外鍵,並且EF會加載此FK(無論您是否將其公開為模型屬性)。 然后,EF能夠根據PK和加載的FK在內存中重建對象圖(稱為“關系修正”)。

但是,它對於多對多關系不起作用,因為Person表和Category表都沒有另一個表的外鍵。 FK在鏈接表CategoryPerson 當您僅從“ PersonCategory表中加載沒有相關數據的“平面”數據時,就不會加載該表中的任何列。 加載那些可以告訴EF哪個Person屬於哪個Categories (反之亦然)的數據后,內存中根本沒有任何信息。

要在內存中創建正確的關系,您必須將鏈接表加載為第三個表。

var linkRecords = context.People.SelectMany(p => p.Categories.Select(c => new
{
    PersonId = p.Id,
    CategoryId = c.Id
}))
.ToList();

(我相信這是一個相對便宜的SQL查詢,僅從鏈接表中獲取數據,而沒有任何連接)

...,然后根據linkRecords和已加載的PersonCategory實體的PK在內存中手動構建導航集合。 EF在這里無濟於事,因為鏈接表記錄不是實體。 linkRecords只是內存中具有一對密鑰的對象的“臨時”集合,EF沒有關於該集合的基礎類型的任何元數據。

對於不太大的表,整個過程可能會更高效-否則可能不會。 沒有測量,我真的無法分辨。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM