保存你不知道状态的对象图的正确方法是什么?我所说的状态是指它们是新的还是正在更新的现有数据库条目。
例如,如果我有:
public class Person
{
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Automobile> Automobiles { get; set; }
}
public class Automobile
{
public int Id { get; set; }
public int Name { get; set; }
public short Seats { get; set; }
public virtual ICollection<MaintenanceRecord> 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; }
}我正在编辑模型,类似于上面的这些对象,然后将这些模型传递到数据层保存,在这个实例中,我碰巧使用了实体框架。所以我把这些模型转换成DAL内部的POCO实体。
看来,除非我的模型有一个状态指示它们是新的还是更新的,我有相当多的工作要做,以“保存”更改。我必须首先选择Person实体,更新它,然后匹配任何现有的汽车并更新这些并添加任何新的,然后对每一辆汽车检查任何新的或更新的维护记录。
有没有一种更快/更容易的方法来做到这一点?我有可能跟踪模型状态,我想这将对此有所帮助,但这意味着对数据层之外的代码进行更改,而我更希望避免这种变化。我只是希望有一个模式的使用,我可以遵循这样的更新。
发布于 2014-06-02 17:29:56
我在一段时间前就遇到了这个问题,并且一直在EF Codeplex站点上关注这个帖子。https://entityframework.codeplex.com/workitem/864
似乎它正在考虑下一个版本,我假设EF 7,这显然是一个相当大的内部改革EF。这可能值得一查..。http://www.nuget.org/packages/RefactorThis.GraphDiff/
当我在做这个的时候,我找到了另一个EF帖子,有人给我举了一个例子,说明如何手动完成这个任务。当我决定手动完成时,不知道为什么,GraphDiff看起来很酷。这是我所做的一个例子。
public async Task<IHttpActionResult> 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();
}发布于 2016-05-24 15:41:12
这对我来说也是一个巨大的痛苦。我将答案从@GetFuzzy提取到一个更可重用的方法:
public void UpdateCollection<TCollection, TKey>(
DbContext context, IList<TCollection> databaseCollection,
IList<TCollection> detachedCollection,
Func<TCollection, TKey> keySelector) where TCollection: class where TKey: IEquatable<TKey>
{
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<TCollection>().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();
}然而,当图形变得更深时,我有一种感觉,这是不够的。
发布于 2014-06-02 17:23:06
你要寻找的是工作单位模式:
http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
您可以跟踪客户端上的UoW并将其传递给DTO,也可以让服务器解决它。真正的DataSet和EF实体都有自己的UoW内部实现。对于一些单独的东西,有这个框架,但我从来没有使用过它,所以没有反馈:
http://genericunitofworkandrepositories.codeplex.com/
或者,另一种选择是使用撤销功能进行实时更新,就像进入Gmail联系人时一样,它在使用撤消选项时保存更改。
https://stackoverflow.com/questions/23999798
复制相似问题