是的,您可以使用投影仅返回列的子集:
var result = from x in context.LargeTable
select new { x.Id, x.Name };
问题:投影和急切加载不能一起工作。一旦您开始使用投影或自定义连接,您正在更改查询的形状并且您无法使用Include
(EF 将忽略它)。在这种情况下,唯一的方法是在投影结果集中手动包含关系:
var result = from x in context.LargeTable
select new {
Id = x.Id,
Name = x.Name,
// You can filter or project relations as well
RelatedEnitites = x.SomeRelation.Where(...)
};
您还可以投影到特定类型,但不能映射特定类型(例如,您不能LargeTable
从我的示例中投影到实体)。映射实体的投影只能在 Linq-to-objects 中的物化数据上完成。
编辑:
可能对 EF 的工作方式存在一些误解。EF 在实体之上工作 - 实体是您所映射的。如果您将 500 列映射到实体,EF 只需使用您定义的实体。这意味着查询加载实体并持久保存实体。
为什么它会这样工作?实体被认为是原子数据结构,它的数据只能加载和跟踪一次——这是将更改正确持久化回数据库的关键特性。这并不意味着您不应该只加载列的子集,但您应该了解加载列的子集并不能定义您的原始实体 - 它被视为实体中数据的任意视图。此视图不会被跟踪,并且无法在没有额外工作的情况下持久保存回数据库(仅仅是因为 EF 不保存有关投影来源的任何信息)。
EF 还对映射实体的能力施加了一些额外的限制
- 每个表通常只能映射一次。为什么?再次因为将表多次映射到不同实体可能会破坏正确持久化这些实体的能力 - 例如,如果任何非键列被映射两次并且您加载映射到同一记录的两个实体的实例,您将在期间使用哪个映射值保存更改?
- 有两个例外允许您多次映射表
- 每个层次结构继承表 - 这是一个映射,其中表可以包含来自继承层次结构中定义的多个实体类型的记录。映射到层次结构中基本实体的列必须由所有实体共享。每个派生实体类型都可以将其自己的列映射到其特定属性(其他实体类型的这些列始终为空)。不能在多个实体之间共享派生属性的列。还必须有一个称为鉴别器的附加列,告诉 EF 记录中存储了哪种实体类型 - 此列不能映射为属性,因为它已经映射为类型鉴别器。
- 表拆分 - 这是单表映射限制的直接解决方案。它允许您将表拆分为具有一些约束的多个实体:
- 实体之间必须存在一对一的关系。您有一个用于加载核心数据的中心实体,并且所有其他实体都可以通过该实体的导航属性进行访问。急切加载、延迟加载和显式加载正常工作。
- 关系是真实的 1-1,因此两个部分或关系都必须始终存在。
- 实体不得共享除键之外的任何属性 - 此约束将解决初始问题,因为每个可修改属性仅映射一次
- 拆分表中的每个实体都必须具有映射的键属性
- 插入需要填充整个对象图,因为其他实体可以包含映射的所需列
Linq-to-Sql 还包含将列标记为延迟加载的功能,但此功能目前在 EF 中不可用 - 您可以为该功能投票。
它导致您的优化选项
- 使用投影获取实体的只读“视图”
- 您可以在 Linq 查询中执行此操作,如我在此答案的前一部分中所示
- 您可以创建数据库视图并将其映射为新的“实体”
- 在 EDMX 中,您还可以使用定义查询或查询视图在映射中封装 SQL 或 ESQL 投影
- 使用表拆分
- EDMX 允许您毫无问题地将表拆分为多个实体
- 代码首先允许您拆分表,但是当您将表拆分为两个以上的实体时会出现一些问题(我认为它要求每个实体类型都具有从拆分表到所有其他实体类型的导航属性 - 这使得它很难使用)。