2011-02-01 6 views
5

Otrzymuję błąd z NHibernate, gdy próbuję wykonać polecenie ISession.Delete na dowolnym stole ze związkiem One to Many.Fluent NHibernate Mapping jest ustawiony na AllDeleteOrphan, ale nadal próbuje zerować klucz obcy w DB

NHibernate próbuje ustawić klucz obcy na tabeli nadrzędnej w tabeli podrzędnej na wartość null, zamiast po prostu usuwać wiersz tabeli podrzędnej.

Oto moja domena:

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; } 
} 

Mam konfiguracji mapowania Fluent NHibernate następująco:

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(); 
    } 
} 

Mówiłem NHibernate do Cascade.AllDeleteOrphan() ale to wciąż próbuje ustawić parentId foriegn key to null to test, który ustawiłem:

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); 
    } 
} 

Który nie działa n linia deleteSession.Delete po próbie następujące SQL:

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

z:

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. 

Czy ktoś wie, co zrobiłem źle w moich odwzorowań, czy znam sposób, aby zatrzymać NHibernate z próbą null identyfikator klucza obcego?

Dzięki

Dave

Odpowiedz

19

Spróbuj ustawić .Inverse() na ParentMap za hasMany, tak to wygląda:

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

Legenda .. dziękuję .. Pomyślałem, że Odwrotność oznaczałaby, że Zapisywanie zakończyłoby się niepowodzeniem, ponieważ próbowałoby wstawić do rekordu Dziecko, zanim zostanie wstawione do rekordu Nadrzędnego. Właśnie poprowadziłem pełne testy Crud i wszystkie kaskady pracują uczta .. dziękuję :) – CraftyFella

+1

Miło mi pomóc. Jest przyzwoity wątek SO na temat odwrotności, jeśli pomaga lepiej wyjaśnić: http://stackoverflow.com/questions/1061179/when-to-use-inverse-false-on-nhibernate-hibernate-onetomany-relationships – nkirkes

+0

To działa w porządku! –

0

Nie jestem pewien, czy kaskada działa jeśli usuniesz z HQL.

Spróbuj tego:

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

Szkoda, że ​​nie masz leniwy załadunku. Load nie potrzebował by element był odczytywany z bazy danych, po prostu tworzyłby proxy w pamięci.

+0

Hi, próbowałem powyżej produkuje te same instrukcje SQL jak wcześniej, co ostatecznie prowadzi do tego samego wyjątku więzów powstawszy przez DB. "Nie można wstawić wartości NULL do kolumny" ParentId ", tabela" SaveUpdateCopyTestingDB.dbo.Child ", kolumna nie zezwala na wartości zerowe UPDATE nie działa Instrukcja została zakończona." Jakieś inne pomysły? – CraftyFella