Trouble with ISessionLocator with sessions being closed

Topics: Customizing Orchard
Dec 11, 2014 at 4:51 PM
I have been following the example at https://bitbucket.org/bleroy/orchardpo. I am trying to do simple (non-content-based) Orchard DB access.

I have successfully queried the database and gotten data back. But I get a "Session was already closed" exception somewhere down the line after the session from the SessionLocator.For() has been disposed. I have found that it works fine if I don't use a using (and I don't manually dispose of the session). But that gives me the heebie-jeebies. :)

Can you offer any insight to how I can stick with a proper using, properly dispose of my session object, and not get the "Session was already closed" exception later?

Thanks in advance (and thanks Bertrand for sharing orchardPo!)

Here is the code I am using:
using System.Linq;
using Advantage.Cider.Catalog.Models;
using NHibernate;
using Orchard;
using Orchard.Data;

namespace Advantage.Cider.Catalog.Services
{
    public interface ITempService : IDependency
    {
        void ReadRows();
    }

    public class TempTemp
    {
        public int Id { get; set; }
        public string ColumnA { get; set; }
        public string ColumnB { get; set; }
        public string ColumnC { get; set; }
    }

    public class TempService : ITempService
    {
        private readonly ISessionLocator _sessionLocator;

        public TempService(ISessionLocator sessionLocator)
        {
            _sessionLocator = sessionLocator;
        }

        public void ReadRows()
        {
            using (var session = _sessionLocator.For(typeof (TempRecord)))
            {
                var rows =
                    session.CreateSQLQuery(
                        @"
                    SELECT 
                        X.Id,
                        X.ColumnA,
                        X.ColumnB,
                        X.ColumnC
                    FROM Cider_Catalog_TempRecord as X")
                        .AddScalar("Id", NHibernateUtil.Int32)
                        .AddScalar("ColumnA", NHibernateUtil.String)
                        .AddScalar("ColumnB", NHibernateUtil.String)
                        .AddScalar("ColumnC", NHibernateUtil.String);

                var result = rows.List<object[]>()
                    .Select(t => new TempTemp()
                    {
                        Id = (int) t[0],
                        ColumnA = (string) t[1],
                        ColumnB = (string) t[2],
                        ColumnC = (string) t[3]
                    }).ToList();
            }
        }
    }
}
            // Temporary table creation (from the migrations.cs file in the module)
            SchemaBuilder.CreateTable(
                "TempRecord",
                table =>
                table
                    .Column<int>("Id", column => column.PrimaryKey().Identity())
                    .Column<string>("ColumnA")
                    .Column<string>("ColumnB")
                    .Column<string>("ColumnC")
            );

namespace Advantage.Cider.Catalog.Models
{
    public class TempRecord
    {
        public TempRecord()
        {    
        }

        public virtual int Id { get; set; }
        public virtual string ColumnA { get; set; }
        public virtual string ColumnB { get; set; }
        public virtual string COlumnC { get; set; }
        
    }
}
Dec 12, 2014 at 5:09 AM
Normally, it's the session locator, at the end of its lifetime (here an unit of work as your service, e.g a web request) in its own dispose method, that uses the shared session to commit the shared transaction and only after close and dispose the session. With an using block on the session, you will dispose it before

At the beginning of a request, to create its context, normally Orchard has already opened a session and begun a transaction. So, when you call _sessionLocator.For(), you only share it

I don't know with older versions, but by reading the code of a quite recent 1.8.x, the normal way is to not use an using block