如何阻止dbentityentry.currentvalues.setvalues尝试更改entitykey值

时间:2013-05-20 09:56:34

标签: c# entity-framework-5

我使用以下代码使用从我的代码收集的新信息更新实体对象。我正在使用实体框架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()代码?

  3. ---- ---- UPDATE

    我之前用于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;
        }
    }
    

1 个答案:

答案 0 :(得分:3)

在我看来,这个例外表明你的主叫代码中出现了错误 - 或者至少是不正常的。

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

确实设置当前值与DbContext的工作方式不同,因为您必须显式提供当前附加的实体才能更新其当前值。使用ApplyCurrentValues 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会在这里抱怨您尝试更改键值。如果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;
}