首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架6更新图

实体框架6更新图
EN

Stack Overflow用户
提问于 2014-06-02 17:07:17
回答 5查看 11K关注 0票数 13

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

例如,如果我有:

代码语言:javascript
复制
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实体,更新它,然后匹配任何现有的汽车并更新这些并添加任何新的,然后对每一辆汽车检查任何新的或更新的维护记录。

有没有一种更快/更容易的方法来做到这一点?我有可能跟踪模型状态,我想这将对此有所帮助,但这意味着对数据层之外的代码进行更改,而我更希望避免这种变化。我只是希望有一个模式的使用,我可以遵循这样的更新。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 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看起来很酷。这是我所做的一个例子。

代码语言:javascript
复制
  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();
    }
票数 8
EN

Stack Overflow用户

发布于 2016-05-24 15:41:12

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

代码语言:javascript
复制
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);
        }
    }
}

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

代码语言:javascript
复制
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();
}

然而,当图形变得更深时,我有一种感觉,这是不够的。

票数 3
EN

Stack Overflow用户

发布于 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联系人时一样,它在使用撤消选项时保存更改。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23999798

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档