EF“保存”出现问题方法,修改子实体集合

发布于 2024-12-15 10:12:40 字数 6302 浏览 0 评论 0原文

我有一个父实体(治疗)和一组子实体(段)。我有一个 save 方法,它进行处理,确定它是新的还是现有的,然后根据它是新的还是现有的将其添加到 objectContext 或将其附加到对象上下文。

它对主实体中的子级执行相同的操作。它迭代子实体的集合,然后根据需要添加或更新。

我试图让它做的是删除任何丢失的子对象。问题是,当我更新父对象然后将其附加到对象上下文时,父对象就会拥有来自数据库的子对象的集合。不是我最初传入的集合。因此,如果我有一个包含 3 个段的 Treatment,我从集合中删除一个段,然后将 Treatment 传递到我的保存方法中,一旦 Treatment 对象附加到对象上下文,它的段数从 2 更改为 3。

我做错了什么?

这是我的保存方法的代码:

public bool Save(Treatment myTreatment, modelEntities myObjectContext)
        {
            bool result = false;

            if (myObjectContext != null)
            {
                if (myTreatment.Treatment_ID == 0)
                {
                    myObjectContext.Treatments.AddObject(myTreatment);
                }
                else
                {
                    if (myTreatment.EntityState == System.Data.EntityState.Detached)
                    {
                        myObjectContext.Treatments.Attach(myTreatment);
                    }
                    myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Modified);
                    myObjectContext.Treatments.ApplyCurrentValues(myTreatment);
                }

                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (mySegment.SegmentID == 0)
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Added);
                        myObjectContext.Segments.AddObject(mySegment);
                    }
                    else
                    {
                        if (mySegment.EntityState == System.Data.EntityState.Detached)
                        {
                            myObjectContext.Segments.Attach(mySegment);
                        }
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Modified);
                        myObjectContext.Segments.ApplyCurrentValues(mySegment);
                    }
                }
            }

            result = (myObjectContext.SaveChanges(SaveOptions.None) != 0);


            return result;
        }

*编辑**** 根据下面的一些反馈,我修改了“保存”方法。新方法的实现如下。但是,它仍然不会删除已从 myTreatments.Segments 集合中删除的 Segment。

public bool Save(Treatment myTreatment, tamcEntities myObjectContext)
        {
            bool result = false;

            if (myObjectContext != null)
            {
                if (myTreatment.Treatment_ID == 0)
                {
                    myObjectContext.Treatments.AddObject(myTreatment);
                }
                else
                {
                    if (myTreatment.EntityState == System.Data.EntityState.Detached)
                    {
                        myObjectContext.Treatments.Attach(myTreatment);
                    }
                    myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Modified);
                }

                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (mySegment.SegmentID == 0)
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Added);
                    }
                    else
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Modified);
                    }
                }
            }

            result = (myObjectContext.SaveChanges(SaveOptions.None) != 0);


            return result;
        }

最终编辑 我终于让它发挥作用了。这是更新后的保存方法,可以正常工作。我必须将初始的段列表保存在局部变量中,然后在附加到数据库后将其与 myTreatments.Segments 列表进行比较,以确定要删除的段列表,然后迭代该列表并删除匹配的新附加的 myTreatment.Segments 列表中的段。我还根据下面几个响应者的建议删除了对象上下文的传递。

public bool Save(Treatment myTreatment)
        {
            bool result = false;


            List<Segment> myTreatmentSegments = myTreatment.Segments.ToList<Segment>();

            using (tamcEntities myObjectContext = new tamcEntities())
            {
                if (myTreatment.Treatment_ID == 0)
                {
                    myObjectContext.Treatments.AddObject(myTreatment);
                }
                else
                {
                    if (myTreatment.EntityState == System.Data.EntityState.Detached)
                    {
                        myObjectContext.Treatments.Attach(myTreatment);
                    }
                    myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Modified);
                }

                // Iterate over all the segments in myTreatment.Segments and update their EntityState to force
                // them to update in the DB.
                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (mySegment.SegmentID == 0)
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Added);
                    }
                    else
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Modified);
                    }
                }

                // Create list of "Deleted" segments
                List<Segment> myDeletedSegments = new List<Segment>();
                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (!myTreatmentSegments.Contains(mySegment))
                    {
                        myDeletedSegments.Add(mySegment);
                    }
                }
                // Iterate over list of "Deleted" segments and delete the matching segment from myTreatment.Segments
                foreach (Segment mySegment in myDeletedSegments)
                {
                    myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Deleted);
                }

                result = (myObjectContext.SaveChanges(SaveOptions.None) != 0);
            }
            return result;
        }

I have a parent entity (Treatment) with a collection of child entities (Segments). I have a save method that take a treatment, determines if it's new or existing, and then either adds it to the objectContext or attaches it to the object context based on whether it is new or existing.

It does the same thing with the children within the main entity. It iterates over the collection of child entities, and then adds or updates as appropriate.

What I'm trying to get it to do, is to delete any child objects that are missing. The problem is, when I'm updating the parent object and then I attach it to the object context, the parent object then has a collection of child objects from the DB. Not the collection I originally passed in. So if I had a Treatment with 3 segments, and I remove one segment from the collection, and then pass the Treatment into my save method, as soon as the Treatment object is attached to the objectcontext, the number of segments it has is changed from 2 to 3.

What am I doing wrong?

Here is the code of my save method:

public bool Save(Treatment myTreatment, modelEntities myObjectContext)
        {
            bool result = false;

            if (myObjectContext != null)
            {
                if (myTreatment.Treatment_ID == 0)
                {
                    myObjectContext.Treatments.AddObject(myTreatment);
                }
                else
                {
                    if (myTreatment.EntityState == System.Data.EntityState.Detached)
                    {
                        myObjectContext.Treatments.Attach(myTreatment);
                    }
                    myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Modified);
                    myObjectContext.Treatments.ApplyCurrentValues(myTreatment);
                }

                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (mySegment.SegmentID == 0)
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Added);
                        myObjectContext.Segments.AddObject(mySegment);
                    }
                    else
                    {
                        if (mySegment.EntityState == System.Data.EntityState.Detached)
                        {
                            myObjectContext.Segments.Attach(mySegment);
                        }
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Modified);
                        myObjectContext.Segments.ApplyCurrentValues(mySegment);
                    }
                }
            }

            result = (myObjectContext.SaveChanges(SaveOptions.None) != 0);


            return result;
        }

*EDIT****
Based on some of the feedback below, I have modified the "Save" method. The new method implementation is below. However, it still does not delete Segments that have been removed from the myTreatments.Segments collection.

public bool Save(Treatment myTreatment, tamcEntities myObjectContext)
        {
            bool result = false;

            if (myObjectContext != null)
            {
                if (myTreatment.Treatment_ID == 0)
                {
                    myObjectContext.Treatments.AddObject(myTreatment);
                }
                else
                {
                    if (myTreatment.EntityState == System.Data.EntityState.Detached)
                    {
                        myObjectContext.Treatments.Attach(myTreatment);
                    }
                    myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Modified);
                }

                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (mySegment.SegmentID == 0)
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Added);
                    }
                    else
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Modified);
                    }
                }
            }

            result = (myObjectContext.SaveChanges(SaveOptions.None) != 0);


            return result;
        }

FINAL EDIT
I have finally got it to work. Here is the updated Save method that is working properly. I had to save the initial list of Segments in a local variable and then compare it to the myTreatments.Segments list after it was attached to the DB, to determine a list of Segments to be deleted, and then iterate over that list and delete matching Segments from the newly attached myTreatment.Segments list. I also removed the passing in of the objectcontext per advice from several responders below.

public bool Save(Treatment myTreatment)
        {
            bool result = false;


            List<Segment> myTreatmentSegments = myTreatment.Segments.ToList<Segment>();

            using (tamcEntities myObjectContext = new tamcEntities())
            {
                if (myTreatment.Treatment_ID == 0)
                {
                    myObjectContext.Treatments.AddObject(myTreatment);
                }
                else
                {
                    if (myTreatment.EntityState == System.Data.EntityState.Detached)
                    {
                        myObjectContext.Treatments.Attach(myTreatment);
                    }
                    myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Modified);
                }

                // Iterate over all the segments in myTreatment.Segments and update their EntityState to force
                // them to update in the DB.
                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (mySegment.SegmentID == 0)
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Added);
                    }
                    else
                    {
                        myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Modified);
                    }
                }

                // Create list of "Deleted" segments
                List<Segment> myDeletedSegments = new List<Segment>();
                foreach (Segment mySegment in myTreatment.Segments)
                {
                    if (!myTreatmentSegments.Contains(mySegment))
                    {
                        myDeletedSegments.Add(mySegment);
                    }
                }
                // Iterate over list of "Deleted" segments and delete the matching segment from myTreatment.Segments
                foreach (Segment mySegment in myDeletedSegments)
                {
                    myObjectContext.ObjectStateManager.ChangeObjectState(mySegment, System.Data.EntityState.Deleted);
                }

                result = (myObjectContext.SaveChanges(SaveOptions.None) != 0);
            }
            return result;
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

比忠 2024-12-22 10:12:40

也许我遗漏了一些东西,但对我来说这段代码看起来过于麻烦。
如果我在这里走错了路并且误解了你,请耐心等待。

关于应删除的对象,我建议您将它们存储在仅保存已删除项目的单独集合中。您可以从ObjectContext 中删除它们。

我不会调用ApplyCurrentValues,而是简单地调用myObjectContext.SaveChanges()。在这种情况下,ApplyCurrentValues 的缺点是它不处理与您要保存的实体有关系的任何其他实体。

MSDN 文档:

标量值从提供的对象复制到以下对象中
具有相同键的 ObjectContext。

由于其他 Segment 已附加到您的 Treatment,因此通过使用 SaveChanges(),它们将自动添加到上下文中,或者如果已添加,则进行更新。

这应该使得 EntityStates 的所有手动处理变得不必要。

编辑:
现在我明白这是怎么回事了......

在你的某个地方你编码 - 在这个 Save() 方法之外 - 你正在删除 Segment 实例。问题在于您的 ObjectContext 完全不知道这一点。又应该怎样……?

您可能已经销毁了某个 Segment 实体的实例,但由于实体是分离的,这意味着它们与 ObjectContext 没有连接。因此上下文完全不知道你做了什么。

因此,当您将处理附加到它时,上下文仍然认为所有段都处于活动状态,因为它不知道删除并将它们再次添加到处理中就像什么也没发生过一样。

解决方案:
正如我上面已经说过的,您需要跟踪已删除的实体。

在这些地方,当您删除 Segment 时,实际上并没有删除它们,而是:

  1. 从 Treatment 实例中Remove() 将其删除。
  2. 将“删除的”Segment 移动到集合中,例如List。我们称其为deletedSegments。
  3. 将deletedSegments 集合传递到Save() 方法
  4. 中循环遍历该集合和ObjectContect.Delete() 它们。
  5. 根据需要执行其余的剩余保存逻辑。

此外,就像 Tomas Voracek 提到的那样,最好本地更多地使用上下文。仅在 save 方法中创建它,而不是将其作为参数传递。

Maybe I am missing something, but to me this code looks overly cumbersome.
Please bear with me, if I am on the wrong track here and misunderstood you.

Regarding the objects that should be deleted, I suggest that you store these in a separate collection that only holds the deleted items. You can the delete them from the ObjectContext.

Instead of calling ApplyCurrentValues I would, I would simply call myObjectContext.SaveChanges(). ApplyCurrentValues has, in this case, the downside that it does not take care of any other entity that has relations to the one you are saving.

MSDN documentation:

Copies the scalar values from the supplied object into the object in
the ObjectContext that has the same key.

As the other Segments are already attached to your Treatment, by using SaveChanges(), they will be added to the context automatically or updated if they were already added.

This should make all that manual handling of the EntityStates unnecessary.

EDIT:
Now I see where this is going...

Somewhere in you you code - outside of this Save() method - you are deleting the Segment instances. The trouble lies in the problem that your ObjectContext is totally unaware of this. And how should it be...?

You may have destroyed the instance of a certain Segment entity, but as the entities are detached, this means that they have no connection to the ObjectContext. Therefore the context has absolutely no idea of what you have done.

As a consequence, when you attach the treament to it, the context still believes that all Segments are alive because it does not know about the deletion and adds them again to the Treatment like nothing ever happened.

Solution:
Like I already said above, you need to keep track of your deleted entities.

In those spots, where you delete the Segments, do not actually delete them, but:

  1. Remove() it from the Treatment instance.
  2. Move the "deleted" Segment into a collection, e.g. List<Segment>. Let's call it deletedSegments.
  3. Pass the deletedSegments collection into the Save() method
  4. Loop through this collection and ObjectContect.Delete() them.
  5. Do the rest of the remaining save logic as necessary.

Also, like Tomas Voracek mentioned, it is preferable to use contexts more locally. Create it only within the save method instead of passing it as an argument.

霊感 2024-12-22 10:12:40

好的,再试一次。

当您说“删除”时,您的意思是标记为已删除。

您正在调用 ChangeObjectState 将状态更改为已修改。

所以如果你发下来3个,1个删除,1个修改,1个不变;那么在调用保存更改之前,所有内容都会被标记为已修改。

Ok try again.

When you say "remove" do you mean mark as deleted.

You are calling ChangeObjectState to change the state to modified.

So if you send 3 down, one deleted, one modified and one unchanged; then all will get marked as modified before save changes is called.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文