2011-02-01 13 views
5

Recibo un error con NHibernate cuando intento realizar una ISession.Delete en cualquier tabla con una relación Uno a Muchos.La correspondencia fluida de NHibernate está establecida en AllDeleteOrphan, pero aún está tratando de anular la clave externa en el DB

NHibernate está tratando de establecer la clave externa a la tabla principal en la tabla secundaria como nula, en lugar de simplemente eliminar la fila de la tabla hija.

Aquí es mi 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; } 
} 

he puesto en marcha las asignaciones Fluido NHibernate de la siguiente manera:

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

le he dicho a NHibernate a Cascade.AllDeleteOrphan() pero todavía está tratando de establecer el parentid aquí la clave para anular es la configuración de la prueba I:

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

¿Qué falla? n la línea deleteSession.Delete después de intentar el siguiente 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. 

¿Alguien sabe lo que he hecho mal en mis asignaciones, o sabe de una manera de detener NHibernate de intentar anula la identificación de la clave externa?

Gracias

de Dave

Respuesta

19

intente configurar .Inverse() en hasMany del ParentMap, por lo que parece:

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

Leyenda ... gracias. Pensé que Inverso significaría que Saved fallaría, ya que trataría de insertar en el registro de Child antes de insertarlo en el registro de Parent. Acabo de ejecutar pruebas completas de Crud y todas las cascadas funcionan de maravilla ... gracias :) – CraftyFella

+1

Me alegra ayudar. Hay un hilo SO decente en el uso de la inversa si ayuda a explicar mejor: http://stackoverflow.com/questions/1061179/when-to-use-inverse-false-on-nhibernate-hibernate-onetomany-relationships – nkirkes

+0

Eso funciona ¡multa! –

0

no estoy seguro si funciona en cascada si se elimina con HQL.

Prueba esto:

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

Es una lástima que usted no tiene carga diferida. Load no necesitaría que la entidad fuera leída de la base de datos, simplemente crearía un proxy en la memoria.

+0

Hola, intentaron lo anterior y produce las mismas sentencias SQL como antes, lo que se traduce en la misma excepción restricción de haber sido criado por el DB. "No se puede insertar el valor NULL en la columna 'ParentId', la tabla 'SaveUpdateCopyTestingDB.dbo.Child'; la columna no permite nulos. ACTUALIZACIÓN falla. La declaración ha finalizado." ¿Alguna otra idea? – CraftyFella

Cuestiones relacionadas