简体   繁体   English

如何在 EF 中更新父实体时添加/更新子实体

[英]How to add/update child entities when updating a parent entity in EF

The two entities are one-to-many relationship (built by code first fluent api).这两个实体是一对多关系(由代码优先的 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; }
}

In my WebApi controller I have actions to create a parent entity(which is working fine) and update a parent entity(which has some problem).在我的 WebApi controller 中,我有创建父实体(工作正常)和更新父实体(有一些问题)的操作。 The update action looks like:更新操作如下所示:

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

Currently I have two ideas:目前我有两个想法:

  1. Get a tracked parent entity named existing by model.Id , and assign values in model one by one to the entity.获取一个名为existing的被跟踪父实体,并将model.Id中的值model该实体。 This sounds stupid.这听起来很愚蠢。 And in model.Children I don't know which child is new, which child is modified(or even deleted).而在model.Children中,我不知道哪个孩子是新的,哪个孩子被修改(甚至被删除)。

  2. Create a new parent entity via model , and attached it to the DbContext and save it.通过model创建一个新的父实体,并将其附加到 DbContext 并保存。 But how can the DbContext know the state of children (new add/delete/modified)?但是DbContext怎么知道children(新增/删除/修改)的state呢?

What's the correct way of implement this feature?实现此功能的正确方法是什么?

Because the model that gets posted to the WebApi controller is detached from any entity-framework (EF) context, the only option is to load the object graph (parent including its children) from the database and compare which children have been added, deleted or updated.由于发布到 WebApi 控制器的模型与任何实体框架 (EF) 上下文分离,因此唯一的选择是从数据库加载对象图(父对象,包括其子对象)并比较哪些子对象已添加、删除或更新。 (Unless you would track the changes with your own tracking mechanism during the detached state (in the browser or wherever) which in my opinion is more complex than the following.) It could look like this: (除非您在分离状态(在浏览器或任何地方)期间使用自己的跟踪机制跟踪更改,我认为这比以下更复杂。)它可能如下所示:

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 can take any object and maps property values to the attached entity based on the property name. ...CurrentValues.SetValues可以接受任何对象,并根据属性名称将属性值映射到附加实体。 If the property names in your model are different from the names in the entity you can't use this method and must assign the values one by one.如果模型中的属性名称与实体中的名称不同,则不能使用此方法,必须一一赋值。

OK guys.好,朋友们。 I had this answer once but lost it along the way.我曾经有过这个答案,但在此过程中丢失了。 absolute torture when you know there's a better way but can't remember it or find it!当您知道有更好的方法但不记得或找不到它时,这绝对是一种折磨! It's very simple.这很简单。 I just tested it multiple ways.我只是通过多种方式对其进行了测试。

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

You can replace the whole list with a new one!您可以用新列表替换整个列表! The SQL code will remove and add entities as needed. SQL 代码将根据需要删除和添加实体。 No need to concern yourself with that.没有必要担心自己。 Be sure to include child collection or no dice.一定要包括孩子收集或没有骰子。 Good luck!祝你好运!

I've been messing about with something like this...我一直在搞这样的事情......

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

which you can call with something like:您可以使用以下内容调用:

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

Unfortunately, this kinda falls over if there are collection properties on the child type which also need to be updated.不幸的是,如果子类型的集合属性也需要更新,这种情况就会失败。 Considering trying to solve this by passing an IRepository (with basic CRUD methods) which would be responsible for calling UpdateChildCollection on its own.考虑尝试通过传递一个 IRepository(带有基本的 CRUD 方法)来解决这个问题,该 IRepository 将负责自行调用 UpdateChildCollection。 Would call the repo instead of direct calls to DbContext.Entry.将调用 repo 而不是直接调用 DbContext.Entry。

Have no idea how this will all perform at scale, but not sure what else to do with this problem.不知道这将如何大规模执行,但不确定如何处理这个问题。

If you are using EntityFrameworkCore you can do the following in your controller post action (The Attach method recursively attaches navigation properties including collections):如果您使用的是 EntityFrameworkCore,则可以在控制器 post 操作中执行以下操作( Attach 方法递归地附加导航属性,包括集合):

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

It is assumed that each entity that was updated has all properties set and provided in the post data from the client (eg. won't work for partial update of an entity).假设更新的每个实体都设置了所有属性并在来自客户端的发布数据中提供(例如,不适用于实体的部分更新)。

You also need to make sure that you are using a new/dedicated entity framework database context for this operation.您还需要确保为此操作使用新的/专用的实体框架数据库上下文。

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

This is how I solved this problem.这就是我解决这个问题的方法。 This way, EF knows which to add which to update.这样,EF 就知道要添加哪些要更新。

There are a few projects out there that make the interaction between the client and the server easier as far as it concerns saving an entire object graph.就保存整个对象图而言,有一些项目可以使客户端和服务器之间的交互更容易。

Here are two you'd want to look at:这里有两个你想看的:

Both the projects above take recognize the disconnected entities when it's returned to the server, detect and save the changes, and return to the client affected data.上述两个项目都在返回到服务器时识别断开连接的实体,检测并保存更改,并返回到客户端受影响的数据。

Just proof of concept Controler.UpdateModel won't work correctly.只是概念证明Controler.UpdateModel将无法正常工作。

Full class 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;
    }

}

This ought to do it...这个应该可以...

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

For VB.NET developers Use this generic sub to mark the child state, easy to use对于 VB.NET 开发者使用这个泛型 sub 来标记子状态,方便使用

Notes:笔记:

  • PromatCon: the entity object PromatCon:实体对象
  • amList: is the child list that you want to add or modify amList:是您要添加或修改的子列表
  • rList: is the child list that you want to remove 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()

Here is my code that works just fine.这是我的代码,效果很好。

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

Because I hate repeating complex logic, here's a generic version of Slauma's solution.因为我讨厌重复复杂的逻辑,这里是 Slauma 解决方案的通用版本。

Here's my update method.这是我的更新方法。 Note that in a detached scenario, sometimes your code will read data and then update it, so it's not always detached.请注意,在分离的情况下,有时您的代码会读取数据然后更新它,因此它并不总是分离的。

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. CheckNotNull 在这里定义。

Create these extension methods.创建这些扩展方法。

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

It's not the most elegant procedure, but it works.这不是最优雅的程序,但它有效。 Cheers!干杯!

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

Just remember to remove the PK.只记得删除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));
  }
}

So, I finally managed to get it working, although not fully automatically.所以,我终于设法让它工作了,虽然不是完全自动的。
Notice the AutoMapper <3.注意 AutoMapper <3。 It handles all the mapping of properties so you don't have to do it manually.它处理所有属性的映射,因此您不必手动完成。 Also, if used in a way where it maps from one object to another, then it only updates the properties and that marks changed properties as Modified to EF, which is what we want.此外,如果以一种从一个对象映射到另一个对象的方式使用,那么它只会更新属性并将更改的属性标记为已修改为 EF,这正是我们想要的。
If you would use explicit context.Update(entity), the difference would be that entire object would be marked as Modified and EVERY prop would be updated.如果您使用显式 context.Update(entity),则不同之处在于整个对象将被标记为 Modified 并且每个 prop 都将被更新。
In that case you don't need tracking but the drawbacks are as mentioned.在这种情况下,您不需要跟踪,但缺点如上所述。
Maybe that's not a problem for you but it's more expensive and I want to log exact changes inside Save so I need correct info.也许这对你来说不是问题,但它更昂贵,我想在 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();

ModifyBarcodes part here.此处修改条码部分。
We want to modify our collection in a way that EF tracking won't end up messed up.我们希望以一种 EF 跟踪不会最终搞砸的方式修改我们的集合。
AutoMapper mapping would, unforunately, create a completely new instance of collection, there fore messing up the tracking, although, I was pretty sure it should work.不幸的是,AutoMapper 映射会创建一个全新的集合实例,因此会破坏跟踪,尽管我很确定它应该可以工作。 Anyways, since I'm sending complete list from FE, here we actually determine what should be Added/Updated/Deleted and just handle the list itself.无论如何,由于我从 FE 发送完整列表,在这里我们实际上确定应该添加/更新/删除什么并且只处理列表本身。
Since EF tracking is ON, EF handles it like a charm.由于 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)));

@Charles McIntosh really gave me the answer for my situation in that the passed in model was detached. @Charles McIntosh 真的为我的情况提供了答案,因为传入的模型已分离。 For me what ultimately worked was saving the passed in model first... then continuing to add the children as I already was before:对我来说,最终有效的是首先保存传入的模型......然后继续添加孩子,就像我之前一样:

public async Task<IHttpActionResult> GetUPSFreight(PartsExpressOrder order)
{
    db.Entry(order).State = EntityState.Modified;
    db.SaveChanges();
  ...
}
var parent = context.Parent.FirstOrDefault(x => x.Id == modelParent.Id);
if (parent != null)
{
  parent.Childs = modelParent.Childs;
}

source 来源

Refer below code snippet from one of my projects where I implemented the same thing.请参阅我的一个项目中的以下代码片段,其中我实现了同样的事情。 It will make save data if new entry, updates if existing and delete if record is not available in the posting json.如果有新条目,它将保存数据,如果存在则更新,如果发布 json 中的记录不可用则删除。 Json Data to help you understand the schema: 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();
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 EF 中更新父实体时更新子实体 - update child entities when updating a parent entity in EF 在 EF 6 中更新父实体时如何删除子实体? - How to delete child entities when updating a parent entity in EF 6? 在 Entity Framework Core 中更新父实体时添加或更新子实体 - Add or Update child entities when updating a parent entity in Entity Framework Core 无法在EF4中将父实体与现有子实体一起添加 - Unable to add parent entity with existing child entities in EF4 为什么先更新EF代码中的父实体和子实体时,为什么不必更新父对象 - Why don't I have to update a parent object when updating both the parent and child entity in EF code first 如何在EF4中将新的子实体添加到父实体? - How do you add new child entities to a parent in EF4? 使用 EF Core 在 SQL Server 中处理乐观并发 - 更新子级时,如何更新父级? - Handle Optimistic Concurrency in SQL Server with EF Core - when updating child, how to update the parent? EF Core:可以添加和保存父实体,但根本无法保存子实体 - EF Core: Can add and save parent entity, but fail to save child entities at all 从父实体更新子实体 - Update child entities from parent entity 多个父实体首先在EF代码中包含一个子实体 - multiple parent Entities with one child entity in EF code first
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM