流畅的NHibernate级联约定问题 - 删除的实例传递给更新

时间:2011-07-14 16:14:31

标签: fluent-nhibernate one-to-many conventions cascade nhibernate-cascade

我通常使用约定将所有设置为全部级联,如下所示:

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实例的集合。没问题。

但是,我也想更新这些问题的答案。在这种情况下,查询和完成的问题需要保持不变。需要发生的是要删除所有问题的答案并创建新的答案(因为该列表的大小增大或缩小)。

所以服务方法是:

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源代码中找到了这个引用的引用。如果我尝试在IOneToManyCollectionInstance上更改级联到All或UpdateAndSave,NHibernate会尝试将QuestionAnswer记录的CompletedQuestionnaire外键更新为null,因为不允许空值。如果删除语句按照代码顺序在此之前运行,这不会有问题,但奇怪的是这不会发生。

还尝试在一对多上设置级联到DeleteOrphan,这给了相同的已删除实例传递异常。

上述服务方法实际上是反复试验的结果。我很感激任何有关“你做错了!”的见解和解释。

是否可以控制使用NHiberante执行的语句的顺序。说一个像DeleteBeforeUpdate或类似的约会吗?

编辑:我已经将服务方法重新设计为:

public CompletedQuestionnaire UpdateCompletedQuestionnaire(long completedQuestionnaireId, IEnumerable<QuestionAnswer> newAnswers)
{
    var completedQuestionnaire = _completedQuestionnaireRepository.Single(q => q.CompletedQuestionnaireId == completedQuestionnaireId);
    Guard.AgainstEntityLoadException(completedQuestionnaire, completedQuestionnaireId);
    ((IList<QuestionAnswer>)completedQuestionnaire.QuestionAnswers).Clear();
    foreach (var newAnswer in newAnswers)
    {
        newAnswer.CompletedQuestionnaire = completedQuestionnaire;
        _questionAnswerRepository.Add(newAnswer);
    }
    _completedQuestionnaireRepository.Update(completedQuestionnaire);
    UnitOfWork.Commit();
    return completedQuestionnaire;
}

并将相关的级联惯例更改为:

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

现在我得到异常“已删除的对象将通过级联重新保存(从关联中删除已删除的对象)”。如何才能正确地从关联中删除对象?

1 个答案:

答案 0 :(得分:2)

的whooo!在Chuck提供了对question 302720: how-to-delete-child-object-in-nhibernate的出色见解后找到了解决方案。我改变了两个约定,以便:

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

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

现在允许我保存父级,它的子级集合将通过级联保存。它也允许我打电话:

completedQuestionnaire.QuestionAnswers.Clear();

它会删除子集合实体。