这两个实体是一对多关系(由代码第一个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?
}

目前我有两个想法:

获取一个被跟踪的父实体,命名为按模型存在的。Id,并将模型中的值逐个分配给实体。这听起来很愚蠢。在模型中。我不知道哪个子是新的,哪个子是修改的(甚至是删除的)。 通过模型创建一个新的父实体,并将其附加到DbContext并保存。但是DbContext如何知道子节点的状态(新增/删除/修改)呢?

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


当前回答

这不是最优雅的方法,但很有效。干杯!

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

其他回答

这是我的代码,工作得很好。

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

好男人。我曾经有过这个答案,但后来又失去了。当你知道有更好的方法,但却想不起来或找不到时,绝对是一种折磨!这很简单。我只是用多种方法测试了一下。

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代码将根据需要删除和添加实体。你不必为那件事操心。确保包含子集合,否则就没有骰子。好运!

这不是最优雅的方法,但很有效。干杯!

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

VB。NET开发人员使用这个通用子标记子状态,易于使用

注: 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()

如果你正在使用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();

假设每个被更新的实体都设置了所有属性,并在来自客户端的post数据中提供。不会为实体的部分更新工作)。

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