Reputation: 5606
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
Upvotes: 4
Views: 3233
Reputation: 5606
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
Upvotes: 2
Reputation: 1717
Having a similar problem, and whether this will provide an answer for you I'm not sure, but from what I have found online, it's down to loading and updating (or deleting in your case) and object with an uninitialised property within the same session. So if the object being loaded has a property set as LazyLoad() that is never called, it won't be initialised and so the session that was used to get the object will be readonly and will stay alive waiting to populate the lazy loaded property. NHibernate will then try to reuse the session to update/delete the object and the exception will be thrown.
That's my take on this link (refers to Java, but describes the problem I'm seeing in .Net) - http://blog.essaytagger.com/2011/11/avoiding-session-conflicts-on-save-due.html - though I'm not sure if I agree with the fix to it.
Hope this helps in some way.
Upvotes: 1