'Best Way to Update only modified fields with Entity Framework

Currently I am doing like this:

For Example:

public update(Person model)
{
    // Here model is model return from form on post
    var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
    db.Entry(oldobj).CurrentValues.SetValues(model);
}

It works, but for example,

I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)

I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".

Just wondering is there any elegant way to do this with less effort and optimal performance?



Solution 1:[1]

I swear by EntityFramework.Extended. Nuget Link

It lets you write:

db.Person
  .Where(x => x.ID == model.ID)
  .Update(p => new Person() 
  {
    Name = newName,
    EditCount = p.EditCount+1
  });

Which is very clearly translated into SQL.

Solution 2:[2]

I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.

I have provided my code sample below; Great if it can help someone else

// Here 
// collection = FormCollection from Post
// model = View Model for Person

var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
    List<string> formcollist = new List<string>();
    foreach (var key in collection.ToArray<string>())
    {
        // Here apply your filter code to remove system properties if any
        formcollist.Add(key);
    }
    foreach (var prop in result.GetType().GetProperties())
    {
            if( formcollist.Contains(prop.Name))
            {
                    prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null)); 
            }
    }
    db.SaveChanges();
}

Solution 3:[3]

Please try this way

public update(Person model)
{
    // Here model is model return from form on post
    var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();

    // Newly Inserted Code
    var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);

    db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}

public static object CheckUpdateObject(object originalObj, object updateObj)
{
   foreach (var property in updateObj.GetType().GetProperties())
   {
      if (property.GetValue(updateObj, null) == null)
      {
         property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
         .GetValue(originalObj, null));
      }
   }
   return updateObj;
}

Solution 4:[4]

This is way I did it, assuming the new object has more columns to update that the one we want to keep.

                if (theClass.ClassId == 0)
                {
                    theClass.CreatedOn = DateTime.Now;

                    context.theClasses.Add(theClass);
                }
                else {
                    var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
                        .Select(c => new TheClasses {
                            CreatedOn = c.CreatedOn
                            // Add here others fields you want to keep as the original record
                        }).FirstOrDefault();
                    theClass.CreatedOn = currentClass.CreatedOn;

                    // The new class will replace the current, all fields
                    context.theClasses.Add(theClass);
                    context.Entry(theClass).State = EntityState.Modified;
                }
                context.SaveChanges();

Solution 5:[5]

I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:

        // load entity without tracking
        var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID); 
        var entityInit = CopyEntity(entityWithoutTracking);

        // do business logic and change entity
        entityWithoutTracking.surname = newValue;

        // for saving, find entity in context
        var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
        var entry = Context.Entry(entity);
        entry.CurrentValues.SetValues(entityWithoutTracking);
        entry.State = EntityState.Modified;

        // get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
        var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
        foreach (var checkProperty in changedPropertiesList)
        {
            try
            {
                var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
                var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
                if ((p1 == null && p2 == null) || p1.Equals(p2))
                {
                    entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
                    entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
                }
            } catch(Exception) { }
        }
        Context.SaveChanges(); // only surname will be updated

Solution 6:[6]

No Worry guys Just write raw sql query

db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 10K35H 5H4KY4
Solution 3
Solution 4 Chris Rosete
Solution 5 Julian Müller
Solution 6 Chandra shekhar menaria