2008-12-30 9 views
7

J'utilise actuellement NHibernate comme ma 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 many-to-many entre eux.NHibernate ne persiste pas relation plusieurs-à-plusieurs

La cartographie est la suivante:

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 certain moment dans ma demande, je vais chercher les attributs existants de la base de données, les ajouter à tripItem.Attributes, puis enregistrez l'objet tripItem. À la fin, le TripItems_TripItemAttributeValues_Link ne reçoit jamais de nouveaux enregistrements, ce qui entraîne la non-persistance des relations.

Si elle aide, ce sont 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?

Répondre

8

@efdee

je le même problème et a passé presque deux jours à ce sujet. J'avais une relation de plusieurs à plusieurs et la table de liens n'était pas mise à jour non plus. Je suis nouveau à NHibernate, essayant 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'est pas Fluent NHibernate, ni la cartographie, mais je ne comprends pas comment NHibernate fonctionne avec plusieurs-à-plusieurs. Dans une relation plusieurs-à-plusieurs, si les collections des deux entités ne sont pas remplies, NHibernate ne conserve pas les données dans la table de liens.

Disons que j'ai cette entités dans 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;} 
} 

quand j'ajouter à un endroit à Contact.Locations, je dois vous assurer que le contact est également présent à l'intérieur emplacement .Contacts.

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 suis juste ramassais NHibernate et apprendre, peut-être il y a une meilleure façon. Si quelqu'un a une meilleure solution, s'il vous plaît poster.

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

2

Je ne suis pas sûr comment vous le faites avec Fluent NHibernate, mais vous devez définir l'option Cascade sur le sac (TripItems). Comme d'habitude, Ayende's got a useful post about cascade options.

D'un rapide sur Google, je vous suggère 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 */ 
1

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

J'ai toujours édité à la main (et créé manuellement) les fichiers de mapping, donc mon penchant naturel est de le mettre là. 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 trouvé que le maintien de pur »et tout en gardant mes cours à faire avec NHibernate dans le fichier .hbm.xml conserve mon propre solution. De cette façon, s'il y a un nouveau logiciel ORM que je veux utiliser, je remplace simplement les fichiers de mapping, et je ne réécris pas les classes. Nous utilisons nos tests unitaires pour tester les classes et donner de la testabilité au xml, bien que j'utilise un peu les méthodes de Fluent NHibernate.

0

Je crains Cascade.All() n'est pas vraiment la solution à mon problème - c'était l'une des choses que j'ai essayé. Le problème n'est pas que les éléments ajoutés à la collection ne sont pas sauvegardés - ils sont déjà dans la base de données au moment où ils sont ajoutés à la collection. C'est juste que les entrées dans la table de liens ne sont pas créées. En outre, je pense que Cascade.All() entraînerait également la suppression d'éléments enfants, ce qui n'est pas un comportement souhaitable dans mon scénario. J'ai essayé d'utiliser Cascade.SaveUpdate(), mais comme je l'ai souligné, 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 faire savoir le résultat. En ce qui concerne le maintien de la pureté des classes, ceci est 100% le cas avec NHibernate Fluent. Les mappages de classe que vous créez sont des fichiers de code C# qui accompagnent vos classes d'entités, à la manière des fichiers .hbm.xml.

+0

Même affaire pour moi - cascade n'est pas ce que je voulais, et cela n'a pas aidé. Flushing était la réponse! –

1

Je vais avoir exactement le même problème, mais je me sers de la NHibernate.JetDriver. J'ai essayé d'utiliser la réponse recommandée sans 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 se trouve être intéressé à les passer en revue pour un moment:

<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

Je l'ai et j'espère que aide quelqu'un d'autre là-bas. Le problème est que j'avais inversé = "vrai" sur les deux sacs. Si vous lisez l'extrait ci-dessous, vous remarquerez qu'il doit y avoir une valeur inverse sur true pour un seul des sacs:

Notez l'utilisation de inverse = "true". Une fois de plus, 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 devant être synchronisée avec la base de données.

0

Je luttais aussi avec cela, et je venais par une raison complètement différente de mes ennuis. Dans mon exemple, si j'avais un objet sans relations many-to-many, je pourrais juste appeler saveOrUpdate, et tout serait bien. Mais si j'avais des relations plusieurs-à-plusieurs, je devais m'assurer que mon appel saveOrUpdate était dans BeginTransaction et CommitTransaction. Je suis très nouveau pour parler couramment Nhibernate, alors excusez si c'est évident. Mais ce n'était pas à moi.

7

Appelez Session.Flush() ou utilisez transaction.

+0

Merci! J'ai eu le même problème. J'appelais Session.SaveOrUpdate (entity) pour un côté de la relation. Mais cela ne créait pas la table de liens. L'appel de Session.Flush() a résolu ceci. –

+0

Cela a fonctionné pour moi aussi ... quel ennui! – Paul

+3

Quelqu'un peut-il expliquer pourquoi nous devons faire cela dans ce scénario? – Oliver

2

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

Questions connexes