Проблема с EF "Save", модифицированный набор дочерних объектов - PullRequest
3 голосов
/ 15 ноября 2011

У меня есть родительская сущность (Лечение) с коллекцией дочерних сущностей (Сегменты). У меня есть метод сохранения, который выполняет обработку, определяет, является ли он новым или существующим, а затем либо добавляет его в objectContext, либо присоединяет его к контексту объекта в зависимости от того, новый он или существующий.

То же самое происходит с детьми в основном существе. Он перебирает коллекцию дочерних объектов, а затем добавляет или обновляет в зависимости от ситуации.

То, что я пытаюсь сделать, это удалить все дочерние объекты, которые отсутствуют. Проблема заключается в том, что когда я обновляю родительский объект, а затем присоединяю его к контексту объекта, у родительского объекта появляется коллекция дочерних объектов из БД. Это не коллекция, в которую я был первоначально передан. Поэтому, если у меня была обработка с 3 сегментами, и я удаляю один сегмент из коллекции, а затем передаю обработку в мой метод сохранения, как только объект обработки присоединяется к объектному контексту, количество сегментов изменилось с 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;
        }

* EDIT **** Основываясь на некоторых комментариях ниже, я изменил метод «Сохранить». Реализация нового метода приведена ниже. Однако он по-прежнему не удаляет сегменты, которые были удалены из коллекции myTreatments.Segments.

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

ЗАКЛЮЧИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ Я наконец получил это, чтобы работать. Вот обновленный метод Save, который работает правильно. Мне пришлось сохранить первоначальный список сегментов в локальной переменной, а затем сравнить его со списком myTreatments.Segments после его присоединения к БД, чтобы определить список сегментов, которые нужно удалить, а затем выполнить итерацию по этому списку и удалить соответствующие Сегменты из недавно добавленного списка myTreatment.Segments. Я также удалил передачу объекта objectcontext по совету нескольких респондентов ниже.

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

Ответы [ 2 ]

1 голос
/ 16 ноября 2011

Может быть, я что-то упускаю, но для меня этот код выглядит слишком громоздким.Пожалуйста, потерпите меня, если я нахожусь на неправильном пути и неправильно вас понял.

Что касается объектов, которые должны быть удалены, я предлагаю вам сохранить их в отдельной коллекции, которая содержит только удаленные элементы.Вы можете удалить их из ObjectContext.

Вместо того, чтобы вызывать ApplyCurrentValues, я бы просто позвонил myObjectContext.SaveChanges().В этом случае ApplyCurrentValues ​​имеет недостаток, заключающийся в том, что он не заботится о какой-либо другой сущности, имеющей отношение к той, которую вы сохраняете.

Документация MSDN:

Копирует скалярные значения из предоставленного объекта в объект в ObjectContext с тем же ключом.

Поскольку другие Segments уже присоединены к вашему Treatment, с помощью SaveChanges () они будут добавлены в контекст автоматически или обновлены, если они уже были добавлены.

Это должно сделать ненужной ручную обработку EntityStates.

РЕДАКТИРОВАТЬ: Теперь я вижу, куда это идет ...

Где-то в вашем коде - вне этого метода Save () - вы удаляете экземпляры сегмента.Проблема заключается в том, что ваш ObjectContext совершенно не знает этого.И как это должно быть ...?

Возможно, вы уничтожили экземпляр определенной сущности Сегмента, но, поскольку сущности отсоединены , это означает, что у них нет связи в ObjectContext.Поэтому контекст абсолютно не имеет представления о том, что вы сделали.

Как следствие, когда вы присоединяете к нему обработку, контекст все еще полагает, что все сегменты живы, потому что он не знает об удалении, и добавляетих снова к Обработке , как будто ничего не случилось.

Решение: Как я уже говорил выше, вам нужно отслеживать ваших удаленныхлица.

В тех местах, где вы удаляете сегменты, фактически не удаляйте их, а:

  1. Remove() это из экземпляра обработки.
  2. Переместите «удаленный» сегмент в коллекцию, например List<Segment>.Давайте назовем его удаленными сегментами.
  3. Передайте коллекцию удаленных сегментов в метод Save()
  4. Переберите эту коллекцию и ObjectContect.Delete() их.
  5. Сделайте оставшуюся часть сохранениялогика по необходимости.

Также, как упоминал Томас Ворачек, предпочтительно использовать контексты более локально .Создайте его только в методе save, а не в качестве аргумента.

1 голос
/ 15 ноября 2011

Хорошо, попробуйте еще раз.

Когда вы говорите «удалить», вы подразумеваете пометку как удаленную.

Вы вызываете ChangeObjectState, чтобы изменить состояние на измененное.

Итакесли вы отправите 3, один удаленный, один измененный и один без изменений;тогда все будут помечены как измененные до вызова сохранения изменений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...