4

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

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

任何人都可以提供有关为什么会发生此问题的指导吗?

提前致谢

4

2 回答 2

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

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

于 2012-01-10T01:17:24.957 回答
1

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

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

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

于 2012-01-09T11:29:13.600 回答