2011-02-01 12 views
5

Ricevo un errore con NHibernate quando provo a perfrom un'ISession.Delete su qualsiasi tabella con una relazione Uno a Molti.Fluent NHibernate Mapping è impostato su AllDeleteOrphan ma sta ancora tentando di azzerare la chiave esterna nel DB

NHibernate sta tentando di impostare la chiave esterna nella tabella padre nella tabella figlio su null, anziché eliminare semplicemente la riga della tabella figlio.

Ecco il mio dominio:

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

ho set-up le mappature Fluent NHibernate come segue:

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

Ho detto NHibernate per Cascade.AllDeleteOrphan() ma è ancora cercando di impostare il ParentId chiave per null qui è il test che ho impostato:

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

Che non funziona o n linea deleteSession.Delete dopo aver tentato il seguente SQL:

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

con:

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. 

Qualcuno sa che cosa ho fatto di sbagliato nelle mie mappature, o sa di un modo per fermare NHibernate di tentare di null l'id della chiave esterna?

Grazie

Dave

risposta

19

Prova impostazione .Inverse() sul hasMany del ParentMap, così sembra:

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

Legenda .. grazie .. Ho pensato che Inverse significherebbe che Save fallirebbe, come proverebbe a inserire nel record Child prima che venisse inserito nel record Parent. Ho appena eseguito i test completi di Crud e tutte le cascate funzionano a meraviglia .. grazie :) – CraftyFella

+1

Felice di aiutare. C'è un discreto thread SO sull'uso dell'inverso se aiuta a spiegare meglio: http://stackoverflow.com/questions/1061179/when-to-use-inverse-false-on-nhibernate-hibernate-onetomany-relationships – nkirkes

+0

Che funziona bene! –

0

non sono sicuro se cascata funziona se si elimina con HQL.

Prova questa:

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

E 'un peccato che non si dispone di lazy loading. Load non avrebbe bisogno dell'entità da leggere dal database, creerebbe semplicemente un proxy in memoria.

+0

Ciao, provato quanto sopra e produce le stesse istruzioni SQL come prima, in ultima analisi, con conseguente la stessa eccezione vincolo di essere sollevato dal DB. "Impossibile inserire il valore NULL nella colonna 'ParentId', la tabella 'SaveUpdateCopyTestingDB.dbo.Child'; la colonna non consente i valori null. UPDATE non riesce. L'istruzione è stata chiusa." Altre idee? – CraftyFella

Problemi correlati