Only update the changes that are different

I have an Entity-Set employee_table

, I get the data through an excel sheet that I have loaded into memory and the user clicks Save

to save the changes to the db, which is all good for the first time inserting records and no problem with that.

but how can I update only the changes that have been made? this means that let's say I have 10 rows and 5 columns and out of 10 rows it says the row of the 7th has been changed and from 5 columns it can be said that the third column has been changed and I just need to update only these change and keep the existing value of the other columns.

I can do validation if (myexistingItem.Name != dbItem.Name) { //update }

, but its very tedious and ineffective and I'm sure there is a better way to handle it.

here is what i got so far.

var excelData = SessionWrapper.GetSession_Model().DataModel.OrderBy(x => x.LocalName).ToList();;
var dbData = context.employee_master.OrderBy(x => x.localname).ToList();

employee_master = dbEntity = employee_master();

if (dbData.Count > 0)
{
   //update
   foreach (var dbItem in dbData)
   {
      foreach(var xlData in excelData)
      {
         if(dbItem.customer == xlData.Customer)
         {
            dbEntity.customer = xlData.Customer;
         }
         //...do check rest of the props....
         db.Entry(dbEntity).State = EntityState.Modified;
         db.employee_master.Add(dbEntity);
      }
   }

   //save
   db.SaveChanges();
}
else
{
  //insert
}

      

+2


source to share


2 answers


You can make this check more general with reflection.

Using this answer to get the value by property name.

public static object GetPropValue(object src, string propName)
{
    return src.GetType().GetProperty(propName).GetValue(src, null);
}

      

Using this answer to set the value by property name.



public static void SetPropertyValue(object obj, string propName, object value)
{
    obj.GetType().GetProperty(propName).SetValue(obj, value, null);
}

      

And this answer will list all properties

public static void CopyIfDifferent(Object target, Object source)
{
    foreach (var prop in target.GetType().GetProperties())
    {
        var targetValue = GetPropValue(target, prop.Name);
        var sourceValue = GetPropValue(source, prop.Name);
        if (!targetValue.Equals(sourceValue))
        {
            SetPropertyValue(target, prop.Name, sourceValue);
        }
    }
}

      

Note . If you need to exclude some properties, you can implement very simply by passing a list of properties to a method, and you can exclude it from if

.

+4


source


Update:

I am updating this answer to provide a little more information on why I am not going to use a manual reflection based solution for now; I also want to clarify that there is nothing wrong with such a solution as such, once you have determined that it is in line with the bill.

  • First of all, I am assuming from the code that this is a work in progress and therefore not completed. In this case, I feel like the code doesn't require more complexity before it's done, and the manual reflection method is more code to write, test, debug, and maintain.

  • For example, right now you seem to have a situation where there is a simple simple 1: 1 simple copy from data to excel for data in an object employee_master

    . So reflection seems useless in this case as it saves you a lot of boring manual property assignments.

  • But what happens when HR (or whoever is using this app) comes back to you asking: if the X field is blank in the Excel sheet, copy the value "Blank" to the target field, Friday, in which case copy the value "NA".

  • Now a generic solution has to combine custom business logic and can become cumbersome. I've been in this situation, and if you're not very careful it will end up in a mess in the end.

  • I just wanted to point it out - and recommend at least taking a look at Automapper because it already provides one very proven way to solve your problem.

In terms of efficiency, they are only mentioned because the question mentioned is mentioned, and I would like to point out that there is higher inefficiency in the code than in the case of inefficiency of manually adjusting 40+ property assignments, or really only care about updating the changed fields.

Why not rewrite the loop:



foreach (var xlData in excelData)
{
    //find existing record in database data:
    var existing = dbData.FirstOrDefault(d=>d.customer==xlData.Customer);
    if(existing!=null)
    {
        //it already exists in database, update it
        //see below for notes on this.

    }
    else
    {
        //it doesn't exist, create employee_master and insert it to context
        //or perform validation to see if the insert can be done, etc.
    }
    //and commit:
    context.SaveChanges();
}

      

This avoids the initial if(dbData.Count>0)

one because you will always insert some row from the excel sheet that doesn't have a matching record in dbData

, so you don't need a separate block of code for the first- time. It is also more efficient than the current loop because right now you are iterating over every object in dbData for every object in xlData; this means that if you have 1000 items, you have a million iterations ...

Notes on the update process and overall performance

(Note: I know the question was not about efficiency, but since you mentioned this in the context of copying properties, I just wanted to give some food for thought)

  • Unless you are building a system that needs to do this operation on multiple objects, I would caution you against adding complexity to your code by creating a copy of reflection-based properties.
  • If you count the number of properties you have to copy (that is, the number of type statements foo.x = bar.x

    ), then consider the code needed to provide a robust, fully tested and provably efficient copy of reflection-based properties (i.e. with a built-in cache, so you don't need to constantly override type properties, a mechanism to allow you to specify exceptions, handling for edge cases where for some unknown reason you find that for a random column X the value is "null" in some cases, etc.), you may find that the first is actually significantly less work :)
  • Remember that even the fastest reflection based solution will always be slower than a good old fashioned assignment foo.x = bar.x

    .
  • Anyway, if you need to perform this operation on 10 or 20 separate objects, consider the general case, otherwise my advice would be to manually write down the property copy assignments, correct them correctly, and then think about generalizing - or look at Automapper , eg.
  • In terms of changing only the updated field, I'm not sure what you even need. If a record exists in the database and the user has just submitted a copy of that record, which they claim is the "correct" version of that object, simply copy all the values ​​they gave and store them in the database.
  • The reason I say this is because, in all likelihood, the efficiency of just sending, for example, 4 modified fields versus 25 or any other, pales in comparison to the overhead of the actual round trip to the database itself; I would be surprised if you could see a significant performance increase in these kinds of operations without dispatching all columns - unless, of course, all columns NVARCHAR(MAX)

    or something :)

  • If there is a concurrency problem (i.e. other uses may modify the same data) then include the type column ROWVERSION

    in the database table, map it to Entity Framework

    and handle the concurrency if and when they occur.

+3


source







All Articles