【问题标题】:How to add/update child entities when updating a parent entity in EF在 EF 中更新父实体时如何添加/更新子实体
【发布时间】:2015-01-26 08:47:59
【问题描述】:

这两个实体是一对多的关系(由code first fluent api构建)。

public class Parent
{
    public Parent()
    {
        this.Children = new List<Child>();
    }

    public int Id { get; set; }

    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }

    public int ParentId { get; set; }

    public string Data { get; set; }
}

在我的 WebApi 控制器中,我有创建父实体(工作正常)和更新父实体(有一些问题)的操作。更新操作如下所示:

public void Update(UpdateParentModel model)
{
    //what should be done here?
}

目前我有两个想法:

  1. 通过model.Id获取一个名为existing的被跟踪父实体,并将model中的值一一赋值给该实体。这听起来很愚蠢。而在model.Children我不知道哪个孩子是新的,哪个孩子被修改(甚至删除)。

  2. 通过model创建一个新的父实体,并将其附加到DbContext并保存。但是 DbContext 怎么知道孩子的状态(新的添加/删除/修改)?

实现此功能的正确方法是什么?

【问题讨论】:

标签: c# asp.net-mvc entity-framework asp.net-web-api


【解决方案1】:

由于发布到 WebApi 控制器的模型与任何实体框架 (EF) 上下文分离,因此唯一的选择是从数据库加载对象图(父对象包括其子对象)并比较添加了哪些子对象,删除或更新。 (除非您在分离状态(在浏览器或任何地方)期间使用自己的跟踪机制跟踪更改,在我看来这比以下更复杂。)它可能看起来像这样:

public void Update(UpdateParentModel model)
{
    var existingParent = _dbContext.Parents
        .Where(p => p.Id == model.Id)
        .Include(p => p.Children)
        .SingleOrDefault();

    if (existingParent != null)
    {
        // Update parent
        _dbContext.Entry(existingParent).CurrentValues.SetValues(model);

        // Delete children
        foreach (var existingChild in existingParent.Children.ToList())
        {
            if (!model.Children.Any(c => c.Id == existingChild.Id))
                _dbContext.Children.Remove(existingChild);
        }

        // Update and Insert children
        foreach (var childModel in model.Children)
        {
            var existingChild = existingParent.Children
                .Where(c => c.Id == childModel.Id && c.Id != default(int))
                .SingleOrDefault();

            if (existingChild != null)
                // Update child
                _dbContext.Entry(existingChild).CurrentValues.SetValues(childModel);
            else
            {
                // Insert child
                var newChild = new Child
                {
                    Data = childModel.Data,
                    //...
                };
                existingParent.Children.Add(newChild);
            }
        }

        _dbContext.SaveChanges();
    }
}

...CurrentValues.SetValues 可以采用任何对象并根据属性名称将属性值映射到附加实体。如果模型中的属性名称与实体中的名称不同,则不能使用此方法,必须一一分配值。

【讨论】:

  • 但是为什么 ef 没有更“聪明”的方式呢?我认为 ef 可以检测孩子是否被修改/删除/添加,IMO 您上面的代码可以成为 EF 框架的一部分,并成为更通用的解决方案。
  • @DannyChen:EF 应该以更舒适的方式 (entityframework.codeplex.com/workitem/864) 支持更新断开连接的实体确实是一个长期的要求,但它仍然不是框架的一部分。目前,您只能尝试该 codeplex 工作项中提到的第三方库“GraphDiff”,或者像我上面的答案那样编写手动代码。
  • 补充一点:在foreach的update和insert children中,不能existingParent.Children.Add(newChild),因为existingChild linq search会返回最近添加的实体,所以实体会被更新.你只需要插入一个临时列表然后添加。
  • @RandolfRincónFadul 我刚遇到这个问题。我的解决方法是更改​​ existingChild LINQ 查询中的 where 子句:.Where(c =&gt; c.ID == childModel.ID &amp;&amp; c.ID != default(int))
  • @RalphWillgoss 您所说的 2.2 中的修复是什么?
【解决方案2】:

好的,伙计们。我曾经有过这个答案,但一路走失。当您知道有更好的方法但不记得或找不到时,绝对是折磨!这很简单。我只是通过多种方式对其进行了测试。

var parent = _dbContext.Parents
  .Where(p => p.Id == model.Id)
  .Include(p => p.Children)
  .FirstOrDefault();

parent.Children = _dbContext.Children.Where(c => <Query for New List Here>);
_dbContext.Entry(parent).State = EntityState.Modified;

_dbContext.SaveChanges();

您可以用新列表替换整个列表! SQL 代码将根据需要删除和添加实体。无需为此担心。一定要包括儿童收藏或没有骰子。祝你好运!

【讨论】:

  • 正是我所需要的,因为我的模型中的子节点数量通常很少,所以假设 Linq 最初会从表中删除所有原始子节点,然后添加所有新子节点,性能影响不会一个问题。
  • @Charles McIntosh。我不明白为什么您在将 Children 包含在初始查询中时再次设置它?
  • @pantonis 我包含了子集合,以便可以加载它进行编辑。如果我依靠延迟加载来弄清楚它是行不通的。我设置了子项(一次),因为我可以简单地替换列表,而不是手动删除项目并将其添加到集合中,而 entityframework 将为我添加和删除项目。关键是将实体的状态设置为修改并允许实体框架完成繁重的工作。
  • @CharlesMcIntosh 我仍然不明白你想和那里的孩子们一起实现什么。你在第一个请求中包含了它(Include(p=>p.Children)。你为什么又请求它?
  • @pantonis @CharlesMcIntosh 不需要再次请求孩子,这就是他决定重新分配给.Children 的方式。 parent.Children = 行可以分配给任何新创建的子节点。
【解决方案3】:

我一直在搞这样的事情......

protected void UpdateChildCollection<Tparent, Tid , Tchild>(Tparent dbItem, Tparent newItem, Func<Tparent, IEnumerable<Tchild>> selector, Func<Tchild, Tid> idSelector) where Tchild : class
    {
        var dbItems = selector(dbItem).ToList();
        var newItems = selector(newItem).ToList();

        if (dbItems == null && newItems == null)
            return;

        var original = dbItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();
        var updated = newItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();

        var toRemove = original.Where(i => !updated.ContainsKey(i.Key)).ToArray();
        var removed = toRemove.Select(i => DbContext.Entry(i.Value).State = EntityState.Deleted).ToArray();

        var toUpdate = original.Where(i => updated.ContainsKey(i.Key)).ToList();
        toUpdate.ForEach(i => DbContext.Entry(i.Value).CurrentValues.SetValues(updated[i.Key]));

        var toAdd = updated.Where(i => !original.ContainsKey(i.Key)).ToList();
        toAdd.ForEach(i => DbContext.Set<Tchild>().Add(i.Value));
    }

您可以使用以下方式调用:

UpdateChildCollection(dbCopy, detached, p => p.MyCollectionProp, collectionItem => collectionItem.Id)

不幸的是,如果子类型上的集合属性也需要更新,这种情况就会失败。考虑尝试通过传递一个 IRepository(带有基本的 CRUD 方法)来解决这个问题,该 IRepository 将负责自行调用 UpdateChildCollection。将调用 repo 而不是直接调用 DbContext.Entry。

不知道这一切将如何大规模执行,但不确定如何解决这个问题。

【讨论】:

  • 很好的解决方案!但是如果添加一个以上的新项目就会失败,更新的字典不能有两次零 id。需要一些工作。并且如果relationship是N -> N也会失败,实际上是项目被添加到数据库中,但是N -> N表没有被修改。
  • toAdd.ForEach(i =&gt; (selector(dbItem) as ICollection&lt;Tchild&gt;).Add(i.Value)); 应该解决 n -> n 问题。
  • 这是按代码列出的 VINN 图。谢谢。
【解决方案4】:

如果您使用的是 EntityFrameworkCore,您可以在控制器发布操作中执行以下操作(Attach method 递归地附加导航属性,包括集合):

_context.Attach(modelPostedToController);

IEnumerable<EntityEntry> unchangedEntities = _context.ChangeTracker.Entries().Where(x => x.State == EntityState.Unchanged);

foreach(EntityEntry ee in unchangedEntities){
     ee.State = EntityState.Modified;
}

await _context.SaveChangesAsync();

假设更新的每个实体都设置了所有属性,并在客户端的发布数据中提供(例如,不适用于实体的部分更新)。

您还需要确保为此操作使用新的/专用实体框架数据库上下文。

【讨论】:

  • 它不会从父集合中删除已删除的实体
【解决方案5】:
public async Task<IHttpActionResult> PutParent(int id, Parent parent)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != parent.Id)
            {
                return BadRequest();
            }

            db.Entry(parent).State = EntityState.Modified;

            foreach (Child child in parent.Children)
            {
                db.Entry(child).State = child.Id == 0 ? EntityState.Added : EntityState.Modified;
            }

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ParentExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Ok(db.Parents.Find(id));
        }

这就是我解决这个问题的方法。这样,EF 就知道要添加哪些更新。

【讨论】:

  • 工作就像一个魅力!谢谢。
【解决方案6】:

就保存整个对象图而言,有一些项目可以使客户端和服务器之间的交互更容易。

这里有两个你想看的:

上述两个项目都在返回服务器时识别断开连接的实体,检测并保存更改,并将受影响的数据返回给客户端。

【讨论】:

    【解决方案7】:

    应该这样做……

    private void Reconcile<T>(DbContext context,
        IReadOnlyCollection<T> oldItems,
        IReadOnlyCollection<T> newItems,
        Func<T, T, bool> compare)
    {
        var itemsToAdd = new List<T>();
        var itemsToRemove = new List<T>();
    
        foreach (T newItem in newItems)
        {
            T oldItem = oldItems.FirstOrDefault(arg1 => compare(arg1, newItem));
    
            if (oldItem == null)
            {
                itemsToAdd.Add(newItem);
            }
            else
            {
                context.Entry(oldItem).CurrentValues.SetValues(newItem);
            }
        }
    
        foreach (T oldItem in oldItems)
        {
            if (!newItems.Any(arg1 => compare(arg1, oldItem)))
            {
                itemsToRemove.Add(oldItem);
            }
        }
    
        foreach (T item in itemsToAdd)
            context.Add(item);
    
        foreach (T item in itemsToRemove)
            context.Remove(item);
    }
    

    【讨论】:

      【解决方案8】:

      只是概念证明 Controler.UpdateModel 无法正常工作。

      全班here:

      const string PK = "Id";
      protected Models.Entities con;
      protected System.Data.Entity.DbSet<T> model;
      
      private void TestUpdate(object item)
      {
          var props = item.GetType().GetProperties();
          foreach (var prop in props)
          {
              object value = prop.GetValue(item);
              if (prop.PropertyType.IsInterface && value != null)
              {
                  foreach (var iItem in (System.Collections.IEnumerable)value)
                  {
                      TestUpdate(iItem);
                  }
              }
          }
      
          int id = (int)item.GetType().GetProperty(PK).GetValue(item);
          if (id == 0)
          {
              con.Entry(item).State = System.Data.Entity.EntityState.Added;
          }
          else
          {
              con.Entry(item).State = System.Data.Entity.EntityState.Modified;
          }
      
      }
      

      【讨论】:

        【解决方案9】:

        因为我讨厌重复复杂的逻辑,所以这里是 Slauma 解决方案的通用版本。

        这是我的更新方法。请注意,在分离的情况下,有时您的代码会读取数据然后对其进行更新,因此它并不总是分离的。

        public async Task UpdateAsync(TempOrder order)
        {
            order.CheckNotNull(nameof(order));
            order.OrderId.CheckNotNull(nameof(order.OrderId));
        
            order.DateModified = _dateService.UtcNow;
        
            if (_context.Entry(order).State == EntityState.Modified)
            {
                await _context.SaveChangesAsync().ConfigureAwait(false);
            }
            else // Detached.
            {
                var existing = await SelectAsync(order.OrderId!.Value).ConfigureAwait(false);
                if (existing != null)
                {
                    order.DateModified = _dateService.UtcNow;
                    _context.TrackChildChanges(order.Products, existing.Products, (a, b) => a.OrderProductId == b.OrderProductId);
                    await _context.SaveChangesAsync(order, existing).ConfigureAwait(false);
                }
            }
        }
        

        CheckNotNull is defined here.

        创建这些扩展方法。

        /// <summary>
        /// Tracks changes on childs models by comparing with latest database state.
        /// </summary>
        /// <typeparam name="T">The type of model to track.</typeparam>
        /// <param name="context">The database context tracking changes.</param>
        /// <param name="childs">The childs to update, detached from the context.</param>
        /// <param name="existingChilds">The latest existing data, attached to the context.</param>
        /// <param name="match">A function to match models by their primary key(s).</param>
        public static void TrackChildChanges<T>(this DbContext context, IList<T> childs, IList<T> existingChilds, Func<T, T, bool> match)
            where T : class
        {
            context.CheckNotNull(nameof(context));
            childs.CheckNotNull(nameof(childs));
            existingChilds.CheckNotNull(nameof(existingChilds));
        
            // Delete childs.
            foreach (var existing in existingChilds.ToList())
            {
                if (!childs.Any(c => match(c, existing)))
                {
                    existingChilds.Remove(existing);
                }
            }
        
            // Update and Insert childs.
            var existingChildsCopy = existingChilds.ToList();
            foreach (var item in childs.ToList())
            {
                var existing = existingChildsCopy
                    .Where(c => match(c, item))
                    .SingleOrDefault();
        
                if (existing != null)
                {
                    // Update child.
                    context.Entry(existing).CurrentValues.SetValues(item);
                }
                else
                {
                    // Insert child.
                    existingChilds.Add(item);
                    // context.Entry(item).State = EntityState.Added;
                }
            }
        }
        
        /// <summary>
        /// Saves changes to a detached model by comparing it with the latest data.
        /// </summary>
        /// <typeparam name="T">The type of model to save.</typeparam>
        /// <param name="context">The database context tracking changes.</param>
        /// <param name="model">The model object to save.</param>
        /// <param name="existing">The latest model data.</param>
        public static void SaveChanges<T>(this DbContext context, T model, T existing)
            where T : class
        {
            context.CheckNotNull(nameof(context));
            model.CheckNotNull(nameof(context));
        
            context.Entry(existing).CurrentValues.SetValues(model);
            context.SaveChanges();
        }
        
        /// <summary>
        /// Saves changes to a detached model by comparing it with the latest data.
        /// </summary>
        /// <typeparam name="T">The type of model to save.</typeparam>
        /// <param name="context">The database context tracking changes.</param>
        /// <param name="model">The model object to save.</param>
        /// <param name="existing">The latest model data.</param>
        /// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
        /// <returns></returns>
        public static async Task SaveChangesAsync<T>(this DbContext context, T model, T existing, CancellationToken cancellationToken = default)
            where T : class
        {
            context.CheckNotNull(nameof(context));
            model.CheckNotNull(nameof(context));
        
            context.Entry(existing).CurrentValues.SetValues(model);
            await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }
        

        【讨论】:

          【解决方案10】:

          对于VB.NET开发者使用这个通用的sub来标记子状态,好用

          注意事项:

          • PromatCon:实体对象
          • amList:是您要添加或修改的子列表
          • rList:是您要删除的子列表
          updatechild(objCas.ECC_Decision, PromatCon.ECC_Decision.Where(Function(c) c.rid = objCas.rid And Not objCas.ECC_Decision.Select(Function(x) x.dcid).Contains(c.dcid)).toList)
          
          Sub updatechild(Of Ety)(amList As ICollection(Of Ety), rList As ICollection(Of Ety))
                  If amList IsNot Nothing Then
                      For Each obj In amList
                          Dim x = PromatCon.Entry(obj).GetDatabaseValues()
                          If x Is Nothing Then
                              PromatCon.Entry(obj).State = EntityState.Added
                          Else
                              PromatCon.Entry(obj).State = EntityState.Modified
                          End If
                      Next
                  End If
          
                  If rList IsNot Nothing Then
                      For Each obj In rList.ToList
                          PromatCon.Entry(obj).State = EntityState.Deleted
                      Next
                  End If
          End Sub
          
          PromatCon.SaveChanges()
          

          【讨论】:

          • 请将其保留为 C# 问题的 C# 答案 :)
          • 如果你习惯于搜索VB答案,通常你最终会在C#中找到答案,--!--
          【解决方案11】:

          这是我运行良好的代码。

          public async Task<bool> UpdateDeviceShutdownAsync(Guid id, DateTime shutdownAtTime, int areaID, decimal mileage,
                  decimal motohours, int driverID, List<int> commission,
                  string shutdownPlaceDescr, int deviceShutdownTypeID, string deviceShutdownDesc,
                  bool isTransportation, string violationConditions, DateTime shutdownStartTime,
                  DateTime shutdownEndTime, string notes, List<Guid> faultIDs )
                  {
                      try
                      {
                          using (var db = new GJobEntities())
                          {
                              var isExisting = await db.DeviceShutdowns.FirstOrDefaultAsync(x => x.ID == id);
          
                              if (isExisting != null)
                              {
                                  isExisting.AreaID = areaID;
                                  isExisting.DriverID = driverID;
                                  isExisting.IsTransportation = isTransportation;
                                  isExisting.Mileage = mileage;
                                  isExisting.Motohours = motohours;
                                  isExisting.Notes = notes;                    
                                  isExisting.DeviceShutdownDesc = deviceShutdownDesc;
                                  isExisting.DeviceShutdownTypeID = deviceShutdownTypeID;
                                  isExisting.ShutdownAtTime = shutdownAtTime;
                                  isExisting.ShutdownEndTime = shutdownEndTime;
                                  isExisting.ShutdownStartTime = shutdownStartTime;
                                  isExisting.ShutdownPlaceDescr = shutdownPlaceDescr;
                                  isExisting.ViolationConditions = violationConditions;
          
                                  // Delete children
                                  foreach (var existingChild in isExisting.DeviceShutdownFaults.ToList())
                                  {
                                      db.DeviceShutdownFaults.Remove(existingChild);
                                  }
          
                                  if (faultIDs != null && faultIDs.Any())
                                  {
                                      foreach (var faultItem in faultIDs)
                                      {
                                          var newChild = new DeviceShutdownFault
                                          {
                                              ID = Guid.NewGuid(),
                                              DDFaultID = faultItem,
                                              DeviceShutdownID = isExisting.ID,
                                          };
          
                                          isExisting.DeviceShutdownFaults.Add(newChild);
                                      }
                                  }
          
                                  // Delete all children
                                  foreach (var existingChild in isExisting.DeviceShutdownComissions.ToList())
                                  {
                                      db.DeviceShutdownComissions.Remove(existingChild);
                                  }
          
                                  // Add all new children
                                  if (commission != null && commission.Any())
                                  {
                                      foreach (var cItem in commission)
                                      {
                                          var newChild = new DeviceShutdownComission
                                          {
                                              ID = Guid.NewGuid(),
                                              PersonalID = cItem,
                                              DeviceShutdownID = isExisting.ID,
                                          };
          
                                          isExisting.DeviceShutdownComissions.Add(newChild);
                                      }
                                  }
          
                                  await db.SaveChangesAsync();
          
                                  return true;
                              }
                          }
                      }
                      catch (Exception ex)
                      {
                          logger.Error(ex);
                      }
          
                      return false;
                  }
          

          【讨论】:

            【解决方案12】:

            这不是最优雅的程序,但它确实有效。干杯!

            var entity = await context.Entities.FindAsync(id);
            
            var newEntity = new AmazingEntity() {
              p1 = child1
              p2 = child2
              p3 = child3.child4 //... nested collections
            };
            
            if (entity != null) 
            {
              db.Entities.Remove(entity);
            }
            
            db.Entities.Add(newEntity);
            
            await db.SaveChangesAsync();
            

            记得删除 PK。

            var child4 = Tools.CloneJson(deepNestedElement);
            child4.id = 0;
            child3.Add(child4);
            
            
            public static class Tools
            {
              public static JsonSerializerSettings jsonSettings = new JsonSerializerSettings {
                ObjectCreationHandling = ObjectCreationHandling.Replace,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
              }; 
            
              public static string JSerialize<T>(T source) {       
                return JsonConvert.SerializeObject(source, Formatting.Indented, jsonSettings);
              }
            
              public static T JDeserialize<T>(string source) {       
                return JsonConvert.DeserializeObject<T>(source, jsonSettings);
              }
            
              public static T CloneJson<T>(this T source)
              { 
                return CloneJson<T, T>(source);
              }
            
              public static TOut CloneJson<TIn, TOut>(TIn source)
              { 
                if (Object.ReferenceEquals(source, null))      
                  return default(TOut);      
                return JDeserialize<TOut>(JSerialize(source));
              }
            }
            

            【讨论】:

              【解决方案13】:

              所以,我终于设法让它工作了,虽然不是完全自动的。
              注意 AutoMapper 如果您使用显式 context.Update(entity),不同之处在于整个对象将被标记为已修改,并且每个道具都将被更新。
              在这种情况下,您不需要跟踪,但缺点如前所述。
              也许这对你来说不是问题,但它更昂贵,我想在 Save 中记录确切的更改,所以我需要正确的信息。

                          // We always want tracking for auto-updates
                          var entityToUpdate = unitOfWork.GetGenericRepository<Article, int>()
                              .GetAllActive() // Uses EF tracking
                              .Include(e => e.Barcodes.Where(e => e.Status == DatabaseEntityStatus.Active))
                              .First(e => e.Id == request.Id);
              
                          mapper.Map(request, entityToUpdate); // Maps it to entity with AutoMapper <3
                          ModifyBarcodes(entityToUpdate, request);
              
                          // Removed part of the code for space
              
                          unitOfWork.Save();
              

              在此处修改条码部分。
              我们希望以一种不会导致 EF 跟踪混乱的方式修改我们的集合。
              不幸的是,AutoMapper 映射会创建一个全新的集合实例,因此会打乱跟踪,尽管我很确定它应该可以工作。 无论如何,由于我是从 FE 发送完整列表,因此我们实际上确定应该添加/更新/删除的内容,并仅处理列表本身。
              由于 EF 跟踪已开启,EF 处理它就像一个魅力。

                          var toUpdate = article.Barcodes
                              .Where(e => articleDto.Barcodes.Select(b => b.Id).Contains(e.Id))
                              .ToList();
              
                          toUpdate.ForEach(e =>
                          {
                              var newValue = articleDto.Barcodes.FirstOrDefault(f => f.Id == e.Id);
                              mapper.Map(newValue, e);
                          });
              
                          var toAdd = articleDto.Barcodes
                              .Where(e => !article.Barcodes.Select(b => b.Id).Contains(e.Id))
                              .Select(e => mapper.Map<Barcode>(e))
                              .ToList();
              
                          article.Barcodes.AddRange(toAdd);
              
                          article.Barcodes
                              .Where(e => !articleDto.Barcodes.Select(b => b.Id).Contains(e.Id))
                              .ToList()
                              .ForEach(e => article.Barcodes.Remove(e));
              
              
              CreateMap<ArticleDto, Article>()
                          .ForMember(e => e.DateCreated, opt => opt.Ignore())
                          .ForMember(e => e.DateModified, opt => opt.Ignore())
                          .ForMember(e => e.CreatedById, opt => opt.Ignore())
                          .ForMember(e => e.LastModifiedById, opt => opt.Ignore())
                          .ForMember(e => e.Status, opt => opt.Ignore())
                          // When mapping collections, the reference itself is destroyed
                          // hence f* up EF tracking and makes it think all previous is deleted
                          // Better to leave it on manual and handle collecion manually
                          .ForMember(e => e.Barcodes, opt => opt.Ignore())
                          .ReverseMap()
                          .ForMember(e => e.Barcodes, opt => opt.MapFrom(src => src.Barcodes.Where(e => e.Status == DatabaseEntityStatus.Active)));
              

              【讨论】:

                【解决方案14】:

                请参阅下面的代码 sn-p 来自我实现相同事情的一个项目。如果新条目,它将保存数据,如果存在则更新,如果记录在发布 json 中不可用,则删除。 帮助您理解架构的 Json 数据:

                {
                    "groupId": 1,
                    "groupName": "Group 1",
                    "sortOrder": 1,
                    "filterNames": [
                        {
                            "filterId": 1,
                            "filterName1": "Name11111",
                            "sortOrder": 10,
                            "groupId": 1           
                        }  ,
                        {
                            "filterId": 1006,
                            "filterName1": "Name Changed 1",
                            "sortOrder": 10,
                            "groupId": 1           
                        }  ,
                        {
                            "filterId": 1007,
                            "filterName1": "New Filter 1",
                            "sortOrder": 10,
                            "groupId": 1           
                        } ,
                        {
                            "filterId": 2,
                            "filterName1": "Name 2 Changed",
                            "sortOrder": 10,
                            "groupId": 1           
                        }                 
                    ]
                }
                
                
                public async Task<int> UpdateFilter(FilterGroup filterGroup)
                        {                        
                            var Ids = from f in filterGroup.FilterNames select f.FilterId;
                            var toBeDeleted = dbContext.FilterNames.Where(x => x.GroupId == filterGroup.GroupId
                            && !Ids.Contains(x.FilterId)).ToList();
                            foreach(var item in toBeDeleted)
                            {
                                dbContext.FilterNames.Remove(item);
                            }
                            await dbContext.SaveChangesAsync();
                
                            dbContext.FilterGroups.Attach(filterGroup);
                            dbContext.Entry(filterGroup).State = EntityState.Modified;
                            for(int i=0;i<filterGroup.FilterNames.Count();i++)            
                            {
                                if (filterGroup.FilterNames.ElementAt(i).FilterId != 0)
                                {
                                    dbContext.Entry(filterGroup.FilterNames.ElementAt(i)).State = EntityState.Modified;
                                }
                            }            
                            return await dbContext.SaveChangesAsync();
                        }
                

                【讨论】:

                • 这如何改善所有给出的其他答案?
                • 一开始比较短
                • @GertArnold:我想,它已经完成了。
                【解决方案15】:

                @Charles McIntosh 确实为我的情况给出了答案,因为传入的模型是分离的。对我来说,最终起作用的是首先保存传入的模型......然后像以前一样继续添加孩子:

                public async Task<IHttpActionResult> GetUPSFreight(PartsExpressOrder order)
                {
                    db.Entry(order).State = EntityState.Modified;
                    db.SaveChanges();
                  ...
                }
                

                更新: 只是为了澄清,因为我的解释似乎被误解了:我用查尔斯麦金托什的例子来修改孩子。但是,就我而言,我需要先保存我的父模型...然后将更改应用到子模型。

                【讨论】:

                • 跟孩子没关系。
                • @MahdiHesari 正如我从一开始就说过的,查尔斯·麦金托什已经举了一个拯救孩子的例子。我在他的例子中添加了一个事实,在我的例子中,我需要先保存我的父模型......然后将更改应用到孩子。
                猜你喜欢
                • 2013-03-03
                • 2022-12-02
                • 1970-01-01
                • 2013-07-24
                • 2016-11-16
                • 2021-10-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多