如何停止dbentityentry.currentvalues.setvalues试图改变entitykey值

本文关键字:改变 entitykey setvalues 何停止 dbentityentry currentvalues | 更新日期: 2023-09-27 18:14:19

我使用下面的代码用从我的代码中收集的新信息更新实体对象。我正在使用实体框架5

我使用以下扩展方法(作为我在EF4中使用的重新连接代码的替代方法):

public static void ApplyValues(this object currentObject, object sourceObject, System.Data.Entity.DbContext obj)
{
  obj.Entry(currentObject).CurrentValues.SetValues(sourceObject);
}

问题是,当这个方法被调用SetValues方法试图修改附加对象上的EntityKey值(显然我不希望它这样做),所以它抛出一个错误。

我想这里有两个问题:

  1. 是否有办法防止它试图更新键值?

  2. 如果不是,我如何复制过去在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;
    }
}

如何停止dbentityentry.currentvalues.setvalues试图改变entitykey值

在我看来,这个异常表明在你的调用代码中有些地方是错误的——或者至少是不寻常的。

currentObject是一个附加的实体,而sourceObject(通常)是一个分离的对象(不一定是实体),应该具有相同的键值(或根本没有键属性)。

设置当前值确实与DbContext不同,因为您必须显式地提供当前附加实体以更新其当前值。使用ApplyCurrentValuesObjectContext,您不提供此实体:

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在这里会抱怨您试图更改键值。如果sourceObjectcurrentObject不是同一类型,它会抱怨,而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.
}

您可以为这个接口创建一个带有约束的泛型方法:

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;
}