2010-11-30 21 views
6

Tengo un problema con lo que debería ser un caso de uso de NHibernate bastante simple (creo).NHibernate establece la clave externa en la actualización secundaria en lugar de en la inserción inicial que viola la restricción no nula en la columna clave

Uno de mis padres clásico y una entidad secundaria, así:

public class Parent 
{ 
    public virtual int ParentId { get; set; } 
    public virtual string Name { get; set; } 
    public virtual IList<Child> Children { get; set; } 
} 

public class Child 
{ 
    public virtual int ChildId { get; set; } 
    public virtual Parent Parent { get; set; } 
    public virtual string Name { get; set; } 
} 

y las asignaciones de la siguiente manera:

public class ParentMap : ClassMap<Parent> 
{ 
    public ParentMap() 
    { 
     Id(x => x.ParentId).GeneratedBy.Native(); 
     Map(x => x.Name); 
     HasMany(x => x.Children).KeyColumn("ParentId").Cascade.SaveUpdate(); 
    } 
} 

public class ChildMap : ClassMap<Child> 
{ 
    public ChildMap() 
    { 
     Id(x => x.ChildId).GeneratedBy.Native(); 
     Map(x => x.Name); 
     References(x => x.Parent).Column("ParentId").ReadOnly().Not.Nullable(); 
    } 
} 

Por último, tengo unas pruebas sencillas:

[Test] 
    public void Test_save_family() 
    { 
     var parent = new Parent(); 
     var child = new Child {Parent = parent}; 
     parent.Children = new List<Child>{child}; 

     SessionManager.WithSession(
      session => 
       { 
        session.Save(parent); 
        session.Flush(); 
       }); 

    } 

La prueba falla con System.Data.SqlClient.SqlException: No se puede insertar el valor NULL en la columna 'ParentId'. Esto es correcto porque la columna no admite nulos, pero ¿por qué está insertando nulo?

Si quito la restricción nula, la obra ahorrar porque NHibernate primero inserta el padre, a continuación, inserta el niño, luego actualiza la columna de la parentid en el registro hijo como se muestra en esta salida:

NHibernate: INSERT INTO [Parent] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = NULL 
NHibernate: INSERT INTO [Child] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = NULL 
NHibernate: UPDATE [Child] SET ParentId = @p0 WHERE ChildId = @p1;@p0 = 2, @p1 = 1 

Esto parece Es extraño para mí, ya que en casi todos los casos, las columnas de clave externa de este tipo se declaran no nulables y, por lo tanto, la clave externa se debe proporcionar en la inserción. Entonces, ¿por qué NHibernate no está configurando la clave externa en la inserción inicial de la fila secundaria y cómo corregir esto?

Respuesta

7

Algunos problemas con su mapeo ... Usted tiene una relación bidireccional y NHibernate necesita saber de qué manera actualizarla. En el mundo OO, las referencias solo van en una dirección y no hay forma de que NHibernate sepa que Parent-> Children es el mismo FK que Child-> Parent. En este momento, tiene Child-> Parent establecido en ReadOnly(). Esto le está diciendo a NHibernate que no actualice esta propiedad. Por lo tanto, intenta insertar el elemento secundario (con un elemento primario nulo) y luego actualiza el archivo FK desde el lado principal. Esto no funciona si tienes una restricción no nula en tu FK. La forma habitual de mapear esto es utilizar Inverso = verdadero en el lado padre y dejar que el niño se preocupe por la persistencia. (Es su trabajo en el modelo orientado a objetos para asegurarse de que la colección de Padres> Niños contiene el mismo conjunto de referencias como el conjunto de Child-> relaciones padre.)

public class ParentMap : ClassMap<Parent> 
{ 
    public ParentMap() 
    { 
     Id(x => x.ParentId).GeneratedBy.Native(); 
     Map(x => x.Name); 
     HasMany(x => x.Children).KeyColumn("ParentId").Inverse().Cascade.SaveUpdate(); 
    } 
} 

public class ChildMap : ClassMap<Child> 
{ 
    public ChildMap() 
    { 
     Id(x => x.ChildId).GeneratedBy.Native(); 
     Map(x => x.Name); 
     References(x => x.Parent).Column("ParentId").Not.Nullable(); // Removed ReadOnly() 
    } 
} 

Las sentencias SQL enviados a la base de datos al guardar son ahora:

INSERT INTO [Parent] 
      (Name) 
VALUES  ('P1' /* @p0 */) 
select SCOPE_IDENTITY() 

INSERT INTO [Child] 
      (Name, 
      ParentId) 
VALUES  ('C1' /* @p0 */, 
      1 /* @p1 */) 
select SCOPE_IDENTITY() 
+0

James, bien retirar el calificativo de sólo lectura() trabajó para conseguir la aprobación de prueba. Sin embargo, ahora estoy confundido sobre el significado de ReadOnly(). Según tengo entendido, ReadOnly() significa que NHibernate nunca actualizará la columna después de la inserción inicial, y que nunca asignará un valor a la inserción. –

+0

ReadOnly() significa que el valor nunca se escribe en insertar o actualizar. Se usa principalmente para tablas de búsqueda y columnas calculadas. –

+0

Hmm, lo sabía, ahora que lo mencionas. Creo que confundí ReadOnly() como un atributo de propiedad con ReadOnly como un atributo de entidad. Debe envejecer. :) ¡Gracias por la ayuda! –

Cuestiones relacionadas