15

如果实体在 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 该实体处于修改状态?

4

2 回答 2

29

如果您Attach在已更改的实体上使用该方法,您还需要在附加实体后告诉 EF 该实体已修改

context.Specifications.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();

另一种方法是获取(带跟踪),然后更新字段并保存:

var entity = context.Specifications.First(s => s.Id == 1234);
entity.Name = "Foo";
... other changes here
context.SaveChanges();

另一种选择是在重新附加实体对其进行更改,例如按照此处

context.Specifications.Attach(entity);
entity.Name = "Foo";
... other changes here
context.SaveChanges();

编辑

您可以将泛型与 DbSet(类或方法)一起使用,如下所示:

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

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

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

但是,对于更高效的场景,我们需要遍历图,检测更改,然后添加新插入的、更新现有的、忽略未更改的以及从Context.

Slauma在这里展示了一个很好的例子。

您可能想看看使用GraphDiff,它可以为您完成所有这些工作!

于 2012-09-06T09:17:01.427 回答
0

对于断开连接的实体,我找到了这个解决方案

要查找现有实体的更改:

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

于 2021-01-12T15:58:26.217 回答