2009-04-27 12 views
8

Soy nuevo en nHibernate y estoy tratando de familiarizarme con la forma correcta de actualizar los objetos desprendidos de una aplicación web desde el POST. (Estamos utilizando ASP.NET MVC)¿Cuál es la forma correcta de actualizar una entidad nhibernate desde un método de acción POST asp.net?

El objeto que estoy tratando de actualizar contiene (entre otras cosas) un IList de objetos secundarios, asigna algo como esto:

<bag name="PlannedSlices" inverse="true" cascade="all-delete-orphan"> 
     <key column="JobNumber" /> 
     <one-to-many class="SliceClass" /> 
</bag> 

Hemos organizado nuestra MVC edita el formulario de vista para que cuando se publique de nuevo, nuestro método de acción se pase a un objeto (incluida la Lista <> de elementos secundarios. Nosotros redirigimos todos los ID de la entidad correctamente a través del formulario

Nuestro ingenuo intento de el método de post-acción hace una session.SaveOrUpdate (parentObject), con el parentObject que ha sido eliminado de la vista por el modo predeterminado Encuadernador.

Esto parece funcionar bien para cualquiera de los siguientes escenarios:

  • Creación de un nuevo objeto padre
  • Modificación de las propiedades de los padres
  • Adición de nuevos objetos secundarios
  • Modificación niño objetos existentes (En cuanto a los registros de nHibernate, puedo ver que establece correctamente si los objetos son nuevos o existentes y se emite la ACTUALIZACIÓN o INSERT correcto)

El escenario que falla es: - Eliminar objetos secundarios - es decir, si no están en el IList, no se eliminan de la base de datos. No hay excepción ni nada, simplemente no se eliminan.

Según entiendo, esto se debe a que la magia que nHibernate realiza para crear una lista de elementos secundarios que requieren eliminación no funciona con instancias separadas.

No he podido encontrar un ejemplo simple de cómo debe ser este tipo de método de acción con nHibernate (es decir, utilizando un objeto de modelo-enlazador como instancia de nHibernate desconectada) - ejemplos basados ​​en MS EF (por ejemplo, http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx) parece utilizar un método 'ApplyPropertyChanges' para copiar las propiedades modificadas del objeto vinculado al modelo a una instancia de entidad recargadura.

Entonces, después de todo eso, la pregunta es bastante simple: si tengo la carpeta modelo, dame un objeto nuevo que contenga colecciones de objetos secundarios, ¿cómo debo actualizar eso a través de nHibernate, (donde 'actualización' incluye posiblemente eliminación de niños)?

Respuesta

4

Aquí hay un ejemplo que hace lo que creo que estás tratando de hacer. Avísame si he entendido mal lo que intentas hacer.

Teniendo en cuenta los siguientes "dominio" clases:

public class Person 
{ 
    private IList<Pet> pets; 

    protected Person() 
    { } 

    public Person(string name) 
    { 
     Name = name; 
     pets = new List<Pet>(); 
    } 

    public virtual Guid Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual IEnumerable<Pet> Pets 
    { 
     get { return pets; } 
    } 

    public virtual void AddPet(Pet pet) 
    { 
     pets.Add(pet); 
    } 

    public virtual void RemovePet(Pet pet) 
    { 
     pets.Remove(pet); 
    } 
} 

public class Pet 
{ 
    protected Pet() 
    { } 

    public Pet(string name) 
    { 
     Name = name; 
    } 

    public virtual Guid Id { get; set; } 
    public virtual string Name { get; set; } 
} 

Con la siguiente asignación:

public class PersonMap : ClassMap<Person> 
    { 
     public PersonMap() 
     { 
      LazyLoad(); 
      Id(x => x.Id).GeneratedBy.GuidComb(); 
      Map(x => x.Name); 
      HasMany(x => x.Pets) 
        .Cascade.AllDeleteOrphan() 
        .Access.AsLowerCaseField() 
        .SetAttribute("lazy", "false"); 
     } 
    } 

    public class PetMap : ClassMap<Pet> 
    { 
     public PetMap() 
     { 
      Id(x => x.Id).GeneratedBy.GuidComb(); 
      Map(x => x.Name); 
     } 
    } 

Esta prueba:

[Test] 
    public void CanDeleteChildren() 
    { 
     Person person = new Person("joe"); 

     Pet dog = new Pet("dog"); 
     Pet cat = new Pet("cat"); 

     person.AddPet(dog); 
     person.AddPet(cat); 

     Repository.Save(person); 

     UnitOfWork.Commit(); 

     CreateSession(); 
     UnitOfWork.BeginTransaction(); 

     Person retrievedPerson = Repository.Get<Person>(person.Id); 
     Repository.Evict(retrievedPerson); 

     retrievedPerson.Name = "Evicted"; 

     Assert.AreEqual(2, retrievedPerson.Pets.Count()); 
     retrievedPerson.RemovePet(retrievedPerson.Pets.First()); 

     Assert.AreEqual(1, retrievedPerson.Pets.Count()); 

     Repository.Save(retrievedPerson); 

     UnitOfWork.Commit(); 

     CreateSession(); 
     UnitOfWork.BeginTransaction(); 

     retrievedPerson = Repository.Get<Person>(person.Id); 
     Assert.AreEqual(1, retrievedPerson.Pets.Count()); 
    } 

carreras y genera el siguiente código SQL:

DeletingChildrenOfEvictedObject.CanDeleteChildren: Pasó NHibernate: INSERT INTO [Name] (Name, Id) VALUES (@ p0, @ p1); @ p0 = 'joe', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: INSERT INTO [Nombre de Mascota] (Id, Id) VALORES (@ p0, @ p1); @ p0 = 'perro', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernate: INSERT INTO [Nombre de Mascota] (Id, Id) VALORES (@ p0, @ p1); @ p0 = 'cat', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: UPDATE [Pet] SET Person_id = @ p0 DONDE id = @ p1; @ P0 = 'cd123fc8-6163-42a5-AEEB-9bf801013ab2', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernate: UPDATE [Pet] SET person_id = @ p0 donde id = @ p1; @ P0 = 'cd123fc8-6163-42a5-AEEB-9bf801013ab2', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: SELECT person0_.Id como Id5_0_, person0_.Name como Name5_0_ DE [persona ] person0_ WHERE [email protected]; @ P0 = 'cd123fc8-6163-42a5-AEEB-9bf801013ab2'

NHibernate: SELECT pets0_.Person_id como Person3_1_, pets0_.Id como Id1_, pets0_.Id como Id6_0_, pets0_.Name como Name6_0_ DE [Mascotas] pets0_ DONDE [email protected]; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: ACTUALIZACIÓN [Person] SET Name = @ p0 DONDE id = @ p1; @ p0 = 'Desalojado', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: ACTUALIZACIÓN [Pet] SET Name = @ p0 WHERE id = @ p1; @ p0 = 'perro', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb' NHibernate: UPDATE [Pet] SET Person_id = null WHERE Person_id = @ p0 AND Id = @ p1; @ P0 = 'cd123fc8-6163-42a5-AEEB-9bf801013ab2', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: DELETE FROM [Mascotas] donde id = @ p0; @ P0 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: SELECT person0_.Id como Id5_0_, person0_.Name como Name5_0_ DE [persona] person0_ DONDE [email protected]; @ P0 = 'cd123fc8-6163-42a5-AEEB-9bf801013ab2'

NHibernate: SELECT pets0_.Person_id como Person3_1_, pets0_.Id como Id1_, pets0_.Id como Id6_0_, pets0_.Name como Name6_0_ DE [Mascotas] pets0_ DONDE [email protected]; @ P0 = 'cd123fc8-6163-42a5-AEEB-9bf801013ab2'

Nota Eliminar en la [Mascotas] ...

es así, lo que necesita para ser capaz de hacer es nhibernate mano un objeto Person (en este ejemplo) con las colecciones modificadas y debería ser capaz de determinar qué eliminar. Asegúrese de tener el conjunto de atributos Cascade.AllDeleteOrphan().

+0

Muchas gracias por esto, ¡mucho trabajo allí! Desafortunadamente, el bit con el que estoy luchando es que el objeto 'Persona' que tengo en la segunda sesión/transacción (es decir, mi POST) es un objeto completamente nuevo creado por ModelBinder, en lugar de un objeto recuperado que ha tenido un Se modificaron algunos campos y se realizaron algunas llamadas de 'eliminar secundarios'. Creo que lo que estoy buscando es una forma de tomar ese nuevo objeto y aplicar sus cambios al objeto recuperado, para que nh pueda resolver el SQL requerido. Quizás eso simplemente no existe. –

+1

La manera en que trato de manejar esa situación es tratando el nuevo objeto creado por el archivador de modelo como un objeto del modelo de presentación. Deberá recuperar el objeto (o crear de alguna manera una instancia de la clase "persistente") que desee actualizar y aplicar esas actualizaciones a ese objeto. A continuación, puede guardar ese objeto en NHibernate. ¿Tiene sentido? –

+0

Entonces, 'manualmente' (es decir, propiedad por propiedad, con bucles o lo que sea para las colecciones secundarias), sobrescribe las propiedades del objeto 'recuperado' con las propiedades del objeto 'presentación (es decir, POST)'? ¿Y calcula manualmente las eliminaciones secundarias requeridas? Parece un poco más de trabajo (y mantenimiento) de lo que esperaba. Apenas parece que valga la pena preocuparse por la carpeta de modelos, realmente, como si tuviera que manejar los campos uno a la vez, podría sacarlos directamente de la respuesta del formulario. Gracias por la ayuda, sin embargo. –

1

La respuesta de Rob me convenció para ver más de cerca el enfoque 'cargar el elemento existente en la nueva sesión y luego fusionarlo', y por supuesto ISession.Merge, que parece hacer exactamente lo que yo quería, que es tomar un objeto nuevo y combinarlo con su predecesor que acaba de volver a cargar en la segunda sesión.

Así que creo que la respuesta a la pregunta que traté de hacer es "volver a cargar la entidad existente y luego llamar a 'ISession.Merge' con la nueva entidad".

Cuestiones relacionadas