Свободная проблема каскадных соглашений NHibernate - удаленный экземпляр передан для обновления - PullRequest
2 голосов
/ 14 июля 2011

Обычно у меня все настроено для каскадирования всех по соглашению, например так:

public class CascadeAllConvention : IHasOneConvention, IHasManyConvention, IReferenceConvention
{
    public void Apply(IOneToOneInstance instance)
    {
        instance.Cascade.All();
    }

    public void Apply(IOneToManyCollectionInstance instance)
    {
        instance.Cascade.All();
    }

    public void Apply(IManyToOneInstance instance)
    {
        instance.Cascade.All();
    }
}

Также используется автоматическое отображение Fluent NHibernate.Обычно это работает очень хорошо, потому что не нужно беспокоиться о конкретных заказах на сохранение и тому подобное.Тем не менее, это становится проблемой в следующем сценарии:

public class QuestionAnswer
{
    public CompletedQuestionnaire CompletedQuestionnaire { get; set; }
}

public class CompletedQuestionnaire
{
    public long CompletedQuestionnaireId { get; set; }
    public IEnumerable<QuestionAnswer> { get; set; }
}

public class Enquiry
{
    EnquiryId { get; set; }
    CompletedQuestionnaire { get; set; }
}

Итак, когда я сохраняю новый запрос, все, что мне нужно сохранить в сервисном методе, это экземпляр запроса, в котором установлен CompletedQuestionnaire.и это в свою очередь имеет коллекцию экземпляров QuestionAnswer.Никаких проблем там нет.

Однако я также хочу обновить ответы на эти вопросы.В этом случае вопросник и вопросник CompletedQuestion должны остаться прежними.Что должно произойти, так это все ответы на вопросы, которые нужно удалить, и создать новые (потому что размер этого списка увеличивается или уменьшается).

Итак, метод обслуживания:

public CompletedQuestionnaire UpdateCompletedQuestionnaire(CompletedQuestionnaire completedQuestionnaire)
{
    var oldCompletedQuestionnaire = _completedQuestionnaireRepository.Single(q => q.CompletedQuestionnaireId == completedQuestionnaire.CompletedQuestionnaireId);
    Guard.AgainstEntityLoadException(oldCompletedQuestionnaire, completedQuestionnaire.CompletedQuestionnaireId);
    foreach (var oldQuestionAnswer in oldCompletedQuestionnaire.QuestionAnswers)
        _questionAnswerRepository.Delete(oldQuestionAnswer);
    var answerCount = oldCompletedQuestionnaire.QuestionAnswers.Count();
    for (var index = 0; index < answerCount; index++ )
        ((IList<QuestionAnswer>) oldCompletedQuestionnaire.QuestionAnswers).RemoveAt(0);
    _completedQuestionnaireRepository.Update(oldCompletedQuestionnaire);
    foreach (var newQuestionAnswer in completedQuestionnaire.QuestionAnswers)
    {
        newQuestionAnswer.CompletedQuestionnaire = oldCompletedQuestionnaire;
        _questionAnswerRepository.Add(newQuestionAnswer);
    }
    UnitOfWork.Commit();
    return _completedQuestionnaireRepository.Single(q => q.CompletedQuestionnaireId == completedQuestionnaire.CompletedQuestionnaireId);
}

Что яполучить от этого ошибка "удаленный экземпляр передан для обновления".Я нашел ссылку только на это исключение в исходном коде NHibernate.Если я пытаюсь изменить каскадирование на Все или UpdateAndSave для IOneToManyCollectionInstance, NHibernate пытается обновить записи QuestionAnswer внешнего ключа CompletedQuestionnaire до нуля, который завершается неудачно, поскольку пустые значения не разрешены.Это не будет проблемой, если операторы удаления будут выполняться до этого в соответствии с порядком кода, но, как ни странно, этого не происходит.

Также попытался установить каскадный режим «один ко многим» для DeleteOrphan, что дает исключение для одного и того же удаленного экземпляра.

Приведенный выше метод обслуживания фактически является методом проб и ошибок.Буду признателен за понимание и объяснение «Вы делаете это неправильно!».

Можно ли контролировать порядок операторов, выполняемых с помощью NHiberante.Скажем, соглашение вроде DeleteBeforeUpdate или аналогичное?

РЕДАКТИРОВАТЬ: я переработал метод обслуживания:Теперь я получаю исключение «удаленный объект будет повторно сохранен каскадом (удалить удаленный объект из ассоциаций)».Как правильно удалить объект из ассоциаций?

1 Ответ

2 голосов
/ 15 июля 2011

Whooo! Нашел решение после того, как Чак предоставил отличную информацию по вопросу 302720: как удалить дочерний объект в nhibernate . Я изменил 2 соглашения, так что:

public void Apply(IOneToManyCollectionInstance instance)
{
    instance.Cascade.AllDeleteOrphan();
    instance.Inverse();
}

public void Apply(IManyToOneInstance instance)
{
    instance.Cascade.SaveUpdate();
}

Теперь это позволяет мне сохранить родителя, и его дочерняя коллекция будет сохранена каскадом. Также это позволяет мне звонить:

completedQuestionnaire.QuestionAnswers.Clear();

И это удаляет дочерние объекты коллекции.

...