使用实体键(实体框架)

每个实体类型都有一个键,该键基于相应实体的一个或多个标量属性。 键是由概念模型中的 Key 元素定义的。 与在关系数据库中一样,这些键值用于验证给定实体的唯一性,从而提高查询的性能。 通常,键属性映射到基础表的键列,该键列可以是标识列或具有保证唯一值约束的其他列。 有关如何在概念模型中定义键的更多信息,请参见Key 元素 (CSDL)

当对象查询返回一个对象时,实体框架 将具体化实体对象。 它还将实体键具体化到 EntityKey 类的实例中。 可以通过实现了 IEntityWithKey 的对象的 EntityKey 属性来访问此 EntityKeyEntityObject(实体数据模型 工具生成的所有数据类的基类)也实现 IEntityWithKey

Dd283139.note(zh-cn,VS.100).gif注意:
实体框架 不需要您在自定义数据类中实现 IEntityWithKey

构造和使用 EntityKey 对象

EntityKey 对象由 EntitySetNameEntityContainerName 属性以及一个包含一个或多个键/值对的数组所组成。 键/值由属性名称和属性值组成。 提供键/值对作为 EntityKeyValues 属性中的一个或多个 EntityKeyMember 对象。

Dd283139.note(zh-cn,VS.100).gif注意:
使用其中一个 EntityKey 构造函数时,提供给 qualifiedEntitySetName 参数的字符串值是前面附有 EntityContainerNameEntitySetName,形式为“实体容器名称.实体集名称”。

还可以使用 ObjectContextCreateEntityKey 方法获取已分离对象的 EntityKey。 如果对象没有有效的键,则对象上下文会为指定对象构造一个新的 EntityKey 实例。 有关更多信息,请参见如何:创建 EntityKey(实体框架)

由于实体键唯一标识实体,因此可以创建只具有键的实体并将该对象附加到对象上下文,即使没有从数据源中检索到其余对象值也是如此。 有关更多信息,请参见如何:附加相关对象(实体框架)。 还可以使用实体键从对象上下文或从数据源中检索对象。 有关更多信息,请参见如何:使用特定对象的键返回特定对象(实体框架)

固定长度的实体键

实体框架 根据 EntityKey(对应于数据库中的主键)的值执行标识解析。 如果查询返回一个对象,且该对象的 EntityKey 已存在于 ObjectContext 中,则不会创建新对象。 如果您使用的是数据库中的固定大小的列,并且保留的值短于数据库中指定的大小,则某些数据库将用空格或零来填充固定大小的类型。 SQL Server 使用尾随空格来填充固定大小的字符串类型。 当固定大小的类型(如 binary 或 char)用作主键时,这样做可能会引起标识解析问题。

请看下面的示例。 Product 表将大小为 10 的固定长度的列用作主键。 创建一个 EntityKeyAB100 的 Product 对象并将该对象添加到 ObjectContext 中。 将该对象保存到数据库中时,由于键列是固定大小的并且保存的值短于数据库中的大小,因此使用尾随空格填充该键。 随后在查询 EntityKeyAB100 的对象时,将返回具有不同的 EntityKeyAB100 后面有尾随空格)的对象,因为 SQL Server 会将 AB100 与填充的字符串匹配。 实体框架 不会裁剪或填充属性的值。 结果是新对象(具有的 EntityKey 为后面有尾随空格的 AB100)会添加到 ObjectContext 中。

Product p1= new Product 
{ 
    ProductID = "AB100", 
    Description = "New product" 
}; 
// An object with EntityKey "AB100" is added to ObjectContext.  ctx.Products.AddObject(p1); 
// The object is saved in the database with a primary key of 
// "AB100     " because the column is of a fixed size.  ctx.SaveChanges();
// When a query is executed for an object with key "AB100", SQL Server // matches the key to "AB100     ".  The result is that a new object 
// with EntityKey "AB100     " is added to ObjectContext.  Product p2 = ctx.Products.First(p => p.ProductCode == "AB100");  

若要避免这一不希望出现的行为,可以执行以下操作之一:

  • 在数据库中使用可变长度类型代替固定长度类型。

  • 在客户端上使用尾随空格或零填充 EntityKey 的值。 可以使用 PadRight 方法为字符串填充空格。

实体键和添加的对象

创建新实体时,实体框架 会定义临时键并将 IsTemporary 属性设置为 true。 调用 SaveChanges 方法时,实体框架 会分配一个永久的键并将 IsTemporary 属性设置为 false

如果相应列值是在数据库中生成的标识,则将存储模型中实体的 Property 元素的 StoreGeneratedPattern 特性设置为 Identity。 实体数据模型 工具根据现有数据源生成数据模型时,会将 StoreGeneratedPattern 特性添加到每个 Property 元素 (CSDL) 元素,该元素表示数据源中的标识或计算所得的列。 调用 SaveChanges 后,实体框架 将临时键中的属性值替换为由数据源生成的标识值。

下面详细介绍了用包含服务器生成的值的永久键替换临时键的内部过程:

  1. 构造实体对象。

    此时,所有键属性都拥有默认值,即 null 或 0。

  2. 新对象会被添加到 ObjectContext 中,方法是对 ObjectContextObjectSet 调用 AddObject 方法,或者向关系“多”端上的对象集合添加对象。

    此时,实体框架 生成一个临时键,该键用于在 ObjectStateManager 中存储对象。

  3. ObjectContext 调用 SaveChanges

    实体框架 生成一个 INSERT 语句并对数据源执行该语句。

  4. 如果该 INSERT 操作成功,则会将服务器生成的值写回 ObjectStateEntry

  5. ObjectStateEntry 用服务器生成的值更新对象。

  6. ObjectStateEntry 调用 AcceptChanges 时,会使用新的服务器生成的值计算永久 EntityKey

    Dd283139.note(zh-cn,VS.100).gif注意:
    SaveChanges 执行结束时或使用 AcceptAllChangesAfterSave 标记调用 SaveChanges 方法时,会自动调用 AcceptChanges

  7. ObjectStateManager 将临时键的所有实例替换为新的永久键。

GUID 属性值

实体框架 支持返回 Guid 类型以确保唯一性的实体属性。

实体框架 支持服务器生成的 GUID 类型标识值,但提供程序必须能够在插入行之后返回服务器生成的标识值。 从 SQL Server 2005 开始,SQL Server 可以通过 OUTPUT 子句(可能为英文网页)返回服务器生成的 GUID 类型。 如果提供程序不支持 OUTPUT 子句的等效子句,则为客户端上的新对象生成 GUID 值。 为此,建议处理 SavingChanges 事件来为处于 Added 状态的任何实体对象生成新的 GUID 值。 有关更多信息,请参见如何:在保存更改时执行业务逻辑(实体框架)

使用Entity Data Model WizardUpdate Model Wizard生成或更新数据模型时,会为数据源中 uniqueidentifier 类型化的列自动生成实体类型的 GUID 属性。 数据源还可以使用 16 个字节的二进制列存储 GUID 值。 由于这些工具为数据源中的每个二进制列均生成一个二进制属性,因此您必须通过编辑 .edmx 文件手动更新此类列到 GUID 属性的映射。 有关更多信息,请参见How to: Map a GUID Property to a Binary Column

本节内容

如何:创建 EntityKey(实体框架)

另请参见

任务

如何:创建 EntityKey(实体框架)

其他资源

Entity Data Model Tools