# 十二、常见陷阱

## 概述

无论何时你开始使用一项新技术，都有可能落入其中一个陷阱。这里我将列出其中的一些。

## 除非调用 SaveChanges，否则更改不会发送到数据库

这可能是显而易见的，但人们有时还是会忘记这一点。

## LINQ 对未映射属性的查询

Visual Studio 智能感知使编写 LINQ 查询变得非常容易，因为它会自动显示所有可用的属性。这些属性中可能有一些实际上没有被映射。例如，它们是从其他属性计算的只读计算列。对这些属性的访问不能转换为 SQL 查询，因此在实体框架查询中使用它们的任何尝试都会导致异常。

## 空导航属性

如果在加载的实体中有空导航属性，这可能是由于以下原因之一:

*   通过将[lazyloadignated](http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbcontextconfiguration.lazyloadingenabled.aspx)或 [ProxyCreationEnabled](http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbcontextconfiguration.proxycreationenabled.aspx) 设置为 false，您已经全局禁用了延迟加载。
*   导航属性或其包含的类不符合延迟加载的条件(或者该类是密封的，或者该属性未标记为虚拟的)。

如需更多信息，请访问 *[【懒惰】【明确】和](04.html#LEnE)* 部分。

## 验证不加载引用

假设您的实体中有一个验证规则，当实体将要被持久化时，该规则会检查引用属性上的某些内容。如果启用了延迟加载，引用属性将在被访问时自动加载。然而，当 EFCF 执行验证时，这不会发生，引用将为空。要么在调用[保存更改](http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext.savechanges.aspx)之前确保加载了所有必需的属性，要么在验证方法上显式强制加载。

## 具体的表继承和身份密钥

应用具体表继承模式时，不能使用 [IDENTITY](http://technet.microsoft.com/en-us/library/ms186775.aspx) 生成密钥。这在 *[传承策略](02.html#InheritanceStrategies)* 中有描述。

## 无法从 SQL 查询返回复杂类型

当使用 SQL 查询模型时，不能返回具有复杂类型的实体。这是一个已知的限制，唯一的解决方法是将实体的所有值作为对象数组获取，然后根据这些值手动创建一个实体。

## 单表继承中不能有不可空的列

使用单表继承模式时，在派生类中不能有不可为空的属性，因为所有的属性都将在同一个表中创建，并且它们不会存在于所有的派生类中。

## 删除带有所需引用的分离实体不起作用

在[第 5 章“将数据写入数据库”](05.html#_Chapter_5_)中，我提出了一种简单的方法，无需先加载实体即可从数据库中删除实体。如果您的实体有任何必需的引用，这将不起作用。如果是这种情况，您需要要么加载实体，要么将其附加到上下文，并在删除之前加载该引用。

## 尝试在分离的实体中惰性加载导航属性

如果您尝试在分离的实体(例如从 ASP.NET 会话中检索到的实体)中加载导航属性，访问将引发异常，因为原始上下文已被释放。在存储之前，您需要加载所有必需的导航属性，或者将实体附加到新的上下文。详见 *[【懒、显、急加载】](04.html#LEnE)* 。

## 选择 N + 1

说到性能，这是迄今为止最典型的问题。发生的事情是:您发出一个查询，然后对于每个返回的记录，当您导航它们时，您访问一些导航属性，并执行另一个查询。通过在原始查询中显式包含所需的导航属性，可以轻松防止这种情况。