Linq 到实体:实体、EntityKey、附加对象和新对象的混合图
-
06-07-2019 - |
题
在我的数据模型中,我的对象/表/数据之间有一个相当常见的划分:
- 代表系统正在完成的工作的“事务”实体。这些实体是由系统创建的,仅在特定上下文中才重要。它们通常是即时创建的。(在旁边:这种类型的实体有合适的名称吗?)
- 代表事务实体的公共属性的“数据字典”实体。它们的定义不规则(主要是在项目开始时),并且具有更加静态的生命周期。它们通常是由我创建的。
例如,我可能有一个 User (事务)实体和一个 UserType (数据字典)实体。
我想将对数据字典实体实例的引用(硬)编码到我的代码中。这使我能够使用易于理解的语言编写业务逻辑。(例如,我可能有一个 UserType 为“Plain”和一个 UserType 为“Admin”,然后有一条规则“仅当用户的 UserType 等于 Admin 时才允许访问”)。
我使用 LINQ-to-Entities 作为我的数据访问技术/ORM。为了实现数据字典引用,我存储了 EntityKey。我的理解是它们与对象上下文分离,因此适合此目的。(因为它们不包含实体状态,所以我也不必担心该状态会过时。)
然而,当我尝试使用 DD-EntityKey-reference 添加新的事务实体时,这给我带来了问题。继续我的例子,我正在这样做:
UserEntities userEntities = new UserEntitites()
User user = new User()
user.UserType = new UserType()
user.UserType.EntityKey = adminEntityKey
userEntities.AddToUser(user)
...这给了我以下错误:
系统.InvalidOperationException:该对象无法添加到 ObjectStateManager,因为它已经具有 EntityKey。使用 ObjectContext.Attach 附加具有现有键的对象。
如果我尝试调用 userEntities.Attach(user) 而不是 AddToUser,我会得到以下结果:
系统.InvalidOperationException:EntityKey 值为空的对象无法附加到对象上下文。
考虑到我正在做的新实体和现有实体的混合,这两个错误都是有道理的。我不确定如何解决这个问题。有什么方法可以分离对 DD 实体的引用并将它们分配给新的附加对象,而不需要我加载整个 DD 实体状态?
解决方案
我将尝试解释为什么您会遇到这些异常:
系统.InvalidOperationException:该对象无法添加到 ObjectStateManager,因为它已经具有 EntityKey。使用 ObjectContext.Attach 附加具有现有键的对象。
创建新的 User 和 UserType 实体。UserType 上的 EntityKey 已设置,但当您将其传递给 userEntities.Add() 时,EF 会尝试将它们设置为 ADDED 状态。由于 UserType 有 EntityKey EF 意识到出现了问题并抛出异常。
系统.InvalidOperationException:EntityKey 值为空的对象无法附加到对象上下文。
在此异常中,UserType 一切正常,但抛出异常是因为 User 实体 - 它没有 EntityKey,因此无法附加。
这就是我解决这个问题的方法(如果,正如你所说,数据字典引用的硬编码是可以的):
在 UserType 实体上,我将创建对 UserType id 的静态引用:
public partial class UserType
{
public const int AdminUserTypeID = 1;
public const int PlainUserTypeID = 2;
}
在 User 实体上,将有扩展器属性,以便更轻松地访问 UserType 外键:
public partial class User
{
public int? UserTypeID
{
set
{
UserTypeReference.EntityKey = new EntityKey("UserEntities.UserType", "UserTypeID", value);
}
get
{
if (UserTypeReference.EntityKey == null)
return null;
if (UserTypeReference.EntityKey.EntityKeyValues.Count() > 0)
return (int)UserTypeReference.EntityKey.EntityKeyValues[0].Value;
else
return null;
}
}
}
最后,创建新用户的用例如下所示:
using(UserEntities userEntities = new UserEntitites())
{
User user = new User();
user.UserTypeID = UserType.AdminUserTypeID;
userEntities.AddToUser(user);
userEntities.SaveChanges()
}