使用EF Code First从数据库中选择特定列

sam*_*360 12 entity-framework linq-to-sql ef-code-first

我们有一个超过500列的客户非常大的桌子(我知道有人这样做!)

其中许多列实际上是其他表的外键.

我们还要求急切加载一些相关的表.

在Linq to SQL或Dynamic Linq中有什么方法可以指定从数据库中检索哪些列?我正在寻找一个linq语句,它实际上对生成的SQL语句有这种影响:

SELECT Id, Name FROM Book
Run Code Online (Sandbox Code Playgroud)

当我们运行由EF生成的reguar查询时,SQL Server会抛出一个错误,表明您已达到查询中可以选择的最大列数!

任何帮助深表感谢!


是的确是这种情况,该表有500列,并且自动引用我们的工具,自动加载第一级关系,这会对可查询的列数达到SQL限制.

我希望我可以设置为仅加载相关实体的有限列,例如Id和Name(在UI中用于向用户查看记录)

我想另一种选择是控制哪些FK列应该是急切加载的.但是,对于具有二进制或ntext列的表,这仍然存在问题,您可能不希望一直加载它们.

有没有办法将多个模型(实体)挂钩到Code First中的同一个表?我们尝试这样做,我认为努力失败了.

Lad*_*nka 18

是的,您只能使用投影返回列的子集:

var result = from x in context.LargeTable
             select new { x.Id, x.Name };
Run Code Online (Sandbox Code Playgroud)

问题:投影和急切加载不能一起工作.一旦开始使用投影或自定义连接,您就会改变查询的形状而无法使用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(...) 
             };
Run Code Online (Sandbox Code Playgroud)

您也可以投影到特定类型,但不能映射特定类型(因此您不能LargeTable从我的示例中投影到实体).只能对Linq到对象中的物化数据进行对映射实体的投影.

编辑:

可能存在一些误解EF如何工作的误解.EF在实体之上工作 - 实体是您映射的.如果将500列映射到实体,则EF只会在您定义实体时使用该实体.这意味着查询加载实体并持久保存实体.

为什么这样工作?实体被视为原子数据结构,其数据只能加载和跟踪一次 - 这是将更改正确保存回数据库的关键功能.这并不意味着您不应该只在需要时加载列的子集,但是您应该理解加载列的子集不会定义您的原始实体 - 它被视为实体中数据的任意视图.没有跟踪此视图,并且无需额外工作就无法将其保留回数据库(仅仅因为EF不保存有关投影原点的任何信息).

EF还对映射实体的能力施加了一些额外的限制

  • 每个表通常只能映射一次.为什么?同样,因为多次映射表到不同的实体可能会破坏正确持久化这些实体的能力 - 例如,如果任何非键列被映射两次并且您加载映射到同一记录的两个实体的实例,那么您将使用哪个映射值保存更改?
  • 有两个例外允许您多次映射表
    • 每个层次结构继承表 - 这是一个映射,其中表可以包含继承层次结构中定义的多个实体类型的记录.映射到层次结构中的基本实体的列必须由所有实体共享.每个派生实体类型都可以将自己的列映射到其特定属性(其他实体类型的这些列始终为空).无法在多个实体之间共享派生属性的列.还必须有一个名为discriminator的附加列告诉EF哪个实体类型存储在记录中 - 这些列不能映射为属性,因为它已经映射为类型鉴别符.
    • 表拆分 - 这是单表映射限制的直接解决方案.它允许您将表拆分为具有一些约束的多个实体:
      • 实体之间必须存在一对一的关系.您有一个用于加载核心数据的中央实体,所有其他实体都可以通过此实体的导航属性访问.急切加载,延迟加载和显式加载正常工作.
      • 关系是真实的1-1,因此两个部分或关系必须始终存在.
      • 实体不得共享除键之外的任何属性 - 此约束将解决初始问题,因为每个可修改属性仅映射一次
      • 拆分表中的每个实体都必须具有映射的键属性
      • 插入需要填充整个对象图,因为其他实体可以包含映射的必需列

Linq-to-Sql还包含将列标记为延迟加载的功能,但此功能目前在EF中不可用 - 您可以为该功能投票.

它会带来您的优化选择

  • 使用投影获取实体的只读"视图"
    • 您可以在Linq查询中执行此操作,如我在本答案的上一部分中所示
    • 您可以创建数据库视图并将其映射为新的"实体"
    • 在EDMX中,您还可以使用"定义查询"或"查询"视图在映射中封装SQL或ESQL投影
  • 使用表拆分
    • EDMX允许您将表拆分为多个实体而没有任何问题
    • 代码首先允许您拆分表,但是当您将表拆分为两个以上的实体时会出现一些问题(我认为它要求每个实体类型都具有来自拆分表的所有其他实体类型的导航属性 - 这使得它很难使用).