Question

J'utilise actuellement NHibernate comme couche d'accès aux données, en utilisant Fluent NHibernate pour créer les fichiers de mappage pour moi. J'ai deux classes, TripItem et TripItemAttributeValue, qui ont une relation plusieurs à plusieurs.

Le mappage est le suivant:

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

À un moment donné dans mon application, je récupère les attributs existants dans la base de données, les ajoute à tripItem.Attributes, puis enregistre l'objet tripItem. En fin de compte, TripItems_TripItemAttributeValues_Link n'obtient jamais de nouveaux enregistrements, ce qui empêche la persistance des relations.

Si cela peut vous aider, voici les fichiers de mappage générés par Fluent NHibernate pour ces classes:

<?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>

et

<?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'est-ce que je fais mal ici?

Était-ce utile?

La solution

@efdee

J'avais le même problème et y ai passé près de deux jours. J'avais plusieurs relations et la table de liens n'était pas mise à jour non plus. Je suis nouveau sur NHibernate, j'essaie juste de l'apprendre, alors prenez tout ce que je dis avec un grain de sel.

Eh bien, il s’est avéré que ce n’était pas NHibernate Fluent, ni la cartographie, mais je ne comprenais pas comment NHibernate fonctionne avec plusieurs-plusieurs. Dans une relation plusieurs à plusieurs si les collections des deux entités ne sont pas renseignées, NHibernate ne conserve pas les données dans la table des liens.

Disons que j'ai ces entités dans une relation plusieurs à plusieurs:


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

lorsque j'ajoute un emplacement à Contact.Locations, je dois m'assurer que le contact est également présent à l'intérieur de l'emplacement.Contacts.

donc pour ajouter un emplacement, j'ai cette méthode dans ma classe Contact.


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

Cela semble avoir résolu mon problème, mais comme je l'ai dit, je ne fais qu'acquérir NHibernate et l'apprendre, il existe peut-être un meilleur moyen. Si quelqu'un a une meilleure solution, merci de poster.

C’est le billet qui m’a demandé de vérifier les deux collections: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

Autres conseils

Appelez Session.Flush () ou utilisez une transaction.

Je ne suis pas sûr de savoir comment procéder avec Fluent NHibernate mais vous devez définir l'option Cascade sur le sac ( TripItems ). Comme d'habitude, Ayende a publié un article utile sur les options de cascade .

D'après un rapide Google, je vous conseillerais d'essayer:

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

J'ai également eu le même problème: les données de jointure pour plusieurs-plusieurs n'étaient pas persistantes. J'avais copié le mappage d'une autre relation plusieurs-à-un (modifié pour une relation plusieurs-à-plusieurs), mais j'avais conservé l'inverse = "vrai" attribut. Lorsque j'ai supprimé cet attribut, le problème a été résolu.

David Kemp a raison: vous souhaitez ajouter une cascade à votre sac.

J'ai toujours édité (et créé à la main) les fichiers de mappage, mon envie naturelle est donc de les y placer. Vous pouvez le faire comme suit:

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

J'ai constaté que le fait de garder mes classes «pures» et de tout faire avec nHibernate dans le fichier .hbm.xml garde ma solution plus propre. Ainsi, si vous souhaitez utiliser un nouveau logiciel ORM, je ne fais que remplacer les fichiers de mappage et ne pas réécrire les classes. Nous utilisons nos tests unitaires pour tester les classes et donner la possibilité de tester le xml, bien que j'aime un peu les méthodes de Fluent NHibernate.

J'ai exactement le même problème mais j'utilise le NHibernate.JetDriver. J'ai essayé d'utiliser la réponse recommandée sans aucun succès. Est-ce que quelqu'un sait si le NHibernate.JetDriver a une limitation en ce qui concerne plusieurs à plusieurs?

Voici mes fichiers hbm au cas où quelqu'un souhaiterait les consulter un instant:

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

Je l'ai et j'espère que cela aidera quelqu'un d'autre. Le problème est que j'avais inverse = 'true' sur les deux sacs. Si vous lisez l'extrait ci-dessous, vous remarquerez qu'il est nécessaire de définir l'inverse sur true sur un seul des sacs:

Notez l'utilisation de inverse = "true". Une fois encore, ce paramètre indique à NHibernate d’ignorer les modifications apportées à la collection de catégories et d’utiliser l’autre extrémité de l’association - la collection d’éléments - comme représentation à synchroniser avec la base de données.

J'ai bien peur que Cascade.All () ne soit pas vraiment la solution à mon problème - c'est l'une des choses que j'ai essayée. Le problème n'est pas que les éléments ajoutés à la collection ne sont pas enregistrés - ils sont déjà dans la base de données au moment où ils sont ajoutés à la collection. C'est simplement que les entrées dans la table de liens ne sont pas en cours de création. De plus, je pense que Cascade.All () provoquerait également la suppression d'éléments enfants, ce qui n'est pas souhaitable dans mon scénario. J'ai essayé d'utiliser Cascade.SaveUpdate (), mais comme je l'ai indiqué, cela résout quelque chose qui n'est pas vraiment mon problème: -)

Cependant, pour être sûr, je vais réessayer cette solution et vous informer du résultat.

Pour ce qui est de garder les classes pures, c’est le cas à 100% avec Fluent NHibernate. Les mappages de classes que vous créez sont des fichiers de code C # qui accompagnent vos classes d'entité, un peu comme les fichiers .hbm.xml.

Je me débattais aussi avec cela, et je suis venu pour une raison totalement différente de mes problèmes. Dans mon exemple, si j'avais un objet sans relation plusieurs-à-plusieurs, je pourrais simplement appeler saveOrUpdate, et tout irait bien. Mais si j'avais des relations plusieurs à plusieurs, je devais m'assurer que mon appel saveOrUpdate se trouvait dans une transaction BeginTransaction et CommitTransaction. Je suis très nouveau pour parler couramment Nhibernate, alors excusez-moi si cela est évident. Mais ce n’était pas pour moi.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top