NHibernate session is closed when refreshing page - asp.net-mvc

This is another strange problem I've encountered this days!!! I've created and MVC 4 app using nhibernate. and added a filter attribute named [LoggingNHibernateSessionAttribute] on my HomeController which manages session for each action. I've followed 'ASP.NET MVC4 and the Web API published by Apress'.
public class LoggingNHibernateSessionAttribute : ActionFilterAttribute
{
private readonly IActionLogHelper _actionLogHelper;
private readonly IActionExceptionHandler _actionExceptionHandler;
private readonly IActionTransactionHelper _actionTransactionHelper;
public LoggingNHibernateSessionAttribute()
: this(WebContainerManager.Get<IActionLogHelper>(),
WebContainerManager.Get<IActionExceptionHandler>(),
WebContainerManager.Get<IActionTransactionHelper>())
{
}
public LoggingNHibernateSessionAttribute(
IActionLogHelper actionLogHelper,
IActionExceptionHandler actionExceptionHandler,
IActionTransactionHelper actionTransactionHelper)
{
_actionLogHelper = actionLogHelper;
_actionExceptionHandler = actionExceptionHandler;
_actionTransactionHelper = actionTransactionHelper;
}
public override void OnActionExecuting(ActionExecutingContext actionExectingContext)
{
_actionLogHelper.LogEntry(actionExectingContext.ActionDescriptor);
_actionTransactionHelper.BeginTransaction();
}
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
_actionTransactionHelper.EndTransaction(actionExecutedContext);
_actionTransactionHelper.CloseSession();
_actionExceptionHandler.HandleException(actionExecutedContext);
_actionLogHelper.LogExit(actionExecutedContext.ActionDescriptor);
}
}
ActionTransactionHelper
public class ActionTransactionHelper : IActionTransactionHelper
{
private readonly ISessionFactory _sessionFactory;
private readonly ICurrentSessionContextAdapter _currentSessionContextAdapter;
public ActionTransactionHelper(
ISessionFactory sessionFactory,
ICurrentSessionContextAdapter currentSessionContextAdapter)
{
_sessionFactory = sessionFactory;
_currentSessionContextAdapter = currentSessionContextAdapter;
}
public void BeginTransaction()
{
var session = _sessionFactory.GetCurrentSession();
if (session != null)
{
session.BeginTransaction();
}
}
public bool TransactionHandled { get; private set; }
public void EndTransaction(ActionExecutedContext filterContext)
{
var session = _sessionFactory.GetCurrentSession();
if (session == null) return;
if (!session.Transaction.IsActive) return;
if (filterContext.Exception == null)
{
session.Flush();
session.Transaction.Commit();
}
else
{
session.Transaction.Rollback();
}
TransactionHandled = true;
}
public bool SessionClosed { get; private set; }
public void CloseSession()
{
if (_currentSessionContextAdapter.HasBind(_sessionFactory))
{
var session = _sessionFactory.GetCurrentSession();
session.Close();
session.Dispose();
_currentSessionContextAdapter.Unbind(_sessionFactory);
SessionClosed = true;
}
}
}
when run the app, I can save an entity in the dataBase. but when I hit refresh button and exception thrown indication session is closed.
I don't know why this happens. (I searched and find this NHibernate throwing Session is closed but couldn't solve my problem).
in my NinjectConfigurator I added inRequestScope() to all of injections but no answer. I checked when I refresh the page session will be opened. but I donnow why it say session is closed?!
UPDATE:
when I first run the app. I can create a new member. but when I hit the refresh button, the session will be closed unexpectedly!!
first run:
everything works well
after hitting refresh button:
a new session bind to the current context.
the new session will be injected the repository (session is open)
the ActionTransactionHelper calls beginTransaction()
4- customMembership createUser (....) called
5- but when the _userRepositoy.save(user)called in the repository session is closed!!!!
note:but when still endTransaction and closeSession isn't called. but how session is closed?
if I comment closeSession() in onActionExecute(). session alway is open and everything woks well if refresh the page.
I checked a lot and tried different way I knew. it only happens when for the second time I want to do CRUD operations with my customMembership.
for other entities it works like a charm!
I have upoaded my sample code. for testing just create and empty database and change connection string. then go to localHost:*****/api/categories (user and pass doesn't required)
Download sample project:
Size: 47 MB
https://www.dropbox.com/s/o63wjng5f799fii/Hashem-MVC4ServicesBook.rar
size: 54 MB
Zip Format: https://www.dropbox.com/s/smrsbz4cbtznx1y/Hashem-MVC4ServicesBook2.zip

A very important thing here, could be the nature of the NHibernate. The NHibernate and its Session are in the ASP.NET MVC living longer, then could be expected. I mean not only inside of the
ActionExecuting (Controller Action starts)
ActionExecuted (the View or Redirect is called)
Session in fact must live also through the phase of rendering. Because, we could load some proxy in the "Action()" but its collection, could be lazily loaded only during the View rendering. So even in these phases Session must be opened (the same Session from the request begining)
ResultExecuting (the proxy could start to be loaded only here)
ResultExecuted (almost all is done, let's close the session)
Other words... keep the session opened throught the complete Request. From authorization untill the content is rendered.
NOTE: Anohter hint, just to be sure that all is ok, I am using this scenario (maybe you do as well):
Client FORM is about to send the data to server. The method is POST, the Action is Update()
Sent FORM is coming to server, Action Update() is triggerred - all the transactions stuff is in place (as described above)
Once NHibernate persists the data into DB, the Update() action ends, and is redirected to action
Detail() if all is ok or
Edit() if something goes wrong
The users Browser was redirected to action Detail or Edit. So if user does REFRESH, the Detail or Edit is refreshed. The Update() is not called at all (it is a POST method)
In fact, the step 1. was one of the Actions Detail or Edit. In this case, we would face this issue already...

You have this error since Asp.Net MVC does not create a new instance of LoggingNHibernateSessionAttribute every request. It creates a new instance when you request an action first time and then uses this instance in the future.
The behaviour is the following:
First invocation of Post -> new instance of 'LoggingNHibernateSession' is created
First invocation of Put -> another one instance of 'LoggingNHibernateSession' is created
Second invocation of Put -> instance of 'LoggingNHibernateSession' from previous step is used
First invocation of Delete -> another one instance of 'LoggingNHibernateSession' is created
[LoggingNHibernateSession]
public JsonResult Post(Dto data)
{
/* ... */
}
[LoggingNHibernateSession]
public JsonResult Put(int id, Dto data)
{
/* ... */
}
[LoggingNHibernateSession]
public JsonResult Delete(int id)
{
/* ... */
}
It can be solved using Func<IActionLogHelper> instead of IActionLogHelper in the constructor. An instance of IActionLogHelper can be initialised within OnActionExecuting method.
public class LoggingNHibernateSessionAttribute : ActionFilterAttribute
{
/* your code */
private readonly Func<IActionTransactionHelper> _getActionTransactionHelper;
private IActionTransactionHelper _actionTransactionHelper;
public LoggingNHibernateSessionAttribute()
: this(WebContainerManager.Get<IActionLogHelper>(),
WebContainerManager.Get<IActionExceptionHandler>(),
() => WebContainerManager.Get<IActionTransactionHelper>())
{
}
public LoggingNHibernateSessionAttribute(
IActionLogHelper actionLogHelper,
IActionExceptionHandler actionExceptionHandler,
Func<IActionTransactionHelper> getActionTransactionHelper)
{
_actionLogHelper = actionLogHelper;
_actionExceptionHandler = actionExceptionHandler;
_getActionTransactionHelper = getActionTransactionHelper;
_actionTransactionHelper = null;
}
public override void OnActionExecuting(ActionExecutingContext actionExectingContext)
{
_actionTransactionHelper = _getActionTransactionHelper();
_actionLogHelper.LogEntry(actionExectingContext.ActionDescriptor);
_actionTransactionHelper.BeginTransaction();
}
/* your code */
}

Related

asp.net mvc web api 2 custom logger + controller lifecycle

story:I have following component in my asp.net mvc 5 web api project(using ef code first)
myBaseController : base api controller class
myLogger : my costume logger class
class myBaseController { MyLogger Logger; }
class MyLogger
{
public addLog(Log log)
{
db.logs.add(log);
db.saveChanges();
}
}
Each api controller use base.logger.addLog(new log("somethings")) and the log committed to database immediately.
Problem: I want MyLogger class be able to collect all log in memory and some where add theme to database at once.
I put it in
protected override void Dispose(bool disposing)
{
logger.AddRange(_logs);
}
but it doesn't work,it seems objects are not available in this method or grabbed from memory
How it should be handled?
You don't need to collect records in memory separately. Because EF will do that for you. For instance db.logs.add means, please add it to memory. But when you call SaveChanges it will commit all of the in-memory collected data to the database at once. So you will need to implement the unit of work pattern (UoW). By using this pattern, you will have only one context per request and then at the end of the request you will call the SaveChanges method once. You shouldn't have multiple SaveChanges everywhere in your codes, this is your problem right now. Your addLog method calls SaveChanges, your other methods call SaveChanges and so on.
More info from Microsoft: Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application
You need to store your entries in memory during request cycle and at end of request you can save those entries into database
protected void Application_EndRequest()
{
//get entries from memory/session/cache whatever your source to store entries
//do stuff to save entries
}
I normally create ActionFilterAttributes, and place it on action methods where I want to log any activity.
TraceMvcAttribute
public class TraceMvcAttribute : ActionFilterAttribute
{
// IoC container will inject this dependencies.
public IWebUserSession WebUserSession { get; set; }
public IDateTime DateTime { get; set; }
public ITraceListener TraceListener { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var traceLog = new TraceLog
{
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
Message = JsonConvert.SerializeObject(filterContext.ActionParameters),
PerformedOn = this.DateTime.Now,
PerformedBy = WebUserSession?.UserName
};
TraceListener.AddTraceLogAsync(traceLog);
base.OnActionExecuting(filterContext);
}
}
TraceApiAttribute
Web API filter is a little bit different from MVC filter. I do not have it in my sample code at Git; in case you might want to take a look at it.
public class TraceApiAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// http://autofac.readthedocs.org/en/latest/integration/webapi.html#standard-web-api-filters-are-singletons
// All filter attributes in Web API are effectively singleton instances that exist
// for the entire lifetime of the application. We must use service location,
// since we need per-request services in your filters.
var requestScope = actionContext.Request.GetDependencyScope();
var datetime = requestScope.GetService(typeof(IDateTime)) as IDateTime;
var webUserSession = requestScope.GetService(typeof(IWebUserSession)) as IWebUserSession;
var traceListener = requestScope.GetService(typeof(ITraceListener)) as ITraceListener;
var traceLog = new TraceLog
{
Controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName,
Action = actionContext.ActionDescriptor.ActionName,
Message = JsonConvert.SerializeObject(actionContext.ActionArguments.Where(x => x.Key != "request").ToList()),
PerformDateTime = datetime.Now,
PerformBy = webUserSession?.UserName
};
traceListener.AddTraceLogAsync(traceLog);
base.OnActionExecuting(actionContext);
}
}
I then view the log history like this -

Cannot get a working Unity Session Lifetime Manager, ASP.NET MVC5

I've read and Googled everything on this, but can't seem to get it to work. I created a custom LifetimeManager for Unity in my MVC5 application based on these posts:
MVC3 Unity Framework and Per Session Lifetime Manager
This may be the issue I am experiencing
Here is my SessionLifetimeManager
public class SessionLifetimeManager : LifetimeManager
{
private string key = Guid.NewGuid().ToString();
public override object GetValue()
{
return HttpContext.Current.Session[key];
}
public override void RemoveValue()
{
HttpContext.Current.Session.Remove(key);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Session[key] = newValue;
}
}
I only have a few types I'm playing with, here is the relevant registrations in UnityConfig.cs:
container.RegisterType<IEpiSession, EpiSession>(new SessionLifetimeManager(),
new InjectionConstructor(config.AppServerURI, config.PathToSysConfig));
container.RegisterType<IReportRepository, EpicorReportRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Note that the EpicorReportRepository has a dependency on IEpiSession via constructor injection.
public class EpicorReportRepository : IReportRepository
{
private IEpiSession session;
// DI constructor
public EpicorReportRepository(IEpiSession session) {
this.session = session;
}
// ...
}
My Problem: After the first user / session connects to the application, every new user / session after that seems to still be using the EpiSession object and credentials that the first user had create/injected for him. This seems to be a common pattern used on the interwebs, so I'm wondering what I am missing.
How did you test that IEpiSession is the same in different Sessions?
Try to open you application from different browsers. If you open several tabs in the same browser then the same session is used.
I checked your code and it works for me.
There is the only one difference in SetResolver():
DependencyResolver.SetResolver(
type => container.Resolve(type),
types => container.ResolveAll(types));
The full registration code is the following:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
var container = new UnityContainer();
container.RegisterType<IEpiSession, EpiSession>(
new SessionLifetimeManager(),
new InjectionConstructor("config.AppServerURI", "config.PathToSysConfig"));
container.RegisterType<IReportRepository, EpicorReportRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(
type => container.Resolve(type),
types => container.ResolveAll(types));
}
}

NHibernate Session Closed In Global Action Filter

I have the following global action filter:
public class AddWidgetsAttribute : ActionFilterAttribute {
private ISession _session;
public ISession Session {
get {
if (_session == null)
_session = DependencyResolver.Current.GetService<ISession>();
return _session;
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext) {
filterContext.Controller.ViewBag.Widgets = Session.Query<Widget>().ToList();
}
}
Here's the code which adds the filter in the Application_Start event:
GlobalFilters.Filters.Add(new AddWidgetsAttribute());
This works fine on the first request but then it throws an error telling me the session is closed. If I change the Session property to the following:
public ISession Session {
get { return DependencyResolver.Current.GetService<ISession>(); }
}
It works fine but it seems like abit of a hack. Is there a nice way to inject the dependency within global action filters?
I'd appreciate the help. Thanks
There is no problem doing this, but, keep in your mind, if you instance a global filter, it will add a single instance into your application. So, your code check if the _session object is not null and instance it, but, when you close the session you still have the reference on the global filter, because it is a single instance, and worst, the session is closed. So, the next access will get a non null object and closed session.
I would keep the DependencyResolver to give me the instance of the session everytime, because you are controlling it in another level (I want to understand you are doing a session per request).
public ISession Session
{
get
{
return DependencyResolver.Current.GetService<ISession>();
}
}
And the asp.net mvc will get the right instance every request.

When to commit NHibernate Transaction?

While very familiar to Webforms and Linq, I am a novice to the ASP.NET MVC and NHibernate World. I have been working through a project using Bob Cravens' examples. My application is mostly reads and non-sequential writes, so typically I would not use a transactions. But to implement the Unit-of-Work pattern, all my research including Ayende's blog says I should.
The problem I have is this -
Ninject creates a Session and Opens a Transaction.
Ninject injects repositories into services, and services into controllers.
I make some changes to the properties and children of an object and save on the aggregate root. This calls Transaction.Commit (works fine, as expected)
In another method later in the controller, I try to save a separate object
The second call fails because the transaction is no longer active.
I'm thinking of adding a bool "CommitNeeded" to the UnitOfWork which would be set by my Save() method and conditionally trigger a Commit() on UnitOfWork.Dispose(). Is this a good idea?
Should I remove the transaction infrastructure? Should I change my Commit()'s to Flush()'s?
Any advice that would help fix my anti-pattern would be appreciated.
In response to the comments - I guess I don't mind if they happen together or separate. There are two things going on. The first one changes a "Customer" object, and then saves it. The second makes a logging entry which then calls the same "Save" method.
var Customer = ServiceLayer.GetCustomer(...);
Transmogrify(Customer, Customer.Children, Widgets, ...);
ServiceLayer.Save(Customer)
ServiceLayer.RecordEvent(Customer, "Customer was frobbed")
where LogEvent looks like
public void RecordEvent(Customer customer, int eventTypeId, string description)
{
...
Save(customer);
}
The RecordEvent method has its own "save" because it is called from other controllers that do no data changes. I believe the Save call doesn't belong in either of those places. The question is, where? The Dispose() method of the Service Layer? or a Filter like the other users suggested?
Using ASP.NET MVC, I use an action filter to bind the transaction scope to the controller action execution lifecycle. This works great most of the time, but you have to be cautious not to keep transactions open too long.
public class UnitOfWorkActionFilter : ActionFilterAttribute
{
public IUnitOfWork UnitOfWork { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
UnitOfWork.Start();
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception == null)
{
UnitOfWork.Commit();
}
else
{
UnitOfWork.Rollback();
}
UnitOfWork.Dispose();
base.OnActionExecuted(filterContext);
}
}
In my case I'm using property injection via a custom ControllerActionInvoker to get the IUnitOfWork dependency into the ActionFilterAttribute.
I'm using for that an http module. I get the transaction at the beginning of http request and terminate it on the end of http request :
public class UnitOfWorkModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
context.EndRequest += context_EndRequest;
}
private void context_BeginRequest(object sender, EventArgs e)
{
IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
instance.Begin();
}
private void context_EndRequest(object sender, EventArgs e)
{
IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
try
{
instance.Commit();
}
catch
{
instance.RollBack();
}
finally
{
instance.Dispose();
}
}
}
My unit of work factory it's just a Func initialized while registering types in IoC container :
public class UnitOfWorkFactory
{
public static Func<IUnitOfWork> GetDefault;
}
Initialization (for my case StructureMap) :
UnitOfWorkFactory.GetDefault = () => container.GetInstance<IUnitOfWork>();
And then you register you in UnitOfWorkModule web.config
<httpModules>
<add name="UnitOfWorkModule" type="UI.UnitOfWorkModule, UI" />
</httpModules>

Best approach to don't request same info over and over

On my controller I have it inherit a MainController and there I override the Initialize and the OnActionExecuting.
Here I see what is the URL and by that I can check what Client is it, but I learned that for every Method called, this is fired up again and again, even a simple redirectToAction will fire the Initialization of the same controller.
Is there a better technique to avoid this repetition of database call? I'm using Entity Framework, so it will take no time to call the DB as it has the result in cache already, but ... just to know if there is a better technique now in MVC3 rather that host the variables in a Session Variable
sample code
public class MyController : MainController
{
public ActionResult Index()
{
return View();
}
}
public class MainController : Controller
{
public OS_Clients currentClient { get; set; }
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
// get URL Info
string url = requestContext.HttpContext.Request.Url.AbsoluteUri;
string action = requestContext.RouteData.GetRequiredString("action");
string controller = requestContext.RouteData.GetRequiredString("controller");
object _clientUrl = requestContext.RouteData.Values["cliurl"];
if (_clientUrl != null && _clientUrl.ToString() != "none")
{
// Fill up variables
this.currrentClient = db.FindClientById(_clientUrl.ToString());
}
base.Initialize(requestContext);
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// based on client and other variables, redirect to Disable or Login Actions
// ... more code here like:
// filterContext.Result = RedirectToAction("Login", "My");
base.OnActionExecuting(filterContext);
}
}
is it still best to do as:
public OS_Clients currentClient {
get {
OS_Clients _currentClient = null;
if (Session["CurrentClient"] != null)
_currentClient = (OS_Clients)Session["CurrentClient"];
return _currentClient;
}
set {
Session["CurrentClient"] = value;
}
}
It seems that you dealing with application security in that case I would suggest to create Authorization filter, which comes much early into the action. You can put your permission checking code over there and the framework will automatically redirect the user to login page if the permission does not meet AuthorizeCore.
Next, if the user has permission you can use the HttpContext.Items as a request level cache. And then you can create another ActionFilter and in action executing or you can use the base controller to get the user from the Httpcontext.items and assign it to controller property.
If you are using asp.net mvc 3 then you can use the GlobalFilters to register the above mentioned filters instead of decorating each controller.
Hope that helps.
In your base controller, you need to cache the result of the first call in a Session variable.
This makes sure the back-end (DB) is not called unnecessarily, and that the data is bound to the user's Session instead of shared across users, as would be the case with the Application Cache.

Resources