我通常使用约定将所有内容设置为级联,如下所示:
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();
}
}
也使用流利的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,因为不允许为null而失败。如果delete语句按照代码顺序在此之前运行,这将不会是一个问题,但奇怪的是,这种情况没有发生。
还尝试将一对多级联设置为DeleteOrphan,这会导致相同的已删除实例传递异常。
以上的服务方法实际上是反复试验的结果。对于"你做错了!"这句话,我很感激你的见解和解释。
是否可以控制nhibernate执行语句的顺序?比如DeleteBeforeUpdate之类的约定?
编辑:我已经将service方法修改为:
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();
}
现在我得到异常"被删除的对象将被级联重新保存(从关联中删除被删除的对象)"。如何正确地从关联中移除对象?
哇!在Chuck对问题302720(如何在nhibernate中删除child-object-in- hibernate)提供了出色的见解之后,找到了一个解决方案。我更改了两个约定,以便:
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Cascade.AllDeleteOrphan();
instance.Inverse();
}
public void Apply(IManyToOneInstance instance)
{
instance.Cascade.SaveUpdate();
}
现在允许我保存父集合,它的子集合将被级联保存。它还允许我调用:
completedQuestionnaire.QuestionAnswers.Clear();
并删除子集合实体