2008-12-30 15 views
7

Obecnie używam NHibernate jako mojej warstwy dostępu do danych, używając Fluent NHibernate do tworzenia plików mapowania dla mnie. Mam dwie klasy, TripItem i TripItemAttributeValue, które mają między nimi relację wiele do wielu.NHibernate nie utrzymuje związku wiele-do-wielu

Odwzorowanie jest następujące:

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

W pewnym momencie w mojej aplikacji pobrać istniejących atrybutów z bazy danych, dodaj je do tripItem.Attributes, a następnie zapisać obiekt tripItem. Ostatecznie TripItems_TripItemAttributeValues_Link nigdy nie otrzymuje żadnych nowych rekordów, co powoduje, że relacje nie są utrzymywane.

Jeśli to pomoże, to są pliki mapowania generowane przez Fluent NHibernate do tych klas:

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

i

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

Co robię źle tutaj?

Odpowiedz

8

@efdee

miałem ten sam problem i spędził prawie dwa dni, w tym. Miałem wiele do wielu relacji i tabela linków też nie była aktualizowana. Jestem nowy w NHibernate, tylko próbuję się tego nauczyć, więc weź wszystko, co mówię, z przymrużeniem oka.

Cóż, okazało się, że to nie jest płynna NHibernate, ani mapowanie, ale ja nie rozumiem, jak NHibernate działa z wieloma do wielu. W relacjach wiele do wielu, jeśli kolekcje w obu jednostkach nie są wypełnione, NHibernate nie utrzymuje danych w tabeli łączy.

Powiedzmy mam to podmioty wchodzące w skład wielu do wielu relacji:


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

kiedy dodać do lokalizacji do Contact.Locations, muszę się upewnić, że kontakt jest również obecny wewnątrz lokalizacji .Łączność.

tak, aby dodać lokalizację Mam tę metodę wewnątrz mojej klasy Kontakt.


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

To wydaje się rozwiązać mój problem, ale jak powiedziałem, że jestem po prostu podnosząc NHibernate i uczenia się, może istnieje lepszy sposób. Jeśli ktokolwiek ma lepsze rozwiązanie, opublikuj.

To jest post, który wskazał mi, by sprawdzić zarówno kolekcje: http://www.coderanch.com/t/217138/Object-Relational-Mapping/link-table-of-ManyToMany-annotation

2

Nie jestem pewien, jak to zrobić z Fluent NHibernate, ale musisz ustawić opcję Cascade na torbie (TripItems). Jak zwykle, Ayende's got a useful post about cascade options.

Z szybkiego Google, polecam spróbować:

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 ma rację: chcesz dodać kaskadę do swojej torby.

Zawsze ręcznie edytowałem (i ręcznie tworzyłem) pliki mapowania, więc moim naturalnym nastawieniem jest umieszczenie go w tym miejscu. Można to zrobić w następujący sposób:

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

Znalazłem, że utrzymanie „czysty” i utrzymując wszystko moich klas zrobić z NHibernate w pliku .hbm.xml zachowa moją czystsze rozwiązanie. W ten sposób, jeśli istnieje nowe oprogramowanie ORM, którego chcę użyć, po prostu zastępuję pliki mapowania i nie przepisuję klas. Używamy naszych testów jednostkowych, aby przetestować klasy i dać testowalność dla xml, chociaż w pewnym sensie lubię metody Fluent NHibernate.

0

Obawiam się, że Cascade.All() nie jest tak naprawdę rozwiązaniem mojego problemu - to była jedna z rzeczy, których próbowałem. Problem nie polega na tym, że elementy dodane do kolekcji nie są zapisywane - są już w bazie danych w momencie, gdy są dodawane do kolekcji. Chodzi tylko o to, że wpisy w tabeli linków nie są tworzone. Ponadto myślę, że Cascade.All() również spowoduje usunięcie elementów potomnych, co nie jest pożądanym zachowaniem w moim scenariuszu. Próbowałem już używać Cascade.SaveUpdate(), ale jak już wskazałem, rozwiązuje to problem, który nie jest moim problemem :-)

Jednak, aby się upewnić, spróbuję ponownie i poznam to rozwiązanie Wynik.

Jeśli chodzi o utrzymywanie czystości w klasach, jest to w 100% przypadek w przypadku Fluent NHibernate. Utworzone odwzorowania klas są plikami kodu C#, które pasują do twoich klas jednostek, podobnie jak pliki .hbm.xml.

+0

To samo dotyczy mnie - kaskadowanie nie jest tym, czego chciałem, a to nie pomogło. Flushing był odpowiedzią! –

1

Mam dokładnie ten sam problem, ale już przy użyciu NHibernate.JetDriver. Próbowałem użyć zalecanej odpowiedzi bez żadnego sukcesu. Czy ktoś wie, czy NHibernate.JetDriver ma ograniczenia w stosunku do wielu do wielu?

Oto moje pliki HBM w przypadku gdy ktoś okazuje się być zainteresowany przeglądając je na chwilę:

<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

Mam go i mam nadzieję, że ten pomaga komuś innemu. Problem polega na tym, że w obu torbach miałem odwrotność = "prawda". Jeśli przeczytasz poniższy fragment, zauważysz, że musi być odwrotnie ustawiony na true tylko w jednej z toreb:

Zwróć uwagę na użycie inverse = "true". Po raz kolejny to ustawienie informuje NHibernate, aby ignorował zmiany wprowadzone do kolekcji kategorii i używał drugiego końca powiązania - kolekcji elementów - jako reprezentacji, która powinna być zsynchronizowana z bazą danych.

0

Walczyłem z tym również, i przyszedł z zupełnie innego powodu moich kłopotów. W moim przykładzie, gdybym miał obiekt bez relacji wiele do wielu, mógłbym tylko wywołać saveOrUpdate, a wszystko byłoby dobrze. Ale gdybym miał jakikolwiek związek wiele do wielu, musiałbym upewnić się, że moje zaproszenie saveOrUpdate było w ramach BeginTransaction i CommitTransaction. Jestem nowicjuszem w płynnym Nhibernate, więc przepraszam, jeśli to oczywiste. Ale nie dla mnie.

7

Zadzwoń do Session.Flush() lub użyj transakcji.

+0

Dzięki! Miałem ten sam problem. Dzwoniłem Session.SaveOrUpdate (entity) po jednej stronie relacji. Ale to nie tworzyło tabeli linków. Wywołanie Session.Flush() rozwiązało to. –

+0

To też działa dla mnie ... jak denerwujące! – Paul

+3

Czy ktoś może wyjaśnić, DLACZEGO musimy to zrobić w tym scenariuszu? – Oliver

2

Miałem również ten sam problem - dane dotyczące łączenia się wielu osób z wieloma osobami nie występowały. Skopiowałem mapowanie z innej relacji "wiele do jednego" (zmodyfikowałem ją dla relacji wiele do wielu), ale zachowałem atrybut odwrotny = "prawdziwy". Po usunięciu tego atrybutu problem został rozwiązany.

Powiązane problemy