[英]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
。 當您僅從“ Person
和Category
表中加載沒有相關數據的“平面”數據時,就不會加載該表中的任何列。 加載那些可以告訴EF哪個Person
屬於哪個Categories
(反之亦然)的數據后,內存中根本沒有任何信息。
要在內存中創建正確的關系,您必須將鏈接表加載為第三個表。
var linkRecords = context.People.SelectMany(p => p.Categories.Select(c => new
{
PersonId = p.Id,
CategoryId = c.Id
}))
.ToList();
(我相信這是一個相對便宜的SQL查詢,僅從鏈接表中獲取數據,而沒有任何連接)
...,然后根據linkRecords
和已加載的Person
和Category
實體的PK在內存中手動構建導航集合。 EF在這里無濟於事,因為鏈接表記錄不是實體。 linkRecords
只是內存中具有一對密鑰的對象的“臨時”集合,EF沒有關於該集合的基礎類型的任何元數據。
對於不太大的表,整個過程可能會更高效-否則可能不會。 沒有測量,我真的無法分辨。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.