仅更新不同的更改

我有一个Entity-Set employee_table ,我通过excel表获取数据,我已经加载到内存中,用户将单击Save保存数据库中的更改,并且它首次插入记录并且没有问题接着就,随即。

但是如何仅更新所做的更改? 意思是,假设我有10行和5列,10行中说第7行被修改,5列中的第3列被修改,我只需要更新那些更改并保留其他值的现有值列。

我可以检查if (myexistingItem.Name != dbItem.Name) { //update }但它非常繁琐且效率不高,我确信有更好的方法可以处理。

这是我到目前为止所得到的。

 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 } 

您可以使用reflection使此检查更通用。

使用此答案通过属性名称获取值 。

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

使用此答案按属性名称设置值 。

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

这个答案列出了所有属性

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

注意 :如果您需要排除某些属性,可以通过将属性列表传递给方法来实现,并且可以检入if要排除。

更新:

我正在更新这个答案,以提供更多的背景信息,为什么我建议暂时不使用基于手动reflection的解决方案; 我还想澄清一下,一旦你确定它适合账单,这样的解决方案本身没有任何问题。

  • 首先,我从代码中假设这是一项正在进行的工作,因此并不完整。 在这种情况下,我觉得代码在完成之前不需要更多的复杂性,并且基于手动reflection的方法是更多的代码供您编写,测试,调试和维护。

  • 例如,现在您似乎有一种情况,即从excel中的数据到employee_master对象中的数据有一个简单的1:1简单副本。 因此,在这种情况下,reflection看起来很简单,因为它可以节省大量无聊的手动属性分配。

  • 但是当HR(或使用此应用程序的任何人)回复给您时,会发生什么: 如果Excel表单上的字段X为空,则将值“空白”复制到目标字段,除非它是星期五,在这种情况下复制值“NA”。

  • 现在,通用解决方案必须适应自定义业务逻辑,并且可能开始变得繁重。 我一直处于这种状况,除非你非常小心,否则从长远来看,它最终会变成一团糟。

  • 我只想指出这一点 – 并建议至少关注Automapper ,因为这已经提供了一种非常有效的方法来解决您的问题。

就效率问题而言,仅提及它们是因为问题提到了它们,我想指出代码中发布的效率低下,而不是手动输入40多个属性赋值的效率低,或者实际上仅关注更新已更改字段的问题。

为什么不重写循环:

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

这使您可以避免使用初始if(dbData.Count>0)因为您将始终从excel表中插入dbData中没有匹配条目的任何行,因此您不需要单独的代码块 -时间插入。 它也比当前循环更有效,因为现在你在dbData中为xlData中的每个对象迭代每个对象; 这意味着如果每个项目中有1,000个项目,那么您将有一百万次迭代……

关于更新过程和效率的说明

(注意:我知道问题不是直接关于效率,但是因为你在复制属性的背景下提到它,我只是想提供一些思考的东西)

  • 除非您构建的系统必须为多个实体执行此操作,否则我建议您不要通过构建基于reflection的属性复制器来增加代码的复杂性。
  • 如果考虑必须复制的属性数量(即foo.x = bar.x类型语句的数量),然后考虑具有强大的,经过充分测试且可certificate有效的基于reflection的属性复制器所需的代码(即具有内置缓存,因此您不必经常重新反映类型属性,一种允许您指定exception的机制,处理边缘情况,对于任何未知原因,您发现对于随机列X,值“null”是在某些情况下,待处理有点不同,等等,你可能会发现前者显然不那么重要:)
  • 请记住,即使是最快的基于reflection的解决方案仍然会比旧的foo.x = bar.x分配慢。
  • 无论如何,如果你必须对10个或20个单独的实体进行此操作,请考虑一般情况,否则我的建议是,手动编写属性副本分配,正确,然后考虑概括 – 或者查看Automapper ,例。
  • 仅更新已更改的字段 – 我不确定您是否需要。 如果记录存在于数据库中,并且用户刚刚提供了他们声称是该对象的“正确”版本的该记录的副本,则只需复制他们提供的所有值并将其保存到数据库中。
  • 我这样说的原因是因为很有可能,仅发送4个修改字段而不是25或其他的效率,在实际往返数据库本身的开销旁边显得微不足道; 如果你能够通过不发送所有列来观察这些操作中有意义的性能提升,我会感到惊讶 – 除非当然所有列都是NVARCHAR(MAX)或者其他:)

  • 如果并发是一个问题(即其他用途可能正在修改相同的数据),那么在数据库表中包含一个ROWVERSION类型列,将其映射到Entity Framework ,并在它们出现时处理并发问题。