Получить исключение дубликата ключа при удалении, а затем повторно добавить дочерние строки с Entity Framework - PullRequest
3 голосов
/ 09 октября 2009

Я использую Entity Framework для моделирования простых родительских и дочерних отношений между документом и его страницами. Следующий код должен (в этом порядке):

  • сделать несколько обновлений свойств документа
  • удалить любую из существующих страниц документа
  • вставить новый список страниц, переданных в метод.

Новые страницы имеют те же ключи, что и удаленные страницы, поскольку существует индекс, который состоит из номера документа, а затем номера страницы (1..n).

Этот код работает. Однако, когда я удаляю первый вызов SaveChanges, он завершается неудачно с:

System.Data.SqlClient.SqlException: Cannot insert duplicate key row in object 
'dbo.DocPages' with unique index 'IX_DocPages'.

Вот рабочий код с двумя вызовами SaveChanges:

        Document doc = _docRepository.GetDocumentByRepositoryDocKey(repository.Repository_ID, repositoryDocKey);

        if (doc == null) {
            doc = new Document();
            _docRepository.Add(doc);
        }
        _fieldSetter.SetDocumentFields(doc, fieldValues);

        List<DocPage> pagesToDelete = (from p in doc.DocPages
                                       select p).ToList();

        foreach (DocPage page in pagesToDelete) {
            _docRepository.DeletePage(page);
        }

        _docRepository.GetUnitOfWork().SaveChanges();  //IF WE TAKE THIS OUT IT FAILS

        int pageNo = 0;
        foreach (ConcordanceDatabase.PageFile pageFile in pageList) {
            ++pageNo;
            DocPage newPage = new DocPage();
            newPage.PageNumber = pageNo;
            newPage.ImageRelativePath = pageFile.Filespec;
            doc.DocPages.Add(newPage);
        }

        _docRepository.GetUnitOfWork().SaveChanges();  //WHY CAN'T THIS BE THE ONLY CALL TO SaveChanges

Если я оставлю код как написано, EF создаст две транзакции - по одной для каждого вызова SaveChanges. Первый обновляет документ и удаляет все существующие страницы. Вторая транзакция вставляет новые страницы. Я изучил трассировку SQL, и вот что я вижу.

Однако , если я удаляю первый вызов SaveChanges (потому что я хотел бы, чтобы все это выполнялось в одной транзакции), EF таинственным образом вообще не удаляет, а генерирует только вставки ?? - которые приводят к ошибке дубликата ключа. Я не думаю, что ожидание вызова SaveChanges должно иметь значение здесь?

Между прочим, при вызове _docRepository.DeletePage (page) выполняется objectContext.DeleteObject (page). Кто-нибудь может объяснить это поведение? Спасибо.

Ответы [ 2 ]

4 голосов
/ 09 октября 2009

Я думаю, что более вероятное объяснение состоит в том, что EF выполняет удаление, но, вероятно, делает это после вставки, поэтому вы в конечном итоге проходите через недопустимое состояние.

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

Итак, вам нужно два вызова SaveChanges ().

Один из вариантов - создать упаковку TransactionScope.

Затем вы можете дважды вызвать SaveChanges (), и все это происходит внутри одной транзакции.

См. этот пост для получения дополнительной информации о связанных техниках

Надеюсь, это поможет

Alex

0 голосов
/ 09 октября 2009

Спасибо, Алекс. Это очень интересно Я действительно решил обернуть все это в область транзакции, и она отлично работала с двумя SaveChanges () - которые, как вы указываете, кажутся необходимыми из-за конфликта первичного ключа с удалениями и последующими вставками , Новая проблема теперь возникает на основе статьи, с которой вы ссылались. Правильно рекомендуется вызывать SaveChanges (false) - инструктировать EF хранить изменения, потому что область внешних транзакций будет фактически контролировать, будут ли эти изменения когда-либо вноситься в базу данных. Как только управляющий код вызывает scope.Complete (), шаблон должен затем вызвать context EF.AcceptAllChanges (). Но я думаю, что это будет проблематично для меня, потому что я вынужден вызывать SaveChanges ДВАЖДЫ для первоначально описанной проблемы. Если оба этих вызова SaveChanges задают значение False для параметра принять изменения, я подозреваю, что второй вызов в конечном итоге будет повторять SQL из первого. Я боюсь, что я могу быть в Catch-22.

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