如何阻止dbentityentry.currentvalues.setvalues尝试更改entitykey值
我使用以下代码使用从我的代码收集的新信息更新实体对象。 我正在使用Entity Framework 5 。
我使用以下扩展方法(作为我以前在EF4中使用的重新附加代码的替代方法):
public static void ApplyValues(this object currentObject, object sourceObject, System.Data.Entity.DbContext obj) { obj.Entry(currentObject).CurrentValues.SetValues(sourceObject); }
问题是,当调用此方法时, SetValues
方法尝试修改附加对象上的EntityKey
值(显然我不希望它这样做),因此它会抛出错误。
我想这里有两个问题:
-
有没有办法阻止它尝试更新键值?
-
如果不是如何复制过去在EF4中正常工作的
ObjectContext.ApplyCurrentValues()
代码?
—- —-更新
我之前用于EF4的代码如下:
public static System.Data.Objects.DataClasses.EntityObject ReAttach(this System.Data.Objects.ObjectContext obj, System.Data.Objects.DataClasses.EntityObject objectDetached) { if (objectDetached.EntityKey != null) { object original = null; if (obj.TryGetObjectByKey(objectDetached.EntityKey, out original)) { objectDetached = obj.ApplyCurrentValues(objectDetached.EntityKey.EntitySetName, objectDetached); return objectDetached; } else { throw new ObjectNotFoundException(); } } else { return objectDetached; } }
在我看来,这个例外表明你的调用代码中出现了错误 – 或者至少是不正常的。
currentObject
是一个附加实体,而sourceObject
(通常)是一个分离的对象(不一定是实体),它应具有相同的键值(或根本没有键属性)。
实际上,设置当前值与DbContext
工作方式不同,因为您必须显式提供当前附加的实体才能更新其当前值。 使用ObjectContext
ApplyCurrentValues
,您不提供此实体:
objectContext.ApplyCurrentValues("MyEntitySet", sourceObject);
这是不同的,因为……
-
sourceObject
必须是实体,不能是任意object
- 它更新与
sourceObject
具有相同键值的附加实体的值
在您的示例中,它将更新除currentObject
之外的另一个实体,因为显然currentObject
不是与sourceObject
具有相同键的实体。
如果您使用了ObjectStateEntry.ApplyCurrentChanges
(它更接近DbContext
的新版本),您将获得相同的exception:
var objectContext = ((IObjectContextAdapter)obj).ObjectContext; var entry = objectContext.ObjectStateManager.GetObjectStateEntry(currentObject); entry.ApplyCurrentValues(sourceObject);
EF会在这里抱怨您尝试更改键值。 如果sourceObject
与currentObject
类型不同,它将会抱怨,而DbContext
允许这样做(这使得使用DbContext
的过程在我看来更有用,因为你可以使用具有匹配属性名称的任意对象 – 例如DTO – 来更新实体)。
编辑
重现与EF 4一起使用的方法的主要问题是具有EF 5 / DbContext
实体不是从EntityObject
派生的,而是POCO。 因此,您没有可用的EntityKey
,允许此方法的通用实现。
你可以做的是引入一个标记你的实体的关键属性的接口,例如:
public interface IEntity { int Id { get; set; } }
您的实体类将实现此接口,例如Order
实体:
public class Order : IEntity { public int Id { get; set; } public DateTime ShippingDate { get; set; } // etc. }
您可以使用此接口的约束创建generics方法:
public static T ReAttach(DbContext context, T objectDetached) where T : class, IEntity { T original = context.Set ().Find(objectDetached.Id); if (original == null) throw new ObjectNotFoundException(); context.Entry(original).CurrentValues.SetValues(objectDetached); return objectDetached; }
如果您的实体并不总是具有int
属性Id
但它们的键具有不同的类型,名称或者可能是复合的,则可能是将实体的键传递给方法而不是使用接口的更简单方法:
public static T ReAttach(DbContext context, T objectDetached, params object[] keyValues) where T : class { T original = context.Set ().Find(keyValues); if (original == null) throw new ObjectNotFoundException(); context.Entry(original).CurrentValues.SetValues(objectDetached); return objectDetached; }