bairog
bairog

Reputation: 3373

Howto update only changed properties for an entity via EntityFramework

Let's say we have a MyDbContext having DbSet<MyEntity> in it. I'm trying to implement the following scenario (as I found out it is called disconnected scenario or disconnected mode):

  1. I obtain List<MyEntity> from database (it can be simply all records via DbContext.MyEntities.ToList() or records by some filter condition - it doesn't matter).
  2. Then those entities are passed to some UI where user can change properties. His behaviour is unpredictable: he can change every property of every entity or he can change only one property of a single entity (he even can perform no changes at all).
  3. After that I need to pass the updates to database.

To implement that scenario I've found for me two possible solutions:

1) Keep connection open during the whole scenario. So it will work like this:

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();

But in this solution database connection persists open too long. It shouldn't be open when UI is working (because user can change entities during several hours for example).

2) Close connection after obtaining List<MyEntity> and open connection again when passing updates to database. So it will work like this:

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();

But in this solution I think that absolutely all properties of all entites were modified. It results in huge database update overhead.

Another choice will be to write MyEntityWrapper class which will track all changes performed by user and will know which properties needs to be updated. So I can change my code above to this one:

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
}

So - is there some more elegant solution, can I update only changed properties without writing MyEntityWrapper class? Can I simply tell DbContext: "here is some List<MyEntity> - take it, determine which properties differs from that values in a database and update those values"?

BTW. If it matters - I'm working with SQLite database via Devart dotConnect for SQLite + EntityFramework 6.0.1

Upvotes: 7

Views: 8671

Answers (1)

OnDoubt
OnDoubt

Reputation: 129

Please have a look on below code, I think it may help you (Found out on Google )

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;
        }
    }
}

Upvotes: 5

Related Questions