I am using Fluent NHibernate as our ORM and I am getting an Error of memory Leak.
I have observerd it in Task Manager that, whenever I tried to access the Home page from different web Browsers in the same PC, CPU usage is 2-3% but Memory usage get 80-90% which results in slowing of website and leads to system Hang.
And to run my webisite again I have to End the process from Task Manager. One more thing, when I access it from a Browser it use some memory but when i close it , It do no release all resources(that memory).
I have made the architecture of my webiste in this way :-
I have made a class "ParentObject" in which i have created Repository object as a static member.
All Entities Which I have created have been Inherited from "ParentObject" Class.
I have made one more class BaseController which I have inherited from "Controller" class and in Base class I have used this code :-
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
EdustructRepository Repository; // My Repository Class where I have written logic for opening and closing Save and Update Session.I have mentioned my this logic below
if (Session["Repository"] != null)
{
Repository = (EdustructRepository)Session["Repository"];
if (!Repository.GetSession().Transaction.IsActive)
Repository.GetSession().Clear();
}
else
{
Repository = new EdustructRepository(typeof(ActivityType), FluentNhibernateRepository.DataBaseTypes.MySql);
Session["Repository"] = Repository;
}
if (ParentObject._repository == null)
{
ParentObject._repository = new EdustructRepository(); // Here i have set the ParentObject's static variable "_repository" by this i have accessed repository in all my Entities .
}
}
And I have Inherited all my controller with BaseController Class. By this I have got the "_repository" object with every Action hit.
My session management Logic
public class EdustructRepository : NHibernetRepository
{
public void Save<T>(T item, bool clearSession)
{
if (typeof(T).GetProperty("Created_at").GetValue(item, null).ToString() == DateTime.MinValue.ToString())
{
typeof(T).GetProperty("Created_at").SetValue(item, MySqlDateTime.CurrentDateTime(), null);
}
typeof(T).GetProperty("Updated_at").SetValue(item, MySqlDateTime.CurrentDateTime(), null);
base.CheckAndOpenSession();
using (var transaction = base.GetSession().BeginTransaction())
{
try
{
base.GetSession().SaveOrUpdate(item);
transaction.Commit();
if (clearSession)
{
Session.Clear();
}
}
catch
{
base.Evict(item);
base.Clear();
throw;
}
}
//base.Save<T>(item, clearSession);
}
public void Save<T>(T item)
{
Save<T>(item, false);
}
}
public class NHibernetRepository : IDisposable
{
public static ISessionFactory _SessionFactory = null;
protected ISession Session = null;
private ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(c => c.FromConnectionStringWithKey("DBConnectionString")))
.Mappings(m =>m.FluentMappings.AddFromAssembly((Assembly.Load("Edustruct.Social.DataModel"))).Conventions.Add<CascadeConvention>())
.ExposeConfiguration(cfg => cfg.SetProperty(NHibernate.Cfg.Environment.CurrentSessionContextClass,"web"))
.BuildSessionFactory();
}
protected void CheckAndOpenSession()
{
if (_SessionFactory == null)
{
_SessionFactory = CreateSessionFactory();
}
if (Session == null)
{
Session = _SessionFactory.OpenSession();
Session.FlushMode = FlushMode.Auto;
}
if (!Session.IsOpen)
Session = _SessionFactory.OpenSession();
else if (!Session.IsConnected)
Session.Reconnect();
}
}
Note: We haven't closed Session in our Repository it is because I am using lazy Initialization also I have used it at Views so if I close session here I get an error showing "Session not found".
This is how I have made flow of my website.
Would you please review this code and let me know why I am getting this Error.
Thanking you in advance.
Problems:
you basicly hold one session per entity open forever. However ISession implements the unit of work pattern which is not meant for this.
CheckAndOpenSession() is not thread safe but webserving is inherently threaded: each request typically gets its own thread.
Whats the use
Every business operation should have its own session which is disposed of at the end of it. Business operation is typically a controller action or web method.
sample
// on appstart
GlobalSessionFactory = CreateSessionFactory();
// in basecontroller befor action
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
DatabaseSession = GlobalSessionFactory.OpenSession();
}
// in basecontroller after action (pseudocode)
protected override void OnActionExecuted()
{
DatabaseSession.Dispose();
}
Related
I can't find any documentation on how to use Autofac together with Lazy and lifetime scopes. Getting an error about
"No scope with a Tag matching 'transaction' is visible from the scope
in which the instance was requested..."
In my Controller constructor:
public HomeController(Lazy<ISalesAgentRepository> salesAgentRepository, Lazy<ICheckpointValueRepository> checkpointValueRepository)
{
_salesAgentRepository = new Lazy<ISalesAgentRepository>(() => DependencyResolver.Current.GetService<ISalesAgentRepository>());
_checkpointValueRepository = new Lazy<ICheckpointValueRepository>(() => DependencyResolver.Current.GetService<ICheckpointValueRepository>());
}
In my Action:
using (var transactionScope = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope("transaction"))
{
using (var repositoryScope = transactionScope.BeginLifetimeScope())
{
// ....
}
}
Are lifetime scopes incompatible with Lazy or did I got it completely wrong?
Yes, you are barking up the wrong tree.
A new controller is created for every new application request. Hence no need to try to manage the lifetime of the dependencies separately.
Configure your repositories to have a scoped lifetime. Do the same for the transaction scope.
When done both repositories will have the same shared transactionScope.
You can also move the transaction commit to an action filter, like this:
public class TransactionalAttribute : ActionFilterAttribute
{
private IUnitOfWork _unitOfWork;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.Controller.ViewData.ModelState.IsValid && filterContext.HttpContext.Error == null)
_unitOfWork = DependencyResolver.Current.GetService<IUnitOfWork>();
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Controller.ViewData.ModelState.IsValid && filterContext.HttpContext.Error == null && _unitOfWork != null)
_unitOfWork.SaveChanges();
base.OnActionExecuted(filterContext);
}
}
(replace IUnitOfWork with transactionscope). Source: http://blog.gauffin.org/2012/06/05/how-to-handle-transactions-in-asp-net-mvc3/
I'm trying to create a UnitOfWork Action Filter.
I'm hooking into the OnActionExecuted method where i want to save all changes to the DBContext depending on three rules :-
The Context is NOT NULL.
The Context ChangeTracker HasChanges
No Exceptions have been caught throughout the lifetime of the ActionMethods existence.
Throughout the action methods in the WEB API, I only ever attach entities to the DbContext, its only when the action itself has completed without any errors do I commit changes.
The DbContext in setup using Ninject witha lifestyle of "InRequestScope".
Here is the UnitOfWork ActionFilterAttribute :-
public class UnitOfWorkActionFilterAttribute : ActionFilterAttribute
{
public virtual IActionTransactionHelper ActionTransactionHelper
{
get { return WebContainerManager.Get<IActionTransactionHelper>(); }
}
public override bool AllowMultiple
{
get { return false; }
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
ActionTransactionHelper.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
ActionTransactionHelper.EndTransaction(actionExecutedContext);
ActionTransactionHelper.CloseSession();
}
}
In Ninject, the DbContext is configured like this :-
container.Bind<MyDbContext>().ToSelf().InRequestScope();
Here is the ActionTransactionHelper Class :-
public class ActionTransactionHelper : IActionTransactionHelper
{
public bool TransactionHandled { get; private set; }
public bool SessionClosed { get; private set; }
public void BeginTransaction()
{
var sessionContext = WebContainerManager.Get<MyDbContext>();
if (sessionContext == null)
{
throw new NullReferenceException("sessioncontext");
}
}
public async Task EndTransaction(HttpActionExecutedContext filterContext)
{
var sessionContext = WebContainerManager.Get<MyDbContext>();
if (sessionContext != null && sessionContext.ChangeTracker.HasChanges() && filterContext.Exception == null)
{
var x = await sessionContext.SaveChangesAsync();
}
TransactionHandled = true;
}
public void CloseSession()
{
var sessionContext = WebContainerManager.Get<MyDbContext>();
if (sessionContext == null) return;
sessionContext.Dispose();
SessionClosed = true;
}
}
I have an Action method that attaches enities to the DBContext like this :-
context.Claims.Add(entity);
When the EndTransaction() Method is fired on ActionExecuted, the context object has no record in the ChangeTracker of any changes and the SaveChangesAsync() method is never fired.
However, If i change the Ninject Binding for the DbContext to a Singleton, the code works fine, the change is tracked and the object is saved to the database.
I don't understand why this isn't working per web request?
I dont want to use Singletons in this case.
This problem was resolved by adding the Ninject.WeApi2 Nuget Package.
In the NinjectWebCommon i replaced the NinjectDependencyResolver Implementation with the one referenced in the Ninject.WeApi2
**Ninject.Web.WebApi.NinjectDependencyResolver**
InRequestScope() now works fine with Action Filters and the DBContext tracks changes.
In my System.Web.Mvc Action filters I previously used TempData to store an instance of my unitOfWork service like so:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.TempData[UnitOfWorkRequestKey] = UnitOfWork;
UnitOfWork.Begin();
}
then to commit the transaction I retreived it from temp data like this..
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var unitOfWork = (IUnitOfWork)filterContext.Controller.TempData[UnitOfWorkRequestKey];
try
{
if (filterContext.Exception == null)
{
unitOfWork.Complete();
}
}
finally
{
unitOfWork.Dispose();
filterContext.Controller.TempData[UnitOfWorkRequestKey] = null;
}
}
So my question is:
In the System.Web.Http Web Api Action Filter (using HttpActionContext) - is there an equivalent location to store my instance of a service, so I can retrieve the same instance when the action has executed?
In the System.Web.Http Web Api Action Filter (using HttpActionContext)
- is there an equivalent location to store my instance of a service, so I can retrieve the same instance when the action has executed?
No, there isn't. The whole point of an API is that it should be stateless. That's rule number 1. If you need to use Session or TempData in an API you are probably doing something very wrong from a design perspective.
Also you shouldn't be using TempData in your MVC application for this task. TempData is used when you need to persist information between more than one request. In your case it is the same request. So you should have used the HttpContext to store this information:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Items[UnitOfWorkRequestKey] = UnitOfWork;
}
and then:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var unitOfWork = (IUnitOfWork) filterContext.HttpContext.Items[UnitOfWorkRequestKey];
try
{
if (filterContext.Exception == null)
{
unitOfWork.Complete();
}
}
finally
{
unitOfWork.Dispose();
filterContext.Controller.TempData[UnitOfWorkRequestKey] = null;
}
}
Alright, now that we have fixed your MVC application here's how to achieve the same in the Web API using the Request.Properties collection:
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Request.Properties[UnitOfWorkRequestKey] = UnitOfWork;
}
and then:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var unitOfWork = (IUnitOfWork) actionExecutedContext.Request.Properties[UnitOfWorkRequestKey];
try
{
if (actionExecutedContext.Exception == null)
{
unitOfWork.Complete();
}
}
finally
{
unitOfWork.Dispose();
}
}
We have an MVC project that constructs the NHibernate dependecies via StructureMap like this
var sessionFactory = ConnectionRegistry.CreateSessionFactory<NHibernate.Context.WebSessionContext>();
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<INHibernateSessionManager>().Singleton().Use<NHibernateWebSessionManager>();
The ConnectionRegistry.CreateSessionFactory looks like this
public static ISessionFactory CreateSessionFactory<T>() where T : ICurrentSessionContext
{
if (_sessionFactory == null)
{
lock (_SyncLock)
{
if (_sessionFactory == null)
{
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(DataFactory.ConnectionString))
.CurrentSessionContext<T>()
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<IInstanceFactory>())
.ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"))
.ExposeConfiguration(c => c.SetProperty("sql_exception_converter", typeof(SqlServerExceptionConverter).AssemblyQualifiedName));
try
{
_sessionFactory = cfg.BuildSessionFactory();
}
catch (Exception ex)
{
Debug.Write("Error loading Fluent Mappings: " + ex);
throw;
}
}
}
}
return _sessionFactory;
}
NHibernateWebSessionManager looks like this
public ISession Session
{
get
{
return OpenSession();
}
}
public ISession OpenSession()
{
if(CurrentSessionContext.HasBind(SessionFactory))
_currentSession = SessionFactory.GetCurrentSession();
else
{
_currentSession = SessionFactory.OpenSession();
CurrentSessionContext.Bind(_currentSession);
}
return _currentSession;
}
public void CloseSession()
{
if (_currentSession == null) return;
if (!CurrentSessionContext.HasBind(SessionFactory)) return;
_currentSession = CurrentSessionContext.Unbind(SessionFactory);
_currentSession.Dispose();
_currentSession = null;
}
In Application_EndRequest, we do this
ObjectFactory.GetInstance<INHibernateSessionManager>().CloseSession();
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
Our controllers are persistence agnostic and actions call out to query model providers or command processors which have the sessionManager injected and manage their own transactions.
For example:
public ActionResult EditDetails(SiteDetailsEditViewModel model)
{
_commandProcessor.Process(new SiteEditCommand { //mappings }
//redirect
}
In the CommandProcessor:
public void Process(SiteEditCommand command)
{
using (var tran = _session.BeginTransaction())
{
var site = _session.Get<DeliveryPoint>(command.Id);
site.SiteName = command.Name;
//more mappings
tran.Commit();
}
}
We also have an ActionFilter attribute that logs access to each controller action.
public void OnActionExecuted(ActionExecutedContext filterContext)
{
SessionLogger.LogUserActionSummary(session, _userActionType);
}
The SessionLogger also manages its own transactions from an injected SessionManager
public void LogUserActionSummary(int sessionId, string userActionTypeDescription)
{
using (var tx = _session.BeginTransaction())
{
//get activity summary
_session.Save(userActivitySummary);
tx.Commit();
}
}
All of this works fine until I have two browsers accessing the app.
In this scenario intermittent errors are thrown because the (NHibernate) Session is closed.
NHProfiler shows SQL statements created from both CommandProcessor methods and SessionLogger methods
from both browser sessions within the same transaction.
How can this occur given the WebSessionContext scope?
I've also tried setting the scope of the sessionManager to HybridHttpOrThreadLocalScoped via structureMap.
The problem is combination of a singleton:
For<INHibernateSessionManager>().Singleton().Use<NHibernateWebSessionManager>();
Having reference to an object from a different scope (webrequest context)
_currentSession = SessionFactory.GetCurrentSession();
This canot work properly in multithread environment (as mentioned in cases of two concurrent browsers accessing it). First request could already force to set the field _currentSession, which is then (for a while) used even for the second one. The first Application_EndRequest will close it ... and lasting one will recreate it...
When relying on NHibernate scopes, follow it fully:
return SessionFactory.GetCurrentSession(); // inside is the scope handled
SessionManager.Open()
public ISession OpenSession()
{
if(CurrentSessionContext.HasBind(SessionFactory))
{
return SessionFactory.GetCurrentSession();
}
// else
var session = SessionFactory.OpenSession();
NHibernate.Context.CurrentSessionContext.Bind(session);
return session;
}
Then even singleton returning correct instances should work. But for a SessionManager I would use HybridHttpOrThreadLocalScoped anyway.
For<INHibernateSessionManager>()
.HybridHttpOrThreadLocalScoped()
.Use<NHibernateWebSessionManager>();
I'm currently using the ASP.NET Web Api along with NHibernate & Autofac...I'm having an issue where my update is not being committed to the database. I'm using an ActionFilterAttribute to open and close a transcation every time an action is performed like so:
private ISessionFactory SessionFactory { get; set; }
public TransactionAttribute()
{
SessionFactory = WebApiApplication.SessionFactory;
}
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var transcation = session.Transaction;
if (transcation != null && transcation.IsActive)
{
transcation.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
Which works fine for the add, read, and delete functions in my repository. Unfortunately, my update doesn't seem to be working (though I've tried it several ways):
public bool Update(Client client)
{
var result = Get(client.ClientID);
if (result == null)
{
return false;
}
result.Name = client.Name;
result.Acronym = client.Acronym;
result.Website = client.Website;
return true;
}
From what I've read if modifying an object during a transaction, there's no need to manually call Update or SaveOrUpdate, as this is tracked by NHibernate & performed when the transaction is committed.
Why would my update function not be working properly?
Thanks!
I figured this out on my own--basically I was creating 2 sessions (one in my TranscationAttribute and one due to DI into my repository). The reason it was creating two is pretty explicit in the OnActionExecuting statement...I'm not checking to see whether any session factory is currently bound to the CurrentSessionContext. This is the new code I'm using:
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!CurrentSessionContext.HasBind(SessionFactory))
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
}
var session = SessionFactory.GetCurrentSession();
session.BeginTransaction();
}
The issue I had after implementing this code is that the logic is not sound..when the session is DI'ed into my repository ISession, it was not being automatically bound. My solution was to bind it in the constructor of my repository like so:
public ClientRepository(ISession session)
{
if (session == null) throw new ArgumentNullException("nhSession");
this._session = session;
CurrentSessionContext.Bind(_session);
}
But I'm not 100% sure that this is going to be safe with many simultaneous HTTP requests...going to bring this question over to code review unless someone has an answer for me here!
Thanks!