2011-02-01 30 views
5

當我試圖在具有一對多關係的任何表上執行ISession.Delete時,出現NHibernate錯誤。流利的NHibernate映射設置爲AllDeleteOrphan,但仍試圖清空數據庫中的外鍵

NHibernate試圖將子表中的父表的外鍵設置爲null,而不是僅刪除子錶行。

這裏是我的域名:

public class Parent 
{ 

    public Parent() 
    { 
     _children = new List<Child>(); 
    } 

    public int Id { get; set; } 
    public string SimpleString { get; set; } 
    public DateTime? SimpleDateTime { get; set; } 

    private IList<Child> _children; 
    public IEnumerable<Child> Children 
    { 
     get { return _children; } 
    } 
    public void AddChild(Child child) 
    { 
     child.Parent = this; 
     _children.Add(child); 
    } 
} 


public class Child 
{ 
    public int Id { get; set; } 
    public string SimpleString { get; set; } 
    public DateTime? SimpleDateTime { get; set; } 
    [JsonIgnore] 
    public Parent Parent { get; set; } 
} 

我已經建立了良好的NHibernate映射如下:

public class ParentMap : ClassMap<Parent> 
{ 
    public ParentMap() 
    { 
     Not.LazyLoad(); 
     Id(x => x.Id); 
     Map(x => x.SimpleString); 
     Map(x => x.SimpleDateTime); 

     HasMany(x => x.Children) 
      .Not.LazyLoad() 
      .KeyColumn("ParentId").Cascade.AllDeleteOrphan() 
      .Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore); 

    } 
} 

public class ChildMap : ClassMap<Child> 
{ 
    public ChildMap() 
    { 
     Not.LazyLoad(); 
     Id(x => x.Id); 
     Map(x => x.SimpleString); 
     Map(x => x.SimpleDateTime); 
     References(x => x.Parent).Not.Nullable().Column("ParentId").Cascade.All().Fetch.Join(); 
    } 
} 

我告訴NHibernate的,以Cascade.AllDeleteOrphan()但它仍然試圖設置的ParentId foriegn這裏的關鍵是空的是我設置的測試:

public void Delete_GivenTableWithChildren_WillBeDeletedFromDB() 
{ 
    int id; 
    using (var createSession = MsSqlSessionProvider.SessionFactory.OpenSession()) 
    { 
     var parent = new Parent(); 
     parent.AddChild(new Child { SimpleString = "new child from UI" }); 

     using (var trx = createSession.BeginTransaction()) 
     { 
      createSession.Save(parent); 
      trx.Commit(); 
      id = parent.Id; 
     } 
    } 

    using (var firstGetSession = MsSqlSessionProvider.SessionFactory.OpenSession()) 
    { 
     var result = firstGetSession.Get<Parent>(id); 
     Assert.IsNotNull(result); 
    } 

    using (var deleteSession = MsSqlSessionProvider.SessionFactory.OpenSession()) 
    { 
     using (var trx = deleteSession.BeginTransaction()) 
     { 
      deleteSession.Delete("from " + typeof(Parent).Name + " o where o.Id = :Id", id, NHibernateUtil.Int32); 
      trx.Commit(); 
     } 
    } 

    using (var session = MsSqlSessionProvider.SessionFactory.OpenSession()) 
    { 
     var result = session.Get<Parent>(id); 
     Assert.IsNull(result); 
    } 
} 

這是失敗的o ñ嘗試下面的SQL後deleteSession.Delete行:

exec sp_executesql N'UPDATE [Child] SET ParentId = null WHERE ParentId = @p0',N'@p0 int',@p0=5 

有:

NHibernate.Exceptions.GenericADOException : could not delete collection: [SaveUpdateOrCopyTesting.Parent.Children#5][SQL: UPDATE [Child] SET ParentId = null WHERE ParentId = @p0] 
    ----> System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'ParentId', table 'SaveUpdateCopyTestingDB.dbo.Child'; column does not allow nulls. UPDATE fails. 
The statement has been terminated. 

有誰知道我在我的映射做錯了,或知道的方式,從試圖阻止NHibernate的null外鍵ID?

感謝

戴夫

回答

19

嘗試在ParentMap的的hasMany設置.Inverse(),所以它看起來像:

HasMany(x => x.Children) 
     .Not.LazyLoad() 
     .KeyColumn("ParentId").Cascade.AllDeleteOrphan().Inverse() 
     .Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore); 
+0

圖例..謝謝..我認爲逆向意味着保存會失敗,因爲它會在插入到父記錄之前插入到子記錄中。只是跑完全Crud測試和所有級聯工作的一種享受..謝謝:) – CraftyFella 2011-02-01 15:58:21

0

我不知道,如果級聯,如果你用HQL刪除工作。

試試這個:

var parent = deleteSession.Load<Parent>(id) 
deleteSession.Delete(parent); 

這是你沒有延遲加載一個遺憾。 Load不需要從數據庫中讀取實體,它只會在內存中創建一個代理。

+0

嗨,嘗試了上面,它會產生相同的SQL語句之前,最終導致同樣的約束外升起由DB。 「 」無法將值NULL插入到'ParentId'列中,表'SaveUpdateCopyTestingDB.dbo.Child';列不允許爲NULL,UPDATE失敗 聲明已終止。 有沒有其他想法? – CraftyFella 2011-02-01 15:43:43