2012-03-12 2 views
9

Я работал с NHibernate, используя Fluent NHibernate для сопоставления. Я решил много проблем и начал думать, как опытный в nhibernate. Однако эта ошибка довольно странная.NHibernate, другой объект с тем же значением идентификатора уже был связан с сеансом

Это моя модель:

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

И мое отображение

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

Вот где происходит исключение:

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

сведения об исключении:

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) 

Генератор Id - генератор идентификаторов с автоматическим инкрементом, управляемый базой данных. (не hilo или любой другой). Версия NHibernate - 3.2.0.

Я попытался перегрузить Equals и GetHashCode, не повезло.

Шаблон UnitOfWork, который я использую, требует не совершать транзакции или сеанса флеша внутри цикла foreach. NHibernate говорит, что есть еще один объект с тем же идентификатором, но все, что я делаю, это вставка нового объекта, который вообще не имеет идентификатора.

Я использую ту же структуру по всему моему проекту, и она хорошо работает везде, кроме этого. Я подозрюю, что это может быть из-за свойства Content, которое является текстом и установлено в большую строку.

Что мне здесь не хватает? Или у NHibernate что-то не хватает?

+1

В вашем сопоставлении у вас есть ClassMap ', но показывая класс' Message' - это опечатка? – Rippo

+0

Да, на самом деле это «MessageNew». Спасибо за предупреждение, я отредактировал сообщение. – SadullahCeran

+0

Вы когда-нибудь это понимаете? – BueKoW

ответ

1

messagenew должен реализовать Equals и 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

Спасибо за отзыв. Я сделал переопределение, но не повезло :( – SadullahCeran

0

Это исключение, как правило, указывает на то, что у вас есть 2 отдельные экземпляры объекта с тем же значением идентификатора, который вы пытаетесь управлять через тот же сеанс.

0

У вас уже есть другой экземпляр объекта с этим идентификатором.

Возможны два вопроса:

1 - Ваше сравнение сущности не работает. Вы можете переопределить равняется, как предложено или вы могли бы изменить тестовый случай, который вы используете до сохранения:

if (userToSend.Id != CurrentUser.Id) 

2 - Вы не генерирует уникальный идентификатор для вашего лица, вам необходимо либо назначить Id самостоятельно, имеют NHibernate, генерируют один или ваш сервер sql делают это за вас. В вашем сопоставлении подразумевается, что Identity должен использоваться (Fluents default), но вы настроили столбец в своей базе данных и столбце Identity?

+0

Спасибо за ответ. Исключение не возникает в этой строке (сравнение пользователей). If (userToSend! = CurrentUser) - это просто бизнес-логика. Исключение выдается, когда сообщение сохраняется на сеансе Для второго: столбец является столбцом идентификации, и я хочу создать сгенерированный идентификатор базы данных, поэтому я не поставил генератор идентификаторов. Кроме того, идентификаторы создаются хорошо, нет проблем – SadullahCeran

1

Я прочитал некоторый код NH. Он в основном вставляет новый экземпляр в базу данных, чтобы получить свой идентификатор. Затем он проверяет, действительно ли идентификатор, созданный базой данных, уникален. Если нет, вы получите это исключение.

В вашей базе данных нет уникальных идентификаторов. Вероятно, вы забыли установить его в колонку IDENTITY.

ИЛИ личность начинает отсчет от 0 вместо 1.

+0

, это наиболее вероятный случай – bernhardrusch

+0

Столбец является столбцом идентификации, где Auto-increment имеет значение true. Спасибо за подсказку, но это не мой случай. – SadullahCeran

+0

И идентификационное семя 1, а не 0? –

0

Мое мнение: вы не заявляющего генератор Id. Поэтому, как только вы получите два экземпляра MessageNew в сеансе, они оба будут иметь 0 в качестве идентификатора.

+0

Однако по умолчанию конфигурация является Identity ID Generator, и я использую ее по всему проекту, не только для этой модели.И он работал правильно. – SadullahCeran

1

Вы можете использовать Evict() для выселения объекта из сеанса, а затем вы можете делать все, что хотите. Эта ошибка возникает, когда у вас есть тот же объект в другом сеансе.

+3

Добро пожаловать в StackOverflow. Помните, что короткие/простые ответы всегда могут быть усилены путем ссылки на страницу документов, которая поддерживает их краткость. –

0

может быть немного поздно, но надеюсь, что это поможет.

У меня была аналогичная проблема, когда я пытался сохранить несколько экземпляров объекта за тот же сеанс с автоматически созданным столбцом на них. Мое решение предоставляло разные значения и назначало его для каждого объекта, поэтому nhibernates не распознает его как один и тот же первичный ключ для этого объекта.

4

Иногда это случается, когда мы назначаем объект тому же новому объекту. Поэтому сначала проверьте свою модель и модель представления, что они не совпадают.

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

Попробуйте это, помог мне.

+3

Возможно, вы захотите пойти немного подробнее. Просто две строки кода и «попробуйте это» на самом деле не ответ. – Fred

3

У меня была аналогичная проблема. Я прошел через много дискуссий, обучающие программы и форумы, но после написания некоторых модульных тестов, я понял:

1) метод session.Contains работает с экземплярами

2) session.Save/SaveorUpdate работает с ID

Эта ошибка показывает, что у вас есть еще один экземпляр объекта с тем же идентификатором в session.So, содержит return false, потому что вы работаете с разными экземплярами, а Save/SaveorUpdate выдает исключение, потому что в сеансе есть другой объект с тем же идентификатором. Я решил мою проблему, как это (моя проблема была в работе Entity):

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

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

lSession.SaveOrUpdate(this); 

Я надеюсь, что это поможет вам

-1

Добавить ниже два строк перед Session.Save или Session.SaveOrUpdate

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

Это очистит все кэшированные объекты с помощью сеанса.

Смежные вопросы

 Смежные вопросы