2

我通常擁有的一切設置使用慣例,像這樣以層疊所有:功能NHibernate級聯公約問題 - 刪除的實例傳遞給更新

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,由於不允許使用空值,因此會失敗。如果刪除語句按照代碼順序在此之前運行,這不會成爲問題,但奇怪的是,這不會發生。

還嘗試設置級聯一對多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(); 
} 

現在我得到異常「刪除對象將被重新保存通過級聯(從關聯中刪除已刪除的對象)「。如何正確地從關聯中刪除對象?

回答

2

天哪! Chuck在question 302720: how-to-delete-child-object-in-nhibernate上提供了很好的見解後找到了解決方案。我已經改變了2個約定,所以:

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

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

這現在允許我保存父母,它的子集合將通過級聯保存。此外,它允許我打電話:

completedQuestionnaire.QuestionAnswers.Clear(); 

而且它刪除子收集實體。