SadullahCeran
SadullahCeran

Reputation: 2435

NHibernate, a different object with the same identifier value was already associated with the session

I have been working with NHibernate, using Fluent NHibernate for mapping. I solved a lot of issues, and started to think myself as experienced in nhibernate. However, this error is quite strange.

This is my 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; }
    }

And my mapping

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

Here where exception occurs:

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

The exception details:

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 generator is database driven auto-increment id generator. (not hilo or any other). NHibernate version is 3.2.0 .

I have tried overloading Equals and GetHashCode, no luck.

The UnitOfWork pattern I am using requires not to commit transaction or flush session inside foreach loop. NHibernate says there is another object with same id, but all i am doing is inserting a new object, which does not have any identifier at all.

I am using the same structure all over my project, and it works well everywhere but this. I am suspicious that it might be because of "Content" property, which is text and set to a large string.

What am i missing here? Or NHibernate is missing something?

Upvotes: 11

Views: 29331

Answers (11)

Manik Jadhav
Manik Jadhav

Reputation: 407

Add below two lines before Session.Save or Session.SaveOrUpdate

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

This will clear all cached entities with the Session.

Upvotes: -1

Fery
Fery

Reputation: 481

I had similar problem. I went through a lot of discussions, tutorials and forums, but after writing some unit tests, I realized:

1) session.Contains method works with instances

2)session.Save/SaveorUpdate works with ID

This error shows you have another instances of object with same ID in session.So, contains return false because you are working on different instances and Save/SaveorUpdate throws an exception because there is another object with same ID in session. I've solved my problem like this(my problem was in Job Entity):

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

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

lSession.SaveOrUpdate(this);

I hope it helps you

Upvotes: 5

progman
progman

Reputation: 41

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

Try this, helped me.

Upvotes: -1

RohannG
RohannG

Reputation: 669

Sometimes it happend when we assign the object to the same new object. So first check your model and viewmodel that they are not same.

Upvotes: 4

user3089602
user3089602

Reputation: 1

maybe a bit late but hope this helps.

I had a similar problem when i was trying to save multiple instances of an object over the same session with an auto generated column on them. My solution was giving a diferent value and assign it mannually for each entity, so nhibernates doesn't recognize it as the same primary key for that entity.

Upvotes: 0

Nic
Nic

Reputation: 1089

You can use Evict() to evict an object from a session and then you can do whatever you want. This error occurs when you have the same object in another session.

Upvotes: 1

Diego Mijelshon
Diego Mijelshon

Reputation: 52725

My take: you are not declaring an Id generator. Therefore, as soon as you get two MessageNew instances in the session, they'll both have 0 as the Id.

Upvotes: 0

Stefan Steinegger
Stefan Steinegger

Reputation: 64628

I read some NH code. It basically inserts the new instance into the database to get its id. Then it checks if the id generated by the database is actually unique. If not, you get this exception.

Your database is not generating unique ids. You most probably forgot to set it to an IDENTITY column.

OR the identity starts counting on 0 instead of 1.

Upvotes: 1

Bronumski
Bronumski

Reputation: 14272

You already have another instance of the entity with that id.

Two possible issues:

1 - Your comparison of the entity does not work. You could override equals as suggested or you could change your test case that you use prior to the save:

if (userToSend.Id  != CurrentUser.Id)

2 - You are not generating a unique Id for your entity, you need to either assign an Id yourself, have NHibernate generate one or have your sql server do it for you. In your mapping it is implied that an Identity should be used (Fluents default) but have you set up the column in your database to be and Identity column?

Upvotes: 0

Trevor Pilley
Trevor Pilley

Reputation: 16393

That exception usually indicates that you have 2 separate instances of an object with the same identifier value which you are trying to manage over the same session.

Upvotes: 0

Firo
Firo

Reputation: 30813

messagenew should implement Equals and 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; } }
}

Upvotes: 1

Related Questions