如何通过 EntityFramework 仅更新实体的已更改属性

bai*_*rog 6 c# entity-framework sql-update

比方说,我们有一个MyDbContextDbSet<MyEntity>它。我正在尝试实现以下场景(因为我发现它被称为断开连接的场景或断开连接的模式):

  1. List<MyEntity>从数据库中获取(它可以只是DbContext.MyEntities.ToList()通过某些过滤条件的所有记录或记录 - 没关系)。
  2. 然后这些实体被传递到一些用户可以更改属性的 UI。他的行为是不可预测的:他可以改变每个实体的每一个属性,或者他只能改变单个实体的一个属性(他甚至可以根本不做任何改变)。
  3. 之后,我需要将更新传递给数据库。

为了实现该场景,我为我找到了两种可能的解决方案:

1)在整个场景中保持连接打开。所以它会像这样工作:

MyDbContext.Database.Connection.Open();
List<MyEntity> EntitiesList = MyDbContext.MyEntities.ToList();

//passing EntitiesList to the UI, where user can change them
EntitiesList[0].SomeStringProperty = "123";
//change some other properties here

//saving changes to database some time later
MyDbContext.SaveGhanges();
MyDbContext.Database.Connection.Close();
Run Code Online (Sandbox Code Playgroud)

但是在此解决方案中,数据库连接持续打开时间过长。UI 工作时不应打开它(例如,因为用户可以在几个小时内更改实体)。

2) 获取后关闭连接List<MyEntity>并在将更新传递到数据库时再次打开连接。所以它会像这样工作:

MyDbContext.Database.Connection.Open();
List<MyEntity> EntitiesList = MyDbContext.MyEntities.ToList();
MyDbContext.Database.Connection.Close();

//passing EntitiesList to the UI, where user can change them
EntitiesList[0].SomeStringProperty = "123";
//change some other properties here

//saving changes to database some time later
MyDbContext.Database.Connection.Open();
foreach(var entity in EntitiesList)
{
    //Attach updated entity and make context know that it is modified
    MyDbContext.MyEntities.Attach(entity);
    MyDbContext.MyEntities(entity).State = EntityState.Modified;
}
MyDbContext.SaveGhanges();
MyDbContext.Database.Connection.Close();
Run Code Online (Sandbox Code Playgroud)

但是在这个解决方案中,我认为所有实体的所有属性都被修改了。这会导致巨大的数据库更新开销。

另一种选择是编写MyEntityWrapper类来跟踪用户执行的所有更改,并知道哪些属性需要更新。所以我可以将上面的代码更改为:

foreach(var entity in EntitiesList)
{
    //Attach updated entity and make context know which properties are modified
    MyDbContext.MyEntities.Attach(entity);
    MyDbContext.Entry(entity).Property(e => e.SomeStringProperty).IsModified = true;
    //mark other changed properties
}
Run Code Online (Sandbox Code Playgroud)

那么 - 有没有更优雅的解决方案,我可以只更新更改的属性而不编写MyEntityWrapper类吗?我可以简单地告诉 DbContext:“这里有一些List<MyEntity>- 接受它,确定哪些属性与数据库中的值不同并更新这些值”?

顺便提一句。如果重要 - 我正在通过Devart dotConnect for SQLite + EntityFramework 6.0.1使用 SQLite 数据库

小智 5

请看看下面的代码,我认为它可以帮助你(在谷歌上找到)

public virtual void Update(T entity, params Expression<Func<T, object>>[] updatedProperties)
{
    //Ensure only modified fields are updated.
    var dbEntityEntry = DbContext.Entry(entity);
    if (updatedProperties.Any())
    {
        //update explicitly mentioned properties
        foreach (var property in updatedProperties)
        {
            dbEntityEntry.Property(property).IsModified = true;
        }
    }
    else{
        //no items mentioned, so find out the updated entries
        foreach (var property in dbEntityEntry.OriginalValues.PropertyNames)
        {
            var original = dbEntityEntry.OriginalValues.GetValue<object>(property);
            var current = dbEntityEntry.CurrentValues.GetValue<object>(property);
            if (original != null && !original.Equals(current))
                dbEntityEntry.Property(property).IsModified = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)