在我看来,这个异常表明您的调用代码中有问题 - 或者至少不寻常。
currentObject
是一个附加实体,同时sourceObject
是(通常)一个独立的对象(不一定是实体),应该具有相同的键值(或根本没有键属性)。
事实上,设置当前值的工作方式与DbContext
因为您必须显式提供当前附加实体才能更新其当前值。使用ApplyCurrentValues
of ObjectContext
您不提供该实体:
objectContext.ApplyCurrentValues("MyEntitySet", sourceObject);
这是不同的,因为...
-
sourceObject
必须是一个实体,不能是任意的object
- 它更新具有相同键值的附加实体的值
sourceObject
在您的示例中,它将更新另一个实体currentObject
因为显然currentObject
不是具有相同密钥的实体sourceObject
.
如果您使用过ObjectStateEntry.ApplyCurrentChanges
(这更接近新版本DbContext
)你会得到同样的异常:
var objectContext = ((IObjectContextAdapter)obj).ObjectContext;
var entry = objectContext.ObjectStateManager.GetObjectStateEntry(currentObject);
entry.ApplyCurrentValues(sourceObject);
EF 会在这里抱怨您尝试更改键值。它会抱怨如果sourceObject
与以下类型不同currentObject
while DbContext
将允许(这使得程序DbContext
在我看来更有用,因为您可以使用具有匹配属性名称的任意对象(例如 DTO)来更新实体)。
Edit
重现您使用 EF 4 的方法的主要问题是使用 EF 5/ 的实体DbContext
不源自EntityObject
但都是 POCO。因为这个你没有EntityKey
可用,将允许此方法的通用实现。
您可以做的是引入一个标记实体关键属性的接口,例如:
public interface IEntity
{
int Id { get; set; }
}
您的实体类将实现此接口,例如Order
entity:
public class Order : IEntity
{
public int Id { get; set; }
public DateTime ShippingDate { get; set; }
// etc.
}
您可以创建一个带有此接口约束的通用方法:
public static T ReAttach<T>(DbContext context, T objectDetached)
where T : class, IEntity
{
T original = context.Set<T>().Find(objectDetached.Id);
if (original == null)
throw new ObjectNotFoundException();
context.Entry(original).CurrentValues.SetValues(objectDetached);
return objectDetached;
}
如果您的实体并不总是有int
财产Id
但它们的键具有不同的类型、名称或者可以是复合的,这可能是将实体的键传递到方法中而不是使用接口的更简单的方法:
public static T ReAttach<T>(DbContext context, T objectDetached,
params object[] keyValues) where T : class
{
T original = context.Set<T>().Find(keyValues);
if (original == null)
throw new ObjectNotFoundException();
context.Entry(original).CurrentValues.SetValues(objectDetached);
return objectDetached;
}