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

C# 如何更新在DbContext之外修改的实体?,c#,entity-framework,entity-framework-4,code-first,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 th

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

如果您对已更改的实体使用
附加方法,您还需要告知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();
另一种选择是在重新附着实体后对其进行更改,例如

编辑

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

public void Update<TEntity>(TEntity entity)
{
    DbContext.Set<TEntity>().Attach(entity);
    DbContext.Entry(entity).State = EntityState.Modified;
    DbContext.SaveChanges();
 }
公共作废更新(TEntity实体)
{
DbContext.Set().Attach(实体);
DbContext.Entry(entity.State=EntityState.Modified;
DbContext.SaveChanges();
}
编辑:用于更新分离的父/子图形

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

但是,为了实现更高效的场景,我们需要遍历图形,检测更改,然后添加新插入的、更新现有的、忽略未更改的,以及从
上下文中删除已删除的项

Slauma就是一个很好的例子


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

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

要查找现有实体上的更改,请执行以下操作:

var existing=context.Find(1);
if(现有!=null)
{ 
context.Entry(现有).CurrentValues.SetValues(已更改);
}
其实体状态将在之后进行修改,但仅在发生实际更改的情况下

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

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);
wait using var context1=new MyContext(new DbContextOptionsBuilder().UseSqlite(“Data Source=demo.db”).Options);
等待context1.Database.EnsureDeletedAsync();
等待context1.Database.EnsureCreatedAsync();
等待context1.Items.AddAsync(新项
{
Id=1,
Name=“开始的东西”
});
wait context1.SaveChangesAsync();
等待使用var context2=new MyContext(new DbContextOptionsBuilder().UseSqlite(“数据源=demo.db”).Options);
var existing=context2.Find(1);
var条目=context2.entry(现有);
entry.CurrentValues.SetValues(新项
{
Id=1,
Name=“开始的东西”
});
entry.State.Should().Be(EntityState.Unchanged);
entry.CurrentValues.SetValues(新项
{
Id=1,
Name=“立即更新。”
});
entry.State.Should().Be(EntityState.Modified);

使用和。

非常感谢:-)这就是我要找的。第二种方法是最好的,但在这种情况下是不可能的。所以我必须直接更新DbEntityEntry的状态。这不是一个很好的解决方案,但它可以工作。如果a必须设置“specificationToSave”的每个相关实体的状态,这是真的吗。例如,如果我在“specificationToSave”中有一个修改项目列表。我真的希望有一个简单的解决方案来做到这一点…@Jürgen parent:儿童场景需要特别考虑,因为您可能需要添加、删除或更新儿童。FWIW I通常只对子项进行哈希,查看是否有更改,如果有,然后删除所有现有的子项并再次插入所有子项。但这只是我的懒惰。谢谢你的一般解决方案。但是,是否可以以一种方式附加和修改实体和所有相关实体的状态?我的“specificationToSave”有一些其他实体的列表,这些实体也被修改。我真的必须遍历所有子实体才能将状态设置为“已修改”吗?在执行context.Entry(entity).state=EntityState.modified;,操作时,不必告诉EF状态已修改并附加它;,您不仅将实体附加到DbContext,还将整个实体标记为dirty,下面将进一步解释:@StuartLC
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);