我有以下型号:
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个请求。
如果我按如下方式使用 Include :
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实现吗?
答案 0 :(得分:0)
EF将无法为您执行此操作。但是它会在表的模式中期望/创建类似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);
}
答案 1 :(得分:0)
首先,我强烈建议在模型中包含外键。避免 Blind 导航是一种推荐和良好的做法。您需要在PersonId
与实体相关的课程中加入Category
。
其次,EF 5.0(我不确定旧版本)支持通过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
答案 2 :(得分:0)
你的想法适用于一对多(或一对一)关系,因为它们在其中一个表中有一个外键,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没有关于此集合的基础类型的任何元数据。
对于不太大的表,整个过程可能更有效 - 或者它可能不会。没有测量,我真的说不出来。