I am using Rhino Security as my security layer on top of NHibernate in an ASP.NET MVC 3 application. When I attempt to delete a secure entity Rhino security fires off an OnPreDelete method used to clean up corresponding data in the database. At this point I receive an Illegally attempted to associate a proxy with two open Sessions error.
2012-01-02 21:47:52,176 [9] ERROR NHibernate.Event.Default.AbstractFlushingEventListener [(null)] - Could not synchronize database state with session
NHibernate.LazyInitializationException: Initializing[Rhino.Security.Model.EntityType#8007cc24-9cdd-447c-a9cd-9fcc015fa95c]-Illegally attempted to associate a proxy with two open Sessions
at NHibernate.Proxy.AbstractLazyInitializer.set_Session(ISessionImplementor value)
at NHibernate.Engine.StatefulPersistenceContext.ReassociateProxy(ILazyInitializer li, INHibernateProxy proxy)
at NHibernate.Engine.StatefulPersistenceContext.ReassociateIfUninitializedProxy(Object value)
at NHibernate.Event.Default.ProxyVisitor.ProcessEntity(Object value, EntityType entityType)
at NHibernate.Event.Default.AbstractVisitor.ProcessValue(Object value, IType type)
at NHibernate.Event.Default.AbstractVisitor.ProcessValue(Int32 i, Object[] values, IType[] types)
at NHibernate.Event.Default.AbstractVisitor.ProcessEntityPropertyValues(Object[] values, IType[] types)
at NHibernate.Event.Default.AbstractVisitor.Process(Object obj, IEntityPersister persister)
at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event, ISet transientEntities)
at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event)
at NHibernate.Impl.SessionImpl.FireDelete(DeleteEvent event)
at NHibernate.Impl.SessionImpl.Delete(Object obj)
at Rhino.Security.DeleteEntityEventListener.OnPreDelete(PreDeleteEvent deleteEvent) in C:\Users\jirwin\Downloads\rhino\rhino-security\Rhino.Security\DeleteEntityEventListener.cs:line 43
at NHibernate.Action.EntityDeleteAction.PreDelete()
at NHibernate.Action.EntityDeleteAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
From my reading on the subject this error is usually caused by bad session management, but from the source code it appears that Rhino Security is using the existing session (note, the Delete method call below is the offending method):
ISession childSession = deleteEvent.Session.GetSession(EntityMode.Poco);
// because default flush mode is auto, a read after a scheduled delete will invoke
// the auto-flush behaviour, causing a constraint violation exception in the
// underlying database, because there still are EntityGroup entities that need
// the deleted EntityReference/SecurityKey.
childSession.FlushMode = FlushMode.Commit;
childSession.Delete(entityReference);
My session management is also pretty straightforward using an MVC action filter attribute to open and commit transactions as follows:
public class NHibernateActionFilter : ActionFilterAttribute
{
private static readonly ISessionFactory sessionFactory = BuildSessionFactory();
private static ISessionFactory BuildSessionFactory()
{
return new Configuration()
.Configure()
.BuildSessionFactory();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var sessionController = filterContext.Controller as SessionController;
if (sessionController == null)
return;
sessionController.Session = sessionFactory.OpenSession();
sessionController.Session.BeginTransaction();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var sessionController = filterContext.Controller as SessionController;
if (sessionController == null)
return;
using (var session = sessionController.Session)
{
if (session == null)
return;
if (!session.Transaction.IsActive)
return;
if (filterContext.Exception != null)
session.Transaction.Rollback();
else
session.Transaction.Commit();
}
}
}
Can anyone provide guidance as to why this issue is occurring?
Thanks in advance
I still do not understand the root cause of the issue, but found a workaround. Previously my delete method loaded a proxy and then executed the delete
var entity = session.Load<T>(21415);
session.Delete(entity);
Replacing the above code with the following resolved the issue:
var queryString = string.Format("delete {0} where id = :id", typeof(T));
Session.CreateQuery(queryString)
.SetParameter("id", id)
.ExecuteUpdate();
Obviously the latter avoids the creation of a proxy and performs a straight delete