使用EntityFramework向父级和子级升级

时间:2019-05-28 12:00:48

标签: entity-framework-6 upsert

我发现自己经常需要EntityFramework中的“ Upsert”功能-我的父级+子级实体图断开了,我想“ upsert”父级(及其子级)。我的意思是

  • 当父母没有分配了ID(id = 0)时,我想简单地插入父母和孩子(两种情况下均自动分配ID)
  • 为父代 分配了ID后,我想用新的ID替换数据库中现有的父代实体(具有相同的ID)。此外,应删除与该父母相关联的所有孩子(父母“拥有”孩子)。应将新父母的孩子插入他们的位置(保留分配的ID)。

此“主要”的工作方式是保留父代的ID,但不保留子代的ID(在每个“ upsert”上为子代分配新的ID)。

public void Upsert(ParentEntity parentEntity) {
    ParentEntity existing = DB.Parents.Find(parentEntity.Id);

    if (existing == null) {
        DB.Parents.Add(parentEntity);
    } else {
        var existingChildren = DB.Children.Where(m => m.ParentId == parentEntity.Id).ToArray();

        foreach (var child in parentEntity.Children) {
            child.ParentId = parentEntity.Id;
        }

        DB.Children.RemoveRange(existingChildren);
        DB.Children.AddRange(parentEntity.Children);

        DB.Entry(existing).State = EntityState.Detached;
        DB.Parents.Attach(parentEntity);
        DB.Entry(parentEntity).State = EntityState.Modified;
    }

    SaveChanges();
}

2 个答案:

答案 0 :(得分:0)

这有效:

public void Upsert(ParentEntity parentEntity) {
    ParentEntity existing = DB.Parents.Find(parentEntity.Id);

    if (existing == null) {
        DB.Parents.Add(parentEntity);
    } else {
        var newChildKeys = new HashSet<int>(parentEntity.Children.Select(m => m.Id));

        var existingChildren = DB.Children.Where(m => m.ParentId == parentEntity.Id).ToArray();
        var existingChildrenKeys = new HashSet<int>(existingChildren.Select(m => m.Id));

        foreach (var existingChild in existingChildren) {
            if (newChildKeys.Contains(existingChild.Id)) {
                DB.Entry(existingChild).State = EntityState.Detached;
            } else {
                DB.Children.Remove(existingChild);
            }
        }

        foreach (var child in parentEntity.Children) {
            child.ParentId = parentEntity.Id;
            if (existingChildrenKeys.Contains((child.Id))) {
                DB.Children.Attach(child);
                DB.Entry(child).State = EntityState.Modified;
            } else {
                DB.Children.Add(child);
            }
        }

        DB.Entry(existing).State = EntityState.Detached;
        DB.Parents.Attach(parentEntity);
        DB.Entry(parentEntity).State = EntityState.Modified;
    }

    SaveChanges();
}

也许可以从样式角度进行改进/使其更通用。基本思想是

  • 查找各个实体(数据库中存在的实体或替换实体中)的公用密钥
  • 对于公共键,分离旧实体,附加新实体并标记为已修改
  • 对于不再存在的密钥-删除实体
  • 对于新键-添加实体

答案 1 :(得分:-1)

这是我用来获取想要的东西的东西。就我而言,它很棒:

$result = Customer::select(Customer::raw("SELECT
    COUNT(CASE WHEN cityId IS NULL AND postCode IS NULL then 1 ELSE NULL END) as \"data1\",
    COUNT(CASE WHEN cityId IN (123, 456) then 1 ELSE NULL END) as \"data2\",
    COUNT(CASE WHEN cityId IN (789,999) then 1 ELSE NULL END) as \"data3\",
    COUNT(CASE WHEN cityId IN (111, 222) then 1 ELSE NULL END) as \"data4\",
    COUNT(CASE WHEN cityId IS NOT NULL AND cityId NOT IN (123, 456, 789, 999, 111, 222) then 1 ELSE NULL END ) as \"data5\"
from trial
        "))->get();

这将分离数据库中已经存在的所有实体,这意味着它们将不会被处理。