Session is closed Object name: 'ISession'.

2019-06-19 16:57发布

问题:

I am using NHibernate in an MVC C# application with MySQL. I am trying to have multiple users access the session. I have been using .InRequestScope() on my session but i am still getting:

System.ObjectDisposedException: Session is closed! Object name: 'ISession'. at NHibernate.Impl.AbstractSessionImpl.ErrorIfClosed() *

...or DataReader errors when i have my colleagues all navigate to the same page that accesses a Service at the same time.

My IMasterSessionSource injection

Bind<IMasterSessionSource>().To<GeneralMasterSessionSource()
                            .InRequestScope();

My IContentService is where my mappings are getting serviced

        //ContentService Bingings
        Bind<IContentService>().To<ContentService>().InRequestScope();
        Bind<ISession>()
            .ToMethod(
                context =>
                    context.Kernel.Get<IMasterSessionSource>()
                        .ExposeConfiguration()
                        .BuildSessionFactory()
                        .OpenSession()
            )
            .WhenInjectedInto<IContentService>()
            .InRequestScope();

ContentService

public interface IContentService
    {
        IQueryable<Question> Questions{ get; }
    }


 public class ContentService : IContentService
    {
        private readonly ISession _session;

        public ContentService(ISession session)
        {
            _session = session;
        }

        public IQueryable<Question> Questions
        {
            get { return _session.Query<Question>(); }
        }
    }

DetailsService

 public interface IDetailsService
    {
        IEnumerable<Question> PullQuestions();
    }

 public class DetailsService : IDetailsService
    {
        private readonly IContentService _contentService;


        public GeneralService(IContentService contentService)
        {
            _contentService = contentService;
        }

        public IEnumerable<Question> PullQuestions()
        {
            var result = _contentService.Questions;
            return result;
        }
}

CONTROLLER

public class Test: Controller
    {

        private readonly IContentService _contentService;
        private readonly IGeneralService _generalService;

        public CollegeController(IContentService contentService, IDetailsService detailsService)
        {
            _contentService = contentService;
            _detailsService = detailsService;
        }

        public ActionResult Index()
        {
            {
                var model = new HomePageContent
                {
                    Questions = _detailsService.PullQuestions().ToList();
                };
            }
        }
    }

MODEL

 public class HomePageContent
    {
        public IEnumerable<Question> Questions { get; set; }
    }

VIEW

foreach(var question in Model.Questions){
@Html.Raw(question.Question)
}

So for a single user visiting that page. All works fine. But when mutliple users visist the same page each get the errors:

{"There is already an open DataReader associated with this Connection which must be closed first."} {"There is already an open DataReader associated with this Connection which must be closed first."} {"No current query in data reader"} {"No current query in data reader"} {"There is already an open DataReader associated with this Connection which must be closed first."} {"Session is closed!\r\nObject name: 'ISession'."}

I already added InRequestScope. I even added this implementation: NHibernate, and odd "Session is Closed!" errors

but I am still getting Sessions are closed! errors. I even tried to create a new Kernel.Get if the session was closed, but the problem is that the error sometimes occurs even when the session is open. Please help! I am at wits end with this issue, and I can't seem to find the solution anywhere. I almost think it's impossible for NHibernate to handle more than one session at once.

UPDATE

Maybe there's a way to wait for disposed session before opening new?

Stack Trace

[ObjectDisposedException: Session is closed! Object name: 'ISession'.] NHibernate.Impl.AbstractSessionImpl.ErrorIfClosed() +192
NHibernate.Impl.AbstractSessionImpl.CheckAndUpdateSessionStatus() +55 NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression) +171
NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery) +226
NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) +80 NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) +74 Remotion.Linq.QueryableBase1.GetEnumerator() +193 System.Collections.Generic.List1..ctor(IEnumerable1 collection) +432 System.Linq.Enumerable.ToList(IEnumerable1 source) +70
Gcus.PublicGeneralSite.Data.Core.Service.General.DetailsService.FindItems(String item, String controller) in c:\Users\wd\Desktop\master\Gcus.PublicGeneralSite.Data.Core\Service\General\DetailsService.cs:724 Gcus.Com.Web.Controllers.CoursesController.Details(String category, String item) in c:\Users\wd\Desktop\master\Gcus.Com.Web\Controllers\CoursesController.cs:213 lambda_method(Closure , ControllerBase , Object[] ) +366
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +87
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +603
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary
2 parameters) +93
System.Web.Mvc.Async.ActionInvocation.InvokeSynchronousActionMethod() +97 System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +53
System.Web.Mvc.Async.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) +137
System.Web.Mvc.Async.WrappedAsyncResultBase
1.End() +187
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +136
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +76
System.Web.Mvc.Async.AsyncInvocationWithFilters.b__3d() +164 System.Web.Mvc.Async.<>c__DisplayClass46.b__3f() +549 System.Web.Mvc.Async.<>c__DisplayClass33.b__32(IAsyncResult asyncResult) +75
System.Web.Mvc.Async.WrappedAsyncResult1.CallEndDelegate(IAsyncResult asyncResult) +79
System.Web.Mvc.Async.WrappedAsyncResultBase
1.End() +187
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +136
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +76
System.Web.Mvc.Async.<>c__DisplayClass2b.b__1c() +114 System.Web.Mvc.Async.<>c__DisplayClass21.b__1e(IAsyncResult asyncResult) +306
System.Web.Mvc.Async.WrappedAsyncResult1.CallEndDelegate(IAsyncResult asyncResult) +75
System.Web.Mvc.Async.WrappedAsyncResultBase
1.End() +176
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +72
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +60
System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +70
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +135
System.Web.Mvc.Async.WrappedAsyncResultBase
1.End() +176
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +72
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +51
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +66 System.Web.Mvc.Controller.b__15(IAsyncResult asyncResult, Controller controller) +60
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +98
System.Web.Mvc.Async.WrappedAsyncResultBase
1.End() +176
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +72
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +51 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +60
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +60
System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +70
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +135
System.Web.Mvc.Async.WrappedAsyncResultBase
1.End() +176
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +72
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +51
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +59
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +399 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +137

回答1:

I finally figured it out. Much appreciation to @Oskar Berggren for at least being avid on understanding my dilemma.

The problem was that I, in fact, was sharing one session all along.

here is where i am binding an open session to ContentService:

 Bind<IContentService>().To<ContentService>().InRequestScope();
        Bind<ISession>()
            .ToMethod(
                context =>
                    context.Kernel.Get<IMasterSessionSource>()
                        .ExposeConfiguration()
                        .BuildSessionFactory()
                        .OpenSession()
            )
            .WhenInjectedInto<IContentService>()
            .InRequestScope(); 

Here is where i am calling the same session in ContentService

 public class ContentService : IContentService
    {
        private readonly ISession _session;

        public ContentService(ISession session)
        {
            _session = session;
        }

        public IQueryable<Question> Questions
        {
            get { return _session.Query<Question>(); }
        }
    }

Here is where the problem is. I am calling the SAME session in another service that is being used elsewhere

public class DetailsService : IDetailsService
    {
        private readonly IContentService _contentService; //BAD


        public GeneralService(IContentService contentService)
        {
            _contentService = contentService; //BAD
        }

This is not thread safe, because that one open session is getting reused. Each service should have its own session.

so i created binding for DetailService with its own session like so....

 Bind<IDetailsService>()
                .To<DetailsService>()
                .InRequestScope();

            Bind<ISession>()
                .ToMethod(
                    context =>
                    {
                        var lockObject = new object();

                        lock (lockObject)
                        {
                            return context.Kernel.Get<IMasterSessionSource>()
                                .ExposeConfiguration()
                                .BuildSessionFactory()
                                .OpenSession();
                        }
                    }
                )
                .WhenInjectedInto<IDetailsService>()
                .InRequestScope();

And instead of calling _contentService in that service i added the session to its constrictor

    private readonly ISession Session;

    public DetailsService(ISession session)
    {
        Session = session;
    }

then just ran queries directly using Session.Query();

no more Session is closed errors, no more DataReader Errors, and finally a working product.



回答2:

I'm unfamiliar with ninject, which I suspect is involved in the problem here.

If you cannot figure out how to make that handle the sessions properly, you may have a look at NHibernate's own handling of contextual sessions: http://nhibernate.info/doc/nhibernate-reference/architecture.html#architecture-current-session