EntityFramework:使用分离的实体更新单个字段

时间:2011-08-31 18:57:36

标签: asp.net entity-framework entity-framework-4.1

与通常情况不同,我的代码实际上有效,但我想知道它是否是唯一(或最佳方法)。

基本的想法是我有一个现有的应用程序,手工数据层被移植到Entity Framework。作为最小化代码更改的折衷方案,我正在使用现有方法,这些方法往往采用更加不连贯的方法。例如,我有很多这样的事情:

UpdateNote(int noteId, string note)

我似乎有一种适用于此类更新的方法,无需重新获取:

var context = new MyEntities();
context.Configuration.ValidateOnSaveEnabled = false;
var note = new Model.Note{ Id = noteId, Note = ""};
context.Notes.Attach(note);
note.Note = "Some Note";
context.SaveChanges();

它有点难看(虽然简洁),所以我想知道是否有更好的方法与EF一起使用?除了丢失内置验证之外,此方法有任何缺点吗?

这是一个将在我的应用程序中使用的模式。

2 个答案:

答案 0 :(得分:3)

DbContext的以下扩展方法是一种避免使用与要将其更改为的值不同的值初始化实体的方法。

public static class EFExtensions
{
    public static void MarkAsModified(this DbContext context, object entity,
        params string[] properties)
    {
        foreach (var property in properties)
            context.Entry(entity).Property(property).IsModified = true;
    }
}

然后您可以这样使用它:

var context = new MyEntities();
context.Configuration.ValidateOnSaveEnabled = false;

var note = new Model.Note { Id = noteId }; // only key properties required to set

note.Note = "Some Note";
note.SomeOtherProperty = 1234;
note.AndAnotherProperty = "XYZ";

context.Notes.Attach(note);
context.MarkAsModified(note, "Note", "SomeOtherProperty" , "AndAnotherProperty");

context.SaveChanges();

注意:这仅适用于标量属性,而不适用于导航属性。

除了验证之外,我可以想象这种方法对于正确的并发检查是有问题的。

修改

根据@Adam Tuliper的评论,下面的并发可能不是问题,因为当手动将实体附加到上下文(不从数据库中读取)并标记为已修改以向其发送UPDATE命令时,将跳过并发检查数据库。它只是覆盖了DB中的最新版本。感谢Adam指出这一点!

答案 1 :(得分:2)

请参阅以下用于轻松将断开连接的对象附加回图表的代码,假设我们现在要保存它。


public static class EntityFrameworkExtensions
{
    /// <summary>
    /// This class allows you to attach an entity.
    /// For instance, a controller method Edit(Customer customer)
    /// using ctx.AttachAsModified(customer); 
    /// ctx.SaveChanges();
    /// allows you to easily reattach this item for udpating.
    /// Credit goes to: http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx
    /// </summary>
    public static void AttachAsModified<T>(this ObjectSet<T> objectSet, T entity) where T : class
    {
        objectSet.Attach(entity);
        objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }

    /// <summary>
    /// This marks an item for deletion, but does not currently mark child objects (relationships).
    /// For those cases you must query the object, include the relationships, and then delete.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="objectSet"></param>
    /// <param name="entity"></param>
    public static void AttachAsDeleted<T>(this ObjectSet<T> objectSet, T entity) where T : class
    {
        objectSet.Attach(entity);
        objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);
    }

    public static void AttachAllAsModified<T>(this ObjectSet<T> objectSet, IEnumerable<T> entities) where T : class
    {
        foreach (var item in entities)
        {
            objectSet.Attach(item);
            objectSet.Context.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
        }
    }
}