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

12

我们有一个非常庞大的客户表,拥有超过500个列(我知道有人这么做了!)

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

我们还需要急切加载一些相关的表。

在 Linq to SQL 或者 Dynamic Linq 中是否有一种方法可以指定从数据库中检索哪些列? 我正在寻找一条 Linq 语句,它实际上对生成的 SQL 语句有此效果:

SELECT Id, Name FROM Book

当我们运行由EF生成的常规查询时,SQL Server会抛出一个错误,提示您已经达到了可以在查询中选择的列的最大数量!!!

非常感谢任何帮助!


没错,情况就是这样,该表有500个列并且引用了自身,我们的工具自动地贪婪地加载第一级关联数据,这超过了SQL允许查询的列数限制。

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

我想另一个选项是控制应该急切加载哪些FK列。然而,对于具有二进制或ntext列的表,您可能不希望始终加载所有数据,这仍然是问题。

在Code First中是否有一种将多个模型(实体)钩入相同表格的方法?我们尝试做到这一点,但成果惨淡。

2个回答

19

您可以使用投影仅返回子列:

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允许您将表拆分为多个实体而不会出现任何问题 - Code First也允许您拆分表,但是当您将表拆分为两个以上的实体时会出现一些问题(我认为它要求每个实体类型都有导航属性到来自拆分表的所有其他实体类型-这使得它非常难以使用)。

我希望有更可靠的方法来指定列。在许多情况下,我们的MVC应用程序被多个模型共享,我们使用动态Linq来执行不同表的相同查询。 - sam360
动态 Linq 应该能够指定投影。EF 是用于处理实体的工具 - 实体是您已映射的内容 = 如果您已将 500 列映射到实体,则没有投影的查询将始终返回 500 列 + 所有贪婪加载关系的列。 - Ladislav Mrnka
是的,情况确实如此。该表有500个列,并且是自引用的。我们的工具会自动急切加载第一级关系,这会触发SQL查询中可以查询的列数限制。我希望我可以设置仅加载相关实体的有限列,例如Id和Name(在UI中用于向用户查看记录)。我想另一个选择是控制应该急切加载哪些列。但是对于具有二进制或ntext列的表仍然存在问题,您可能不希望加载所有项。 - sam360
我在我的回答中添加了一些细节。 - Ladislav Mrnka
1
我认为EF需要支持加载选择的列,而不管实体上的属性如何。对于小型自定义应用程序来说,使用你上面提到的方法之一可能非常容易。但是,如果应用程序需要根据用户模式动态工作,所有这些方法都是无用的。所以,请投票支持对EF生成的SQL查询拥有更多的控制权。 - sam360

0

创建存储过程来查询所需的列数,然后从代码中调用这些存储过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接