Pregunta

Actualmente estoy usando NHibernate como mi capa de acceso a datos, usando 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.

La asignación es la 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 punto de mi aplicación, obtengo los atributos existentes de la base de datos, los agrego a tripItem.Attributes y luego guardo el objeto tripItem. Al final, TripItems_TripItemAttributeValues_Link nunca obtiene ningún registro nuevo, lo que hace que las relaciones no se mantengan.

Si ayuda, estos son los archivos de mapeo generados por Fluent 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í?

¿Fue útil?

Solución

@efdee

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

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

Supongamos 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 a Contactos.Localizaciones, debo asegurarme de que el contacto también esté presente dentro de la ubicación.Contactos

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 dije, solo estoy recogiendo NHibernate y aprendiendo, quizás haya una mejor manera. Si alguien tiene una solución mejor, por favor publique.

Esta es la publicación que me indicó que revisara ambas colecciones: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

Otros consejos

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

No estoy seguro de cómo lo hace con Fluent NHibernate, pero necesita configurar la opción de cascada en la bolsa ( TripItems ). Como de costumbre, Ayende tiene una publicación útil sobre opciones de cascada .

Desde un rápido Google, te sugiero que lo intentes:

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 */

También tuve el mismo problema: los datos de unión de muchos a muchos no se mantuvieron. Copié la asignación de otra relación muchos a uno (la modifiqué para una relación de muchos a muchos) pero conservé una inversa = "verdadero" atributo. Cuando quité este atributo el problema se resolvió.

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

Siempre he editado a mano (y creado a mano) los archivos de mapeo, por lo que mi inclinación natural es ponerlos allí. Puedes 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>

Descubrí que mantener mis clases "puras" y mantener todo lo relacionado con nHibernate en el archivo .hbm.xml mantiene mi solución más limpia. De esa manera, si hay un nuevo software ORM que quiero usar, simplemente reemplazo los archivos de mapeo y no reescribo las clases. Usamos nuestras pruebas de unidad para probar las clases y dar prueba al xml, aunque me gustan los métodos de Fluent NHibernate.

Tengo exactamente el mismo problema, pero he estado usando NHibernate.JetDriver. Intenté 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 hbm en caso de que alguien esté interesado en revisarlos 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>

Lo tengo y espero que esto ayude a alguien más allá afuera. El problema es que tuve inverso = 'verdadero' en ambas bolsas. Si lee el extracto a continuación, notará que debe haber un conjunto inverso a verdadero en solo una de las bolsas:

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

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 que se encuentran 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 haría que los elementos secundarios se eliminen, 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 asegurarme, volveré a intentar esta solución y le haré saber el resultado.

En cuanto a mantener las clases puras, este es el 100% del caso con Fluent NHibernate. Las asignaciones de clase que creas son archivos de código C # que van junto con las clases de tu entidad, casi como lo harían los archivos .hbm.xml.

Yo también estaba luchando con esto, y tuve una razón completamente diferente para mis problemas. En mi ejemplo, si tuviera un objeto sin ninguna relación de muchos a muchos, podría simplemente llamar saveOrUpdate, y todo estaría bien. Pero si tenía relaciones de muchos a muchos, tenía que asegurarme de que mi llamada saveOrUpdate estaba dentro de BeginTransaction y CommitTransaction. Soy muy nuevo en Nhibernate con fluidez, así que discúlpeme si esto es obvio. Pero no fue para mí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top