1

It's my understanding -- e.g. from sources like First and Second Level caching in NHibernate -- that NHibernate's ISession.get<T>(object id) should, when using the "default" setup -- session, etc., return the same instance if called twice with the same id. However, I'm getting two separate instances.

I've seen vaguely-similar questions but no useful results with searches like this, and this.

Here's my get method:

BillingItem IEntityRepository.GetBillingItemByID(int id)
{
    var session = Helpers.NHibernateHelper.OpenSession();

    using (ITransaction tran = session.BeginTransaction())
    {
        var ret = session.Get<BillingItem>(id);
        tran.Commit();
        return ret;
    }
}

Here's my test, which is failing:

var repo = (IEntityRepository) new SqliteEntityRepository();
var bi1 = repo.GetBillingItemByID(26);
var bi2 = repo.GetBillingItemByID(26);
Assert.AreSame(bi1, bi2); // fails

Here's NHibernateHelper just in case you want to see it:

internal static class NHibernateHelper
{
    private static ISessionFactory _sessionFactory;

    internal static ISession OpenSession()
    {
        return SessionFactory.OpenSession();
    }

    private static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
            {
                var configuration = new Configuration();
                configuration.Configure();
                configuration.AddAssembly(typeof(BillingItem).Assembly);
                configuration.AddAssembly(typeof(PaymentItem).Assembly);
                var mapper = new ModelMapper();
                mapper.AddMappings(typeof(Mappings.BillingItemMapping).Assembly.GetExportedTypes());
                mapper.AddMappings(typeof(Mappings.PaymentItemMapping).Assembly.GetExportedTypes());
                var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
                configuration.AddDeserializedMapping(mapping, null);
                SchemaMetadataUpdater.QuoteTableAndColumns(configuration);
                _sessionFactory = configuration.BuildSessionFactory();
            }

            return _sessionFactory;
        }
    }
}

What am I missing here?

rory.ap
  • 34,009
  • 10
  • 83
  • 174

2 Answers2

2

This must be true, because in a snippet above we are using ... almost anti-pattern ... a very short session:

using (ISession session = Helpers.NHibernateHelper.OpenSession())
{ ... }

That is not what we usually need. We need a Unit of Work session. In web app, it usually last through whole request... (In a desktop... there should be some UoW workaround).

So, if there are two different sessions - then both produce different run-time instance.

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • Good point. I just modified the code (see my update) to remove the session from the `using` statement, but I get the same result. – rory.ap Sep 15 '16 at 12:11
  • Well.. that is even worse ;) session is **not disposed!!** (missing using) ... but every call of that method **still creates new session** ;) so.. just wrap a session around both calls - try to read about it here http://nhibernate.info/doc/nh/en/index.html#architecture-current-session – Radim Köhler Sep 15 '16 at 12:17
  • But I want to be able to query instances from my session throughout the lifetime of the application. It's using the same session instance from the `ISessionFactory.OpenSession()`, right? It should be the same inistance -- sure, I don't close the session right there, maybe i close it when my app exits, but that's kind of besides the point because the session is still open for both `Get` calls, or should be from what I understand. – rory.ap Sep 15 '16 at 12:17
  • Session ope for **application life time** is just another extreme and again almost anti-pattern. In web.. it is easy.. use request. In desktop app .. simply create some wrapper, some Unit of Work.. where all changes will happen in ONE session.. hope that this helps to answer this question (while I know that you have to do much more ;) – Radim Köhler Sep 15 '16 at 12:19
  • Well I have to sign off for now, but thank you for your help! – rory.ap Sep 15 '16 at 12:20
1

Repositories should not be responsible for handling transactions. You need to have a single instance of unit of work that would allow you to run multiple queries in the same session/transaction.

It looks to me that the OpenSession() method creates a new session each time. Can you post the code for it?

Dmitry S.
  • 8,373
  • 2
  • 39
  • 49
  • I've updated to include the code for `NHibernateHelper`. I was copying the approach used by Jeffrey Palermo in his sample code for [the Onion Architecture](http://jeffreypalermo.com/blog/onion-architecture-part-4-after-four-years/). – rory.ap Sep 16 '16 at 11:42
  • So you can see I'm doing all the queries in the same session, because the session is never closed until just before the application exits. – rory.ap Sep 16 '16 at 11:43
  • You know what, I just realized I misread this article. I had interpreted it as saying `ISessionFactory.OpenSession()` always returns the same session for a given instance of `ISessionFactory` (which, at the time, I thought was weird, because it should have been a property instead of a method if that were true), but that appears not to be the case. – rory.ap Sep 16 '16 at 12:40
  • `ISessionFactory.OpenSesson()` always creates a new session. See the link below how to reuse a session in NHibernate. Alternatively, you can store it in `HttpContext.Items` (for web apps) or the current thread for console/windows apps. Just do not forget to dispose it at the end. http://stackoverflow.com/a/5916539/395359 – Dmitry S. Sep 16 '16 at 12:51
  • Yeah, I just implemented IDisposable on my repo and wrap it in a using block. – rory.ap Sep 16 '16 at 13:05