6

Is it possible to set read-only mode on entities that are loaded using NHibernate's Linq provider?

For example, the following can be done with Session.QueryOver (and I believe with Criteria as well):

Session.QueryOver(Of Foo)().ReadOnly()

Is there an equivilent for Session.Query available?

DanP
  • 6,310
  • 4
  • 40
  • 68

3 Answers3

8

As stated in the documentation 10.1.2. Loading persistent entities as read-only:

To change the default behavior so NHibernate loads entity instances of mutable classes into the session and automatically makes them read-only, call:

Session.DefaultReadOnly = true;

To change the default back so entities loaded by NHibernate are not made read-only, call:

Session.DefaultReadOnly = false;

So before calling Session.Query... call Session.DefaultReadonly = true, because this setting goes to the ISession not to the Provider on top of it.

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • Thanks for this! I wasn't aware of this session-level setting. Strange how they implemented a "Read Only" operator for Criteria and QueryOver, but not for the Linq provider. – DanP May 30 '13 at 13:11
  • 1
    @DanP, that would be a nice and easy feature to implement. You can open an issue at https://nhibernate.jira.com. Source is at https://github.com/nhibernate/nhibernate-core. This would probably include a change in `LinqExtensionMethods` and `NhQueryable`. – Diego Mijelshon May 30 '13 at 13:39
  • 3
    @DiegoMijelshon - I've opened the following issue: https://nhibernate.jira.com/browse/NH-3470 – DanP May 30 '13 at 14:55
2

if you need read only session, better approach is here: how to create a readonly session in nHiberate?

Session.DefaultReadOnly = true; - in this case NHibernate will accumulate all updates for entities (call all listeners e.t.c.)

session.FlushMode = FlushMode.Never

in this case NHibernate will not call any listeners, as I know

Community
  • 1
  • 1
razon
  • 3,882
  • 2
  • 33
  • 46
0

Based on the comments of the marked answer the feature is already implemented in HN and the solution is as follows:

var result = await session
    .Query<Company>()
    .WithOptions(x => x.SetReadOnly(true))
    .ToListAsync();

Best approach based on my experience (isolation level ReadCommitted). The following session does not require database modifications:

// Readonly mode is used for running only READ queries without any changes. Flush mode is not needed.
using (var session = sessionFactory.WithOptions().FlushMode(FlushMode.Never).OpenSession())
{
    session.DefaultReadOnly = true;

    // Do your read only queries here
}

The following session used when some modifications are required but we do not want to lock tables which are not going to be modified:

// The Flush (send all pending changes to the db) will be executed automatically on Commit.
using (var session = sessionFactory.WithOptions().FlushMode(FlushMode.Commit).OpenSession())
{
    // Read committed with snapshot (https://stackoverflow.com/a/1664236/1143349)
    using (var tran = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
    {
        // We are not modifiing settings so we can get a record in readonly mode
        // Because of readonly, we do not lock the [settings] table for the time of that transaction.
        // So, other sessions can use that table too at the same time.
        var setting = session.Query<Setting>()
            .WithOptions(x => x.SetReadOnly(true))
            .Where(x => x.Key == "IsTenantRequired")
            .SingleOrDefault();

        if (setting?.Value == true)
        {
            var newTenant = new Tenant("My Tenant Name");
            session.Save(newTenant);
        }

        // Do your other transactional changes 
        // but you promised to not touch the [settings] table (no modifications) 
        // in the current transaction.

        tran.Commit();

    } // Rollback happens here in case of errors

} // Session is closed here
ADM-IT
  • 3,719
  • 1
  • 25
  • 26