如何更新在DbContext之外修改的实体?

时间:2012-09-06 09:14:45

标签: c# entity-framework entity-framework-4 code-first

如果在DbContext之外更改实体(是一个分离的实体),我在更新实体方面遇到了一个小问题。如果我附加修改后的实体,则其状态不会被修改。

我的代码如下所示:

var specificationToSave = GetSpecificationFromTmpStore(userSessionGuid);
using (var context = DataContextFactory.GetDataContext())
{
    // this works for update, if I change the values inside the context while debugging
    // but it breaks with new entities
    context.Specifications.Attach(specificationToSave);

    // this works for insert new entities, modified entities will be saved as new entities
    context.Specifications.Add((specificationToSave);)
    context.SaveChanges();
}

我知道NHibernate,它的方法是SaveOrUpdate。如果更新或插入实体,NHibernate会因为值而决定。

使用EF 4.x以及在DbContext之外修改的实体执行此操作的最佳做​​法是什么? 如何告诉EF该实体处于修改状态?

2 个答案:

答案 0 :(得分:23)

在附加实体后,您还需要告诉EF实体是modified

context.Entry(specificationToSave).State = EntityState.Modified;

或者,您可以在重新附加之后对实体进行更改,例如见MVC3 with EF 4.1 and EntityState.Modified

修改

您可以将泛型与DbSet一起使用 - 无论是类还是方法 - 如下所示:

    public void Update<TEntity>(TEntity entity)
    {
        DbContext.Set<TEntity>().Attach(entity);
        DbContext.Entry(entity).State = EntityState.Modified;
        DbContext.SaveChanges();
    }

编辑:用于更新已分离的父/子图

为了更新效率和性能不重要的简单/浅层父子关系,简单地删除所有旧孩子并重新插入新孩子是一个简单(虽然难看)的解决方案。

但是,对于更有效的方案,要求我们遍历图表,检测更改,然后添加新插入,更新现有,忽略未更改,并从Context删除已删除的项目。

Slauma展示了一个很好的例子of this here

您可能希望使用GraphDiff,这可以为您完成所有这些工作!

答案 1 :(得分:0)

对于断开连接的实体,我发现了这个 solution

用于查找现有实体的更改:

var existing = context.Find<Item>(1);
if (existing != null) 
{ 
   context.Entry(existing).CurrentValues.SetValues(changed);
}

之后它的 EntityState 将是 Modified,但仅限于有实际更改的地方。

我在单元/集成测试中所做的完整示例:

await using var context1 = new MyContext(new DbContextOptionsBuilder().UseSqlite("Data Source=demo.db").Options);
await context1.Database.EnsureDeletedAsync();
await context1.Database.EnsureCreatedAsync();

await context1.Items.AddAsync(new Item
{
    Id = 1,
    Name = "Something to start with"
});

await context1.SaveChangesAsync();

await using var context2 = new MyContext(new DbContextOptionsBuilder().UseSqlite("Data Source=demo.db").Options);
var existing = context2.Find<Item>(1);

var entry = context2.Entry(existing);
entry.CurrentValues.SetValues(new Item
{
    Id = 1,
    Name = "Something to start with"
});
entry.State.Should().Be(EntityState.Unchanged);

entry.CurrentValues.SetValues(new Item
{
    Id = 1,
    Name = "Updated now."
});
entry.State.Should().Be(EntityState.Modified);

使用 Microsoft.EntityFrameworkCore.SqliteFluentAssertions