C# 使用实体框架同时更新和添加子行

C# 使用实体框架同时更新和添加子行,c#,asp.net,entity-framework,C#,Asp.net,Entity Framework,在同时修改和添加子行时,我遇到了一些麻烦。我正在使用答案中的技巧: 以下代码中存在问题: public void EditReport(tbl_inspection inspection) { foreach (var roll in inspection.tbl_inspection_roll) { container.tbl_inspection_roll.Attach(roll); container.

在同时修改和添加子行时,我遇到了一些麻烦。我正在使用答案中的技巧:

以下代码中存在问题:

public void EditReport(tbl_inspection inspection)
{
    foreach (var roll in inspection.tbl_inspection_roll)
    {                    
        container.tbl_inspection_roll.Attach(roll);
        container.ObjectStateManager.ChangeObjectState(roll, (roll.id_inspection_roll == 0) ? EntityState.Added : EntityState.Modified);
    }

    container.SaveChanges();
}
我总是至少有一行要更新。当我有一行要添加时,它工作正常,问题是当我尝试同时添加多行时,显示了一个众所周知的错误:

ObjectStateManager中已存在具有相同密钥的对象。 ObjectStateManager无法跟踪具有相同属性的多个对象 钥匙


感觉我在这里遗漏了什么…

我认为您需要将修改的内容从添加的内容中分离出来。在您链接到Ladislav的问题中,有以下代码作为示例:

if (myEntity.Id != 0)
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();
我认为导致错误的原因是使用Attach而不是AddObject

编辑:请改为对附着零件尝试以下操作:

var r = new tbl_inspection_roll { id_inspection_roll = roll.id_inspection_roll };
container.tbl_inspection_roll.Attach(r);
container.Entry(r).CurrentValues.SetValues(roll);

如果我正确理解了这个问题,那么您正在尝试保存包含ParentObject和相关ChildObject的对象图,并且您得到了异常,因为Attach()无法协调数据库中的ChildObject和附加的ParentObject之间的Id冲突

下面是我过去使用过的一个工作环境。这并不漂亮,但它完成了任务

public ParentObject Upsert(ParentObject newParentObject)
{
    // Check for an existing ParentObject.
    ParentObject exisitingParentObject = container.ParentObjects.SingleOrDefault(o => o.Id == newParentObject.Id);

    // If not an existing ParentObject
    if (exisitingParentObject == null)
    {
        // Add a new ParentObject
        exisitingParentObject = container.ParentObjects.Add(container.ParentObjects.Create());
    }

    // Update the properties of the existing ParentObject with the values of the new ParentObject
    var parentEntry = container.Entry(exisitingParentObject);
    parentEntry.CurrentValues.SetValues(newParentObject);

    // Remove ChildObjects that are in the existing but not in the new.
    for (int i = exisitingParentObject.ChildObjects.Count() - 1; i >= 0; i--)
    {
        ChildObject exisitingChildObject = exisitingParentObject.ChildObjects[i];
        if (!newParentObject.ChildObjects.Any(o => o.Id == exisitingChildObject.Id))
        {
            container.ChildObjects.Remove(exisitingChildObject);
        }
    }

    // Upsert new ChildObjects
    foreach (ChildObject newChildObject in newParentObject.ChildObjects)
    {
        // Check for an existing ChildObject
        ChildObject exisitingChildObject = exisitingParentObject.SingleOrDefault(o => o.Id == newChildObject.Id);

        // If not an existing ChildObject
        if (exisitingChildObject == null)
        {
            // Add a new ChildObject
            exisitingChildObject = exisitingParentObject.ChildObjects.Add(container.ChildObject.Create());
        }

        var childEntry = container.Entry(exisitingChildObject);
        childEntry.CurrentValues.SetValues(newChildObject);
    }

    container.SaveChanges();

    return exisitingParentObject;
}

这里的问题是因为两个或多个子存根具有相同的键:0。尝试附加第一个对象后,将触发错误

该方法必须重新设计,使用某种DTO(我认为将ViewModel对象传递给域模型层是不正确的,这就是我使用存根的原因)。或者直接从控制器调用函数来添加/修改

编辑:

代码如下:

public void EditReport(Inspection obj)
{
    var inspection = new tbl_inspection
    {
        id_inspection = obj.ID,
        code = obj.Code       
    };

    foreach (var roll in obj.Rolls)
    {                    
        var rollStub = new tbl_inspection_roll
        {
            id_inspection_roll = roll.ID,
            id_inspection = obj.ID,
            description = roll.Description
        };

        container.tbl_inspection_roll.Attach(roll);
        container.ObjectStateManager.ChangeObjectState(roll, (roll.id_inspection_roll == 0) ? EntityState.Added : EntityState.Modified);
    }

    container.tbl_inspection.Attach(inspection);
    container.ObjectStateManager.ChangeObjectState(inspection, EntityState.Modified);

    container.SaveChanges();
}

欢迎提供更好的解决方案……

1)检查您的tbl_检查表,确保没有重复项。2) 在调试器中,检查tbl_检查列表中这些记录的状态,然后再附加它们,并查看其中一条记录是否已被视为已附加。3) 同样在调试器中,检查是否在本地集合中也找到任何tbl_检查记录。我以前也遇到过这种情况……我检查了您描述的所有项目,看起来很好,尽管当我尝试添加多行时,两行的id\u inspection\u roll都等于零。这意味着他们是平等的?有什么不同的方法吗?非常感谢您的帮助。仍然无法工作:(…我想我必须添加一些引用或类似的内容…我试图设置tbl_inspection_rollReference.EntityKey,但仍然不起作用…它在哪一行失败?附加?附加之前,它是否表示实体已分离,或已附加?您在本地集合中找到该记录了吗?如果是f在本地集合上,它会爆炸,因为它已经知道跟踪该记录。如果它不在那里,那么我有一个不同的解决方案。不,不,它最初来自html表单,然后我使用存根创建整个对象,并传递给我发布的函数…我以前对这些实体进行过一些查询,但我使用MergeOption。不跟踪…是的。我对必须这样做并不感到兴奋,但我知道它是有效的。如果其他人有更干净的方法来做,我也想听听。一个存根实体,我不知道您调用Attach()的上下文但是我在ApiController中的Put操作中调用它,所以我的实体本质上是一个存根。如何使它不是存根?一旦它不是存根,为什么需要调用Attach()总之?我在使用mvc…所以我在控制器操作中创建存根对象,并将其传递给我发布的函数…一旦其分离,我必须附加它…这样EF在容器处Id=0的多个转鼓时不会犹豫。tbl\U检查\U转鼓。附加(转鼓)?不…如果你有一个包含父对象和子对象的完整填充存根实体,它只会触发错误…我也没有感觉…但是这样工作…现在我有一个设计问题…我应该将什么类型的对象传递给这个函数…我应该在哪一层保留它…我不知道这是否适用于你,但我有DT我的控制器将其作为参数并返回的操作系统。控制器负责将DTO转换为存根实体,并将其传递到存储库对象上调用的方法。存储库对象处理与我的DbContext之间的所有附加/添加/分离。我将替换我的reposi中存在的应答中的代码我用视图模型代替DTO,它在一个与MVC应用程序分离的项目中…然后我的控制器创建了存根并传递给存储库中的方法。但是一旦我必须将对象直接传递给存储库,我就不知道该使用哪种方法了…你同意吗是否也在MVC应用程序中使用DTO?我有一个MVC应用程序项目,其中也包含我的实体,还有一个可移植类库项目,其中包含我的DTO。DTO项目是可移植的,因为我的MVC应用程序和Silverlight客户端应用程序都使用它。使用相同的DTO库,服务器端和客户端都可以生成JSON(反)序列化更容易。控制器将传入的JSON反序列化为DTO,将DTO转换为存根实体,将存根实体传递到存储库,将生成的非存根实体转换回DTO,然后在响应中将DTO序列化为JSON。