2008-12-30 16 views
7

Ich verwende derzeit NHibernate als meine Datenzugriffsebene, Fluent NHibernate verwenden, um die Zuordnungsdateien für mich zu erstellen. Ich habe zwei Klassen, TripItem und TripItemAttributeValue, die eine Viele-zu-Viele-Beziehung zwischen ihnen haben.NHibernate nicht persistent viele-zu-viele-Beziehung

Die Abbildung ist wie folgt:

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

An einem gewissen Punkt in meiner Anwendung, die ich holen vorhandene Attribute aus der Datenbank, fügen Sie sie tripItem.Attributes, dann das tripItem Objekt speichern. Am Ende erhält TripItems_TripItemAttributeValues_Link niemals neue Datensätze, was dazu führt, dass die Beziehungen nicht beibehalten werden.

Wenn es hilft, das sind die Mapping-Dateien, die durch Fluent NHibernate für diese Klassen:

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

und

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

Was mache ich falsch hier?

Antwort

8

@efdee

ich das gleiche Problem hatte und verbrachte fast zwei Tage auf diesem. Ich hatte eine Beziehung von vielen zu vielen und die Linktabelle wurde auch nicht aktualisiert. Ich bin neu in NHibernate, versuche es einfach zu lernen, also nimm alles, was ich sage, mit einem Körnchen Salz.

Nun, es stellte sich heraus, dass es nicht Fluent NHibernate ist, noch die Zuordnung, aber ich verstehe nicht, wie NHibernate mit vielen zu vielen arbeitet. In einer Viele-zu-viele-Beziehung, wenn Sammlungen in beiden Entitäten nicht gefüllt sind, speichert NHibernate keine Daten in der Verknüpfungstabelle.

Lassen Sie uns sagen, ich habe diese Entitäten in einem many-to-many-Beziehung:


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

, wenn ich an einen Ort zu Contact.Locations hinzufügen, ich habe, um sicherzustellen, dass der Kontakt innerhalb Lage ist auch vorhanden .Kontakte.

so, um einen Speicherort hinzuzufügen, habe ich diese Methode in meiner Contact-Klasse.


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

Das scheint mein Problem gelöst zu haben, aber wie ich schon sagte, ich bin nur NHibernate aufzunehmen und es zu lernen, kann ein besserer Weg gibt. Wenn jemand eine bessere Lösung hat, bitte posten.

Dies ist der Beitrag, den ich zeigte beiden Sammlungen zu überprüfen: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

2

Ich bin nicht sicher, wie Sie es mit Fluent NHibernate tun, aber Sie müssen die Cascade-Option auf der Tasche (TripItems) festlegen. Wie üblich, Ayende's got a useful post about cascade options.

Von einer schnellen Google, ich würde vorschlagen, dass Sie versuchen:

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 hat es richtig: Sie wollen eine Kaskade zu Ihrer Tasche hinzuzufügen.

Ich habe immer die Mapping-Dateien von Hand bearbeitet (und von Hand erstellt), also ist es meine natürliche Neigung, es dort zu platzieren. Sie können das tun, wie folgt:

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

ich gefunden habe, dass meine Klassen ‚rein‘ zu halten und halten alles mit nHibernate in der .hbm.xml Datei Reiniger meine Lösung zu tun, hält. Wenn es eine neue ORM-Software gibt, die ich verwenden möchte, ersetze ich einfach die Mapping-Dateien und schreibe die Klassen nicht neu. Wir verwenden unsere Komponententests, um die Klassen zu testen und der xml Testbarkeit zu geben, obwohl ich die Methoden von Fluent NHibernate mag.

0

Ich fürchte, Cascade.All() ist nicht wirklich die Lösung für mein Problem - es war eines der Dinge, die ich ausprobiert habe. Das Problem besteht nicht darin, dass die der Sammlung hinzugefügten Elemente nicht gespeichert werden. Sie befinden sich bereits zu dem Zeitpunkt, zu dem sie der Sammlung hinzugefügt werden, in der Datenbank. Es ist nur so, dass die Einträge in der Verknüpfungstabelle nicht erstellt werden. Außerdem würde Cascade.All() auch dazu führen, dass untergeordnete Elemente gelöscht werden, was in meinem Szenario nicht wünschenswert ist. Ich habe versucht, mit Cascade.SaveUpdate(), aber wie ich bereits hingewiesen habe, löst dies etwas, das ist nicht wirklich mein Problem :-)

Allerdings, ich werde versuchen, diese Lösung und lassen Sie es wissen das Ergebnis.

Um die Klassen rein zu halten, ist dies bei Fluent NHibernate zu 100% der Fall. Die Klassenzuordnungen, die Sie erstellen, sind C# -Code-Dateien, die neben Ihren Entitätsklassen liegen, ähnlich wie bei .hbm.xml-Dateien.

+0

Gleiches Angebot für mich - Cascading ist nicht das, was ich wollte, und es hat nicht geholfen. Flushing war die Antwort! –

1

ich das gleiche Problem genau mit bin, aber ich habe den NHibernate.JetDriver im Einsatz. Ich habe versucht, die empfohlene Antwort ohne Erfolg zu verwenden. Weiß jemand, ob der NHibernate.JetDriver eine Beschränkung in Bezug auf Viele-zu-Viele hat?

Hier sind meine hbm Dateien, falls jemand bei der Überprüfung der sie für einen Moment zu interessieren passiert:

<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

ich es habe, und ich hoffe, dass diese hilft jemand anderem da draußen. Das Problem ist, dass ich auf beiden Taschen inverse = 'true' hatte. Wenn Sie den folgenden Auszug lesen, werden Sie bemerken, dass nur für eines der Taschen die umgekehrte Einstellung "Wahr" verwendet werden muss:

Beachten Sie die Verwendung von inverse = "true". Diese Einstellung weist NHibernate erneut an, Änderungen zu ignorieren, die an der categories-Auflistung vorgenommen wurden, und das andere Ende der Verknüpfung - die Elementauflistung - als die Darstellung zu verwenden, die mit der Datenbank synchronisiert werden sollte.

0

Ich hatte auch damit zu kämpfen, und kam aus einem ganz anderen Grund für meine Probleme. In meinem Beispiel, wenn ich ein Objekt ohne viele Viele-zu-viele-Beziehungen hätte, könnte ich einfach saveOrUpdate aufrufen, und alles wäre gut. Aber wenn ich irgendwelche Viele-zu-Viele-Beziehungen hatte, musste ich sicherstellen, dass mein saveOrUpdate-Aufruf innerhalb einer BeginTransaction und CommitTransaction war. Ich bin sehr neu in fließenden Nhibernate, also entschuldige mich, wenn das offensichtlich ist. Aber es war nicht für mich.

7

Rufen Sie Session.Flush() auf oder verwenden Sie die Transaktion.

+0

Danke! Ich hatte das gleiche Problem. Ich habe Session.SaveOrUpdate (Entität) für eine Seite der Beziehung aufgerufen. Aber das erstellte die Verbindungstabelle nicht. Das Aufrufen von Session.Flush() löste dies. –

+0

Das hat auch für mich funktioniert ... wie nervig! – Paul

+3

Kann mir jemand erklären, WARUM wir das in diesem Szenario tun müssen? – Oliver

2

Ich hatte auch das gleiche Problem - die Verbindung Daten für die Viele-zu-Viele wurde nicht beibehalten. Ich hatte das Mapping von einer anderen Viele-zu-Eins-Beziehung kopiert (für eine Viele-zu-Viele-Beziehung geändert), aber ein inverse = "True" -Attribut beibehalten. Als ich dieses Attribut entfernte, wurde das Problem gelöst.