entity framework6更新图表

保存不知道状态的对象图的正确方法是什么? 按状态我的意思是它们是新的还是现有的数据库条目正在更新。

例如,如果我有:

public class Person { public int Id { get; set; } public int Name { get; set; } public virtual ICollection Automobiles { get; set; } } public class Automobile { public int Id { get; set; } public int Name { get; set; } public short Seats { get; set; } public virtual ICollection MaintenanceRecords { get; set ;} public virtual Person Person { get; set; } } public class MaintenanceRecord { public int Id { get; set; } public int AutomobileId { get; set; } public DateTime DatePerformed { get; set; } public virtual Automobile Automobile{ get; set; } } 

我正在编辑模型,类似于上面的这些对象,然后将这些模型传递到数据层进行保存,对于这个实例,我碰巧使用entity framework。 所以我将这些模型转换为DAL内部的POCO实体。

似乎除非我的模型具有指示它们是新的还是更新的状态,否则我还有很多工作要做“保存”更改。 我必须首先选择Person实体,更新它,然后匹配任何现有的汽车并更新它们并添加任何新的汽车,然后为每个汽车检查任何新的或更新的维护记录。

有更快/更简单的方法吗? 我可以跟踪模型状态,我想这会对此有所帮助,但这意味着要更改数​​据层之外的代码,我宁愿避免。 我只是希望有一种使用模式,我可以关注这样的更新。

我前一段时间遇到了这个问题,并且一直关注EF Codeplex网站上的这个post。 https://entityframework.codeplex.com/workitem/864

好像它正在考虑下一个版本,我假设EF 7,这显然是一个相当大的EF内部改造。 这可能值得一试…… http://www.nuget.org/packages/RefactorThis.GraphDiff/

回到我的工作时,我在SO上发现了另一个EFpost,有人举了一个如何手动执行此操作的示例。 当时我决定手动完成,不知道为什么,GraphDiff看起来很酷。 这是我所做的一个例子。

  public async Task PutAsync([FromBody] WellEntityModel model) { try { if (!ModelState.IsValid) { return BadRequest(ModelState); } var kne = TheContext.Companies.First(); var entity = TheModelFactory.Create(model); entity.DateUpdated = DateTime.Now; var currentWell = TheContext.Wells.Find(model.Id); // Update scalar/complex properties of parent TheContext.Entry(currentWell).CurrentValues.SetValues(entity); //We don't pass back the company so need to attached the associated company... this is done after mapping the values to ensure its not null. currentWell.Company = kne; // Updated geometry - ARGHHH NOOOOOO check on this once in a while for a fix from EF-Team https://entityframework.codeplex.com/workitem/864 var geometryItemsInDb = currentWell.Geometries.ToList(); foreach (var geometryInDb in geometryItemsInDb) { // Is the geometry item still there? var geometry = entity.Geometries.SingleOrDefault(i => i.Id == geometryInDb.Id); if (geometry != null) // Yes: Update scalar/complex properties of child TheContext.Entry(geometryInDb).CurrentValues.SetValues(geometry); else // No: Delete it TheContext.WellGeometryItems.Remove(geometryInDb); } foreach (var geometry in entity.Geometries) { // Is the child NOT in DB? if (geometryItemsInDb.All(i => i.Id != geometry.Id)) // Yes: Add it as a new child currentWell.Geometries.Add(geometry); } // Update Surveys var surveyPointsInDb = currentWell.SurveyPoints.ToList(); foreach (var surveyInDb in surveyPointsInDb) { // Is the geometry item still there? var survey = entity.SurveyPoints.SingleOrDefault(i => i.Id == surveyInDb.Id); if (survey != null) // Yes: Update scalar/complex properties of child TheContext.Entry(surveyInDb).CurrentValues.SetValues(survey); else // No: Delete it TheContext.WellSurveyPoints.Remove(surveyInDb); } foreach (var survey in entity.SurveyPoints) { // Is the child NOT in DB? if (surveyPointsInDb.All(i => i.Id != survey.Id)) // Yes: Add it as a new child currentWell.SurveyPoints.Add(survey); } // Update Temperatures - THIS IS A HUGE PAIN = HOPE EF is updated to handle updating disconnected graphs. var temperaturesInDb = currentWell.Temperatures.ToList(); foreach (var tempInDb in temperaturesInDb) { // Is the geometry item still there? var temperature = entity.Temperatures.SingleOrDefault(i => i.Id == tempInDb.Id); if (temperature != null) // Yes: Update scalar/complex properties of child TheContext.Entry(tempInDb).CurrentValues.SetValues(temperature); else // No: Delete it TheContext.WellTemperaturePoints.Remove(tempInDb); } foreach (var temps in entity.Temperatures) { // Is the child NOT in DB? if (surveyPointsInDb.All(i => i.Id != temps.Id)) // Yes: Add it as a new child currentWell.Temperatures.Add(temps); } await TheContext.SaveChangesAsync(); return Ok(model); } catch (Exception ex) { Trace.WriteLine(ex.Message); } return InternalServerError(); } 

您所寻找的是工作单元模式:

http://msdn.microsoft.com/en-us/magazine/dd882510.aspx

您可以在客户端上跟踪UoW并使用DTO传递它或让服务器弄清楚它。 真正的DataSet和EF实体都有自己的UoW内部实现。 对于独立的东西有这个框架,但我从来没有使用它所以没有反馈:

http://genericunitofworkandrepositories.codeplex.com/

或者,另一个选项是使用撤消function进行实时更新,有点像进入Gmail联系人时,它会在您使用撤消选项进行更改时保存更改。

这对我来说也是一个巨大的痛苦。 我从@GetFuzzy中提取了一个更可重用的方法:

 public void UpdateCollection( DbContext context, IList databaseCollection, IList detachedCollection, Func keySelector) where TCollection: class where TKey: IEquatable { var databaseCollectionClone = databaseCollection.ToArray(); foreach (var databaseItem in databaseCollectionClone) { var detachedItem = detachedCollection.SingleOrDefault(item => keySelector(item).Equals(keySelector(databaseItem))); if (detachedItem != null) { context.Entry(databaseItem).CurrentValues.SetValues(detachedItem); } else { context.Set().Remove(databaseItem); } } foreach (var detachedItem in detachedCollection) { if (databaseCollectionClone.All(item => keySelector(item).Equals(keySelector(detachedItem)) == false)) { databaseCollection.Add(detachedItem); } } } 

有了这个方法,我可以像这样使用它:

 public void UpdateProduct(Product product) { ... var databaseProduct = productRepository.GetById(product.Id); UpdateCollection(context, databaseProduct.Accessories, product.Accessories, productAccessory => productAcccessory.ProductAccessoryId); UpdateCollection(context, databaseProduct.Categories, product.Categories, productCategory => productCategory.ProductCategoryId); ... context.SubmitChanges(); } 

然而,当图表变深时,我感觉这还不够。

这取决于您如何完成添加/更改实体。

我想你可能在任何时候都试图对一个实体做太多。 允许同时编辑和添加可能会导致您无法确定实体正在执行的操作,尤其是在断开连接的情况下。 除非您要删除实体,否则您应该一次只对单个实体执行一次操作。 这肯定是单调的,但99%的用户想要一个干净且易于理解的界面。 很多时候我们最终制作了应用程序“神”屏幕的屏幕,可以完成所有事情。 不需要9/10次(YAGNI)。

这样,当您编辑用户时,您就知道您正在执行更新操作。 如果要添加新的维护记录,则表示您正在创建附加到汽车的新记录。

总而言之,您应该限制为单个屏幕提供的操作数量,并确保为实体提供某种类型的唯一信息,以便您可以尝试查找实体以查看它是否存在。