2008-12-30 13 views
7

Actualmente estoy usando NHibernate como capa de acceso a datos, utilizando Fluent NHibernate para crear los archivos de mapeo para mí. Tengo dos clases, TripItem y TripItemAttributeValue, que tienen una relación de muchos a muchos entre ellos.NHibernate no persiste la relación muchos a varios

El mapeo es el siguiente:

public class TripItemMap : ClassMap<TripItem2> 
{ 
    public TripItemMap() 
    { 
     WithTable("TripItemsInt"); 
     NotLazyLoaded(); 

     Id(x => x.ID).GeneratedBy.Identity().WithUnsavedValue(0); 
     Map(x => x.CreateDate, "CreatedOn").CanNotBeNull(); 
     Map(x => x.ModifyDate, "LastModified").CanNotBeNull(); 

     /* snip */ 

     HasManyToMany<TripItemAttributeValue>(x => x.Attributes).AsBag() 
      .WithTableName("TripItems_TripItemAttributeValues_Link") 
      .WithParentKeyColumn("TripItemId") 
      .WithChildKeyColumn("TripItemAttributeValueId") 
      .LazyLoad(); 
    } 
} 

public class TripItemAttributeValueMap : ClassMap<TripItemAttributeValue> 
{ 
    public TripItemAttributeValueMap() 
    { 
     WithTable("TripItemAttributeValues"); 

     Id(x => x.Id).GeneratedBy.Identity(); 
     Map(x => x.Name).CanNotBeNull(); 

     HasManyToMany<TripItem2>(x => x.TripItems).AsBag() 
      .WithTableName("TripItems_TripItemAttributeValues_Link") 
      .WithParentKeyColumn("TripItemAttributeValueId") 
      .WithChildKeyColumn("TripItemId") 
      .LazyLoad(); 
    } 
} 

En algún momento de mi solicitud voy a buscar atributos existentes de la base de datos, agregarlos a tripItem.Attributes, a continuación, guardar el objeto tripItem. Al final, TripItems_TripItemAttributeValues_Link nunca obtiene ningún registro nuevo, lo que provoca que las relaciones no se mantengan.

Si ayuda, estos son los archivos de asignación generados por Fluido NHibernate para estas clases:

<?xml version="1.0" encoding="utf-8"?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="ETP.Core" namespace="ETP.Core.Domain"> 
    <class name="TripItem2" table="TripItemsInt" xmlns="urn:nhibernate-mapping-2.2" lazy="false"> 
    <id name="ID" column="ID" type="Int32" unsaved-value="0"> 
     <generator class="identity" /> 
    </id> 
    <property name="CreateDate" column="CreatedOn" type="DateTime" not-null="true"> 
     <column name="CreatedOn" /> 
    </property> 
    <property name="ModifyDate" column="LastModified" type="DateTime" not-null="true"> 
     <column name="LastModified" /> 
    </property> 
    <bag name="Attributes" lazy="true" table="TripItems_TripItemAttributeValues_Link"> 
     <key column="TripItemId" /> 
     <many-to-many column="TripItemAttributeValueId" class="ETP.Core.Domain.TripItemAttributeValue, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 
    </bag> 
    </class> 
</hibernate-mapping> 

y

<?xml version="1.0" encoding="utf-8"?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="ETP.Core" namespace="ETP.Core.Domain"> 
    <class name="TripItemAttributeValue" table="TripItemAttributeValues" xmlns="urn:nhibernate-mapping-2.2"> 
    <id name="Id" column="Id" type="Int32"> 
     <generator class="identity" /> 
    </id> 
    <property name="Name" column="Name" length="100" type="String" not-null="true"> 
     <column name="Name" /> 
    </property> 
    <bag name="TripItems" lazy="true" table="TripItems_TripItemAttributeValues_Link"> 
     <key column="TripItemAttributeValueId" /> 
     <many-to-many column="TripItemId" class="ETP.Core.Domain.TripItem2, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 
    </bag> 
    </class> 
</hibernate-mapping> 

¿qué estoy haciendo mal aquí?

Respuesta

8

@efdee

que estaba teniendo el mismo problema y pasó casi dos días en esto. Tenía una relación de muchos a muchos y tampoco se actualizaba la tabla de enlaces. Soy nuevo en NHibernate, solo trato de aprenderlo así que tome todo lo que digo con un grano de sal.

Bueno, resultó que no es Fluent NHibernate, ni el mapeo, pero no entiendo cómo funciona NHibernate con muchos a muchos. En una relación muchos a muchos si las colecciones en ambas entidades no están pobladas, NHibernate no conserva los datos en la tabla de enlaces.

Digamos que tengo estas entidades en una relación de muchos a muchos:


partial class Contact 
{ 
    public string ContactName {get; set;} 
    public IList Locations {get; set;} 

} 

partial class Location 
{ 
    public string LocationName {get; set;} 
    public string LocationAddress {get;set;} 
    public IList Contacts {get;set;} 
} 

cuando agrego a una ubicación para Contact.Locations, tengo que asegurarse de que el contacto también está presente dentro ubicación .Contactos.

así que para agregar una ubicación tengo este método dentro de mi clase de contacto.


public void AddLocation(Location location) 
     { 
      if (!location.Contacts.Contains(this)) 
      { 
       location.Contacts.Add(this); 
      } 
      Locations.Add(location); 
     } 

Esto parece haber resuelto mi problema, pero como he dicho sólo estoy recogiendo NHibernate y aprenderlo, puede ser que hay una manera mejor. Si alguien tiene una mejor solución, por favor publíquela.

Este es el post que me señaló a marcar ambas colecciones: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

2

No estoy seguro de cómo lo hace con Fluent NHibernate pero debe establecer la opción Cascade en la bolsa (TripItems). Como de costumbre, Ayende's got a useful post about cascade options.

Desde una búsqueda rápida en Google, te recomiendo que pruebes:

HasManyToMany<TripItem2>(x => x.TripItems).AsBag() 
     .WithTableName("TripItems_TripItemAttributeValues_Link") 
     .WithParentKeyColumn("TripItemAttributeValueId") 
     .WithChildKeyColumn("TripItemId") 
     .LazyLoad() 
/*-->*/ .Cascade.All(); /*<-- this is the bit that should make it work */ 
1

David Kemp tiene razón: desea agregar una cascada a su bolso.

Siempre he editado a mano (y creado a mano) los archivos de mapeo, por lo que mi inclinación natural es ponerlo allí. Puede hacerlo de la siguiente manera:

<bag name="TripItems" lazy="true" table="TripItems_TripItemAttributeValues_Link" cascade="all"> 
    <key column="TripItemAttributeValueId" /> 
    <many-to-many column="TripItemId" class="ETP.Core.Domain.TripItem2, ETP.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 
</bag> 

He descubierto que mantener 'pura' y mantener todo lo que mis clases que ver con NHibernate en el archivo .hbm.xml mantiene mi limpiador de solución. De esa forma, si hay un nuevo software ORM que quiero usar, simplemente reemplazo los archivos de mapeo y no reescribo las clases. Usamos nuestras pruebas unitarias para probar las clases y otorgar testeibilidad al xml, aunque me gustan los métodos de Fluent NHibernate.

0

Me temo que Cascade.All() no es realmente la solución a mi problema, fue una de las cosas que probé. El problema no es que los elementos agregados a la colección no se guarden; ya están en la base de datos en el momento en que se agregan a la colección. Es solo que las entradas en la tabla de enlaces no se están creando. Además, creo que Cascade.All() también provocaría que se eliminen elementos secundarios, lo que no es un comportamiento deseable en mi escenario. He intentado usar Cascade.SaveUpdate(), pero como he señalado, esto resuelve algo que no es realmente mi problema :-)

Sin embargo, para asegurarse, voy a volver a intentar esta solución y hacerle saber el resultado.

En cuanto a mantener las clases puras, esto es 100% el caso con Fluiber NHibernate. Las asignaciones de clase que crea son archivos de código C# que van junto con las clases de entidad, más o menos como los archivos .hbm.xml.

+0

Lo mismo para mí: la cascada no es lo que quería, y no ayudó. ¡Flushing fue la respuesta! –

1

que estoy teniendo exactamente el mismo problema, pero he estado utilizando el NHibernate.JetDriver. Traté de usar la respuesta recomendada sin ningún éxito. ¿Alguien sabe si el NHibernate.JetDriver tiene una limitación con respecto a muchos a muchos?

Aquí están mis archivos de HBM en caso de que alguien pasa a estar interesado en la revisión de ellos por un momento:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Ace.Docs.Core.Domain" assembly="Ace.Docs.Core"> 
<class name="Ace.Docs.Core.Domain.Address, Ace.Docs.Core" table="Addresses" lazy="true"> 
    <id name="Id" column="ID"> 
     <generator class="identity" /> 
    </id> 
    <property name="Address1" column="Address1" /> 
    <property name="Address2" column="Address2" /> 
    <property name="City" column="City" /> 
    <property name="EmailAddress" column="EmailAddress" /> 
    <property name="Phone1" column="Phone1" /> 
    <property name="Phone2" column="Phone2" /> 
    <property name="PostalCode" column="PostalCode" /> 
    <property name="StateOrProvince" column="StateOrProvince" /> 
    <many-to-one name="AddressTypeMember" column="AddressTypeID" class="AddressType" /> 
    <bag name="HasPersonalInfo" table="Link_PersonalInfo_Addresses" lazy="true" cascade="save-update" inverse="true" > 
     <key column="AddressID"></key> 
     <many-to-many column="PersonalInfoID" class="PersonalInfo" /> 
    </bag> 
</class> 

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Ace.Docs.Core.Domain" assembly="Ace.Docs.Core"> 
<class name="Ace.Docs.Core.Domain.PersonalInfo, Ace.Docs.Core" table="PersonalInfo" lazy="true"> 
    <id name="Id" column="ID"> 
     <generator class="identity" /> 
    </id> 
    <property name="Prefix" column="Prefix" /> 
    <property name="FirstName" column="FirstName" /> 
    <property name="MiddleName" column="MiddleName" /> 
    <property name="LastName" column="LastName" /> 
    <property name="SIN" column="SIN" /> 
    <property name="Birthdate" column="Birthdate" /> 
    <property name="Note" column="Notes" /> 
    <bag name="HasAddress" table="Link_PersonalInfo_Addresses" lazy="true" cascade="save-update" inverse="true" > 
     <key column="PersonalInfoID"></key> 
     <many-to-many column="AddressID" class="Address" /> 
    </bag> 
</class> 

1

lo tengo y espero que esto ayuda a alguien más allá. El problema es que tenía inversa = 'verdadero' en ambas bolsas. Si lees el extracto a continuación, notarás que es necesario que el conjunto inverso sea verdadero en solo una de las bolsas:

Observa el uso de inverso = "verdadero". Una vez más, esta configuración le dice a NHibernate que ignore los cambios realizados en la colección de categorías y utilice el otro extremo de la asociación, la colección de elementos, como la representación que debe sincronizarse con la base de datos.

0

Tuve problemas con esto también, y tuve un motivo completamente diferente para mis problemas. En mi ejemplo, si tuviera un objeto sin relaciones entre muchos, podría simplemente llamar a saveOrUpdate, y todo sería bueno. Pero si tuviera relaciones de muchos a muchos, tenía que asegurarme de que mi llamada a saveOrUpdate estaba dentro de BeginTransaction y CommitTransaction. Soy muy nuevo en Nhibernate con fluidez, así que discúlpate si esto es obvio. Pero no fue para mí.

7

Llame a Session.Flush() o use la transacción.

+0

¡Gracias! Tuve el mismo problema. Estaba llamando a Session.SaveOrUpdate (entidad) para un lado de la relación. Pero esto no creaba la tabla de enlaces. Llamar a Session.Flush() resolvió esto. –

+0

Esto funcionó para mí también ... ¡qué molesto! – Paul

+3

¿Alguien puede explicar por qué tenemos que hacer esto en este escenario? – Oliver

2

También tuve el mismo problema: no se conservaron los datos de unión para muchos a muchos. Copié el mapeo de otra relación de muchos a uno (lo modifiqué para un rel de muchos a muchos) pero conservé un atributo inverso = "verdadero". Cuando eliminé este atributo, el problema se resolvió.

Cuestiones relacionadas