user204588
user204588

Reputation: 1633

Nhibernate Flush works commit doesn't

Sorry if my question doesn't have much detail but I'm new to nhibernate so not sure how to phrase. I'm wondering why calling session flush in a web application would not throw an error but Commit would.

I have this code normally:

session.SaveOrUpdate(item);
session.Flush();

but if I call session.Commit() I get a different object with the same identifier value was already associated with the session error

Doesn't Commit and Flush work the same?

Upvotes: 1

Views: 1042

Answers (2)

Radim Köhler
Radim Köhler

Reputation: 123861

The NonUniqueObjectException, you are experiencing, is thrown in scenarios, when:

  • the current ISession instance was called to get by id, e.g. .Get<TEntity>(id), and it keeps reference to this item A
  • the same ISession instance is called to persist (SaveOrUpdate) an item B and
    • both items have the same key (itemA.ID == itemB.ID)
    • both are different references (itemA != itemB)

So, if this happens, the NonUniqueObjectException ("a different object with the same identifier...") is thrown.

The Flush() and its configuration via FlushMode is the implementation of the concept of the detached persistence layer. We are working/interacting with a session, calling Read operations (mostly executed immedietly or provided from cache), calling Write operations - which are queued. Not executed. No INSERT, UPDATE, DELETE is issued into DB engine.

Only when the Flush() is called, the session does the "synchronization" of the changes to DB. The advantage (one of many) is that NHibernate can manage the order of the write statements. See:

9.6. Flush

The order of issued statements:

  • all entity insertions, in the same order the corresponding objects were saved using ISession.Save()
  • all entity updates
  • all collection deletions
  • all collection element deletions, updates and insertions
  • all collection insertions
  • all entity deletions, in the same order the corresponding objects were deleted using ISession.Delete()

And finally, this is a snippet of the enum FlushMode, which does configure the Flush() call:

/// <summary> Represents a flushing strategy.</summary>
/// <remarks>
/// The flush process synchronizes database state with session state by detecting state
/// changes and executing SQL statements
/// </remarks>
[Serializable]
public enum FlushMode
{
    /// <summary>
    /// Special value for unspecified flush mode (like <see langword="null" /> in Java).
    /// </summary>
    Unspecified = -1,
        /// <summary>
    /// The <c>ISession</c> is never flushed unless <c>Flush()</c> is explicitly
    /// called by the application. This mode is very efficient for read only
    /// transactions
    /// </summary>
    Never = 0,
        /// <summary>
    /// The <c>ISession</c> is flushed when <c>Transaction.Commit()</c> is called
    /// </summary>
    Commit = 5,
        /// <summary>
    /// The <c>ISession</c> is sometimes flushed before query execution in order to
    /// ensure that queries never return stale state. This is the default flush mode.
    /// </summary>
    Auto = 10,
        /// <summary>
    /// The <see cref="ISession"/> is flushed before every query. This is
    /// almost always unnecessary and inefficient.
    /// </summary>
    Always = 20
}

Upvotes: 3

user204588
user204588

Reputation: 1633

Seems to be because I had FlushMode.Commit and not FlushMode.Auto. Not 100% sure why

Upvotes: 0

Related Questions