NHibernate与Rhino Security - 非法尝试将代理与两个打开的Sessions关联

时间:2012-01-03 03:25:43

标签: nhibernate rhino-security

我在ASP.NET MVC 3应用程序中使用Rhino Security作为我的安全层在NHibernate之上。当我尝试删除安全实体时,Rhino安全性会触发用于清理数据库中相应数据的OnPreDelete方法。此时我收到非法尝试将代理与两个打开的Sessions 错误相关联。

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)

从我对该主题的阅读中,这个错误通常是由错误的会话管理引起的,但是从源代码看来,Rhino Security正在使用现有会话(注意,下面的Delete方法调用是违规方法):

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);

我的会话管理也非常简单,使用MVC操作过滤器属性来打开和提交事务,如下所示:

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();
        }
    }
}

有人可以提供有关此问题发生原因的指导吗?

提前致谢

2 个答案:

答案 0 :(得分:2)

我仍然不明白问题的根本原因,但找到了解决方法。以前我的删除方法加载了一个代理,然后执行了删除

var entity = session.Load<T>(21415);
session.Delete(entity);

使用以下代码替换上述代码解决了问题:

 var queryString = string.Format("delete {0} where id = :id", typeof(T));
            Session.CreateQuery(queryString)
                   .SetParameter("id", id)
                   .ExecuteUpdate();

显然,后者避免创建代理并执行直接删除

答案 1 :(得分:1)

有一个类似的问题,以及这是否会为你提供答案我不确定,但从我在网上找到的,它是由于未初始化的属性加载和更新(或删除你的情况)和对象在同一个会话中。因此,如果正在加载的对象具有设置为从未调用的LazyLoad()的属性,则它将不会被初始化,因此用于获取对象的会话将是只读的并且将保持活动状态等待填充延迟加载的属性。然后NHibernate将尝试重用会话来更新/删除对象,并抛出异常。

这是我对这个链接的看法(指Java,但描述了我在.Net中看到的问题) - http://blog.essaytagger.com/2011/11/avoiding-session-conflicts-on-save-due.html - 虽然我不确定我是否同意修复它。

希望这在某种程度上有所帮助。