2012-03-12 16 views
9

Pracowałem z NHibernate, używając Fluent NHibernate do mapowania. Rozwiązałem wiele problemów i zacząłem myśleć, że jestem doświadczony w nhibernate. Jednak ten błąd jest dość dziwny.NHibernate, inny obiekt o tej samej wartości identyfikatora był już powiązany z sesją

To jest mój model:

public class MessageNew 
    { 
     public virtual int Id { get; set; } 
     public virtual string Content { get; set; } 
     public virtual string Subject { get; set; } 
     public virtual User User { get; set; } 
     public virtual bool IsSent { get; set; } 
     public virtual string AmazonMessageId { get; set; } 
    } 

A mój mapowanie

public class MessageNewMap : ClassMap<MessageNew> 
{ 
    public MessageNewMap() 
    { 
     Id(x => x.Id); 
     Map(x => x.Content).CustomSqlType("text"); 
     Map(x => x.Subject); 
     Map(x => x.AmazonMessageId); 
     Map(x => x.IsSent); 

     References(x => x.User); 
    } 
} 

Tu, gdzie występuje wyjątek:

foreach (var userToSend in usersToSend) 
{ 
    string body = MailHelper.BuildSomeBody() 
    if (userToSend != CurrentUser) 
    { 
     MessageNew message = new MessageNew 
     { 
      User = userToSend, 
      IsSent = false, 
      Content = body, 
      Subject = subject 
     }; 
     session.Save(message); // Exception thrown 
    } 
} 

prosimy o wyjątek:

NHibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: 1779, of entity: Models.MessageNew 
    at NHibernate.Engine.StatefulPersistenceContext.CheckUniqueness(EntityKey key, Object obj) 
    at NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess) 
    at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess) 
    at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) 
    at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) 
    at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) 
    at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event) 
    at NHibernate.Impl.SessionImpl.Save(Object obj) 

Generator identyfikatorów to generator id z automatyczną inkrementacją baz danych. (nie hilo ani żadna inna). Wersja NHibernate to 3.2.0.

Próbowałem przeciążać Equals i GetHashCode, bez powodzenia.

Wzorzec UnitOfWork, którego używam, nie wymaga zatwierdzania transakcji ani sesji przepłukiwania w pętli foreach. NHibernate mówi, że istnieje inny obiekt o tym samym id, ale wszystko, co robię, to wstawianie nowego obiektu, który w ogóle nie ma żadnego identyfikatora.

Używam tej samej struktury w całym moim projekcie i działa dobrze wszędzie, ale to. Podejrzewam, że może to wynikać z właściwości "Treść", która jest tekstem i jest ustawiona na duży ciąg.

Czego tu mi brakuje? Lub NHibernate czegoś brakuje?

+1

W twoim mapowaniu masz 'ClassMap ', ale pokazanie klasy 'Wiadomość' jest literówką? – Rippo

+0

Tak, w rzeczywistości jest to "MessageNew". Dzięki za ostrzeżenie, zredagowałem post. – SadullahCeran

+0

kiedykolwiek to rozgryzłeś? – BueKoW

Odpowiedz

1

messagenew powinny wdrożyć Równa i GetHashCode

public class MessageNew 
{ 
    public virtual int Id { get; set; } 

    public override bool Equals(object obj) 
    { 
     var other = obj as MessageNew; 
     return (other != null) && (IsTransient ? ReferenceEquals(this, other) : Id == other.Id; 
    } 

    private int? _cachedHashcode; // because Hashcode should not change 
    public override int GetHashCode() 
    { 
     if (_cachedHashcode == null) 
      _cachedHashcode = IsTransient ? base.GetHashCode() : Id.GetHashCode(); 

     return _cachedHashcode.Value; 
    } 

    public bool IsTransient { get { return Id == 0; } } 
} 
+1

Dzięki za napiwek. Zastąpiłem, ale bez powodzenia :( – SadullahCeran

0

Wyjątek zwykle wskazuje, że masz 2 osobne instancje obiektu o tej samej wartości identyfikatora, który próbujesz zarządzać ciągu tej samej sesji.

0

Masz już inne wystąpienie podmiotu o tym identyfikatorze.

dwa możliwe problemy:

1 - Twój porównania podmiotu nie działa. Można zastąpić równa jak sugeruje czy można zmienić przypadek testowy, który służy przed zapisywania:

if (userToSend.Id != CurrentUser.Id) 

2 - Nie jesteś generowania unikatowy identyfikator dla danego podmiotu, należy albo przypisać identyfikator samodzielnie, NHibernate wygenerować jeden lub twój serwer sql zrobić to za Ciebie. W twoim mapowaniu zakłada się, że należy użyć Tożsamości (domyślnie Fluents), ale czy ustawiłeś kolumnę w bazie danych jako kolumnę Identity?

+0

Dziękuję za odpowiedź.Z wyjątkiem nie pochodzi w tej linii (porównanie użytkowników) .Jeśli (userToSend! = CurrentUser) jest po prostu logiki biznesowej.Określenie jest zgłaszane, gdy wiadomość jest zapisana do sesji Po drugie: kolumna jest kolumną tożsamości i chcę wygenerować identyfikator bazy danych, więc nie wstawiłem generatora id. Poza tym Id są tworzone dobrze, nie ma problemu tam – SadullahCeran

1

Czytam jakiś kod NH. W zasadzie wstawia nową instancję do bazy danych, aby uzyskać jej identyfikator. Następnie sprawdza, czy identyfikator wygenerowany przez bazę danych jest rzeczywiście unikalny. Jeśli nie, otrzymasz ten wyjątek.

Twoja baza danych nie generuje unikalnych identyfikatorów. Najprawdopodobniej zapomniałeś ustawić go w kolumnie IDENTYFIKACJA.

lub tożsamości zaczyna liczyć na 0 zamiast 1.

+0

to jest najbardziej prawdopodobny przypadek – bernhardrusch

+0

Kolumna jest kolumną tożsamości, gdzie Auto-inkrementacja jest ustawiona na true. Dzięki za wskazówkę, ale to nie moja sprawa. – SadullahCeran

+0

I tożsamość seed to 1, a nie 0? –

0

moje zdanie: nie deklarują generator id. Dlatego, gdy tylko otrzymasz dwie instancje MessageNew w sesji, oba będą miały 0 jako identyfikator.

+0

Jednak domyślna konfiguracja to Identity ID Generator i używam go w całym projekcie, nie tylko dla tego modelu.I działało poprawnie. – SadullahCeran

1

Możesz użyć Evict(), aby usunąć obiekt z sesji, a następnie możesz zrobić, co chcesz. Ten błąd występuje, gdy masz ten sam obiekt w innej sesji.

+3

Witamy w StackOverflow. Pamiętaj, że krótkie/proste odpowiedzi zawsze można wzmocnić, łącząc się ze stroną z dokumentami, która wspiera ich zwięzłość. –

0

może trochę późno, ale mam nadzieję, że to pomaga.

Miałem podobny problem, gdy próbowałem zapisać wiele wystąpień obiektu w tej samej sesji z kolumną generowaną automatycznie. Moim rozwiązaniem było podanie innej wartości i przypisanie jej do każdego podmiotu, więc nhibernates nie rozpoznaje go jako tego samego klucza podstawowego dla tej jednostki.

4

Czasami dzieje się tak, gdy przypisujemy obiekt do tego samego nowego obiektu. Najpierw sprawdź model i viewmodel, czy nie są one takie same.

-1
 [..] 
    }; 
    session.Clear(); 
    session.Save(message); 

Spróbuj tego, pomógł mi.

+3

Możesz chcieć zajrzeć trochę dalej w szczegóły. Tylko dwa wiersze kodu i "spróbuj tego" nie jest tak naprawdę odpowiedzią. – Fred

3

Miałem podobny problem. Przeszedłem przez wiele dyskusji, tutoriale i fora, ale po napisaniu kilku testów jednostkowych, zdałem sobie sprawę:

1) Metoda session.Contains współpracuje z instancjami

2) session.Save/SaveorUpdate współpracuje z ID

Ten błąd pokazuje, że masz inne instancje obiektu z tym samym identyfikatorem w session.So, zawiera return false, ponieważ pracujesz nad różnymi instancjami, a Save/SaveorUpdate zgłasza wyjątek, ponieważ w sesji znajduje się inny obiekt z tym samym identyfikatorem. Mam rozwiązać mój problem tak (mój problem był w pracy jednostki):

Job lJob = lSession.Load<Job>(this.ID); 

if(lJob.ID==this.ID) 
    lSession.Evict(lJob); 

lSession.SaveOrUpdate(this); 

Mam nadzieję, że to pomaga

-1

Dodaj poniżej dwóch linii przed Session.Save lub Session.SaveOrUpdate

Session.Clear(); 
Session.Flush(); 

ten usunie wszystkie buforowane jednostki z Sesją.

Powiązane problemy