景色异常后NHibernate的延迟加载异常 [英] Nhibernate Lazy Load exception after a view exception

查看:358
本文介绍了景色异常后NHibernate的延迟加载异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我得到一个怪异的行为与NHibernate与流利的配置。

每当一个通用的异常无关的NHibernate的发生,即视图中的 DivideByZeroException 的异常后每个请求抛出。

 类型的异常NHibernate.LazyInitializationException发生在NHibernate.dll但在用户code没有处理。其他信息:初始化[实体] -Could无法初始化代理 - 没有会话。

由于错误性质的错误是至关重要的,由于这一事实,即1用户可以使整个网站死了,如果他产生异常

以下是我的HttpModule为NHibernate的与Asp.Net MVC 5的会议需要照顾。

NHibernateSessionPerRequest.cs

 公共类NHibernateSessionPerRequest:IHttpModule的
{
    私人静态只读ISessionFactory SessionFactory的;    //构造我们的HTTP模块
    静态NHibernateSessionPerRequest()
    {
        SessionFactory的= CreateSessionFactory();
    }    //初始化HTTP模块
    公共无效初始化(HttpApplication的情况下)
    {
        context.BeginRequest + =的BeginRequest;
        context.EndRequest + = EndRequest;
    }    //部署HTTP模块
    公共无效的Dispose(){}    //返回当前会话
    公共静态的ISession的getCurrentSession()
    {
        返回SessionFactory.getCurrentSession()函数;
    }    //打开会话,开始交易,并绑定该会话
    私有静态无效的BeginRequest(对象发件人,EventArgs的发送)
    {
        ISession的会话= SessionFactory.OpenSession();        session.BeginTransaction();        CurrentSessionContext.Bind(会话);
    }    //取消绑定会议上,提交事务,并关闭会话
    私有静态无效EndRequest(对象发件人,EventArgs的发送)
    {
        ISession的会话= CurrentSessionContext.Unbind(SessionFactory的);        如果(会话== NULL)回报;        尝试
        {
            session.Transaction.Commit();
        }
        赶上(例外)
        {
            session.Transaction.Rollback();
            扔;
        }
        最后
        {
            session.Close();
            session.Dispose();
        }
    }    //返回我们的会话工厂
    私有静态ISessionFactory CreateSessionFactory()
    {
        如果(HttpContext.Current!= NULL)//为Web应用程序
            _configFile = HttpContext.Current.Server.MapPath(
                            的String.Format(〜/ App_Data文件/ {0}求CacheFile)
                            );        _configuration = LoadConfigurationFromFile();
        如果(_configuration == NULL)
        {
            FluentlyConfigure();
            SaveConfigurationToFile(_configuration);
        }
        如果(_configuration!= NULL)回报_configuration.BuildSessionFactory();
        返回null;
    }    //返回我们的数据库配置
    私有静态MsSqlConfiguration CreateDbConfigDebug2()
    {
        返回MsSqlConfiguration
            .MsSql2008
            .ConnectionString(C => c.FromConnectionStringWithKey(MyConnection的));
    }    //更新数据库架构,如果有对模型进行任何更改,
    //或下降,并创建它,如果它不存在
    私有静态无效UpdateSchema(配置CFG)
    {
        新SchemaUpdate工具(CFG)
            .Execute(假,真);
    }
    私有静态无效SaveConfigurationToFile(配置配置)
    {
        使用(var文件= File.Open(_configFile,FileMode.Create))
        {
            变种BF =新的BinaryFormatter();
            bf.Serialize(文件,配置);
        }
    }    私有静态配置LoadConfigurationFromFile()
    {
        如果(IsConfigurationFileValid == FALSE)
            返回null;
        尝试
        {
            使用(var文件= File.Open(_configFile,FileMode.Open))
            {
                变种BF =新的BinaryFormatter();
                返回bf.Deserialize(文件)作为配置;
            }
        }
        赶上(例外)
        {
            返回null;
        }    }
    私有静态无效FluentlyConfigure()
    {
        如果(_configuration == NULL)
        {
            _configuration = Fluently.Configure()
            .Database(CreateDbConfigDebug2)
            .CurrentSessionContext< WebSessionContext>()
            .Cache(C => c.ProviderClass< SysCacheProvider>()UseQueryCache()。)
            .Mappings(M = GT; m.FluentMappings.AddFromAssemblyOf< EntityMap>()
                .Conventions.Add(DefaultCascade.All(),DefaultLazy.Always()))
            .ExposeConfiguration(UpdateSchema)
            .ExposeConfiguration(C => c.Properties.Add(cache.use_second_level_cache,真))
            .BuildConfiguration();
        }
    }
    私人静态布尔IsConfigurationFileValid
    {
        得到
        {
            VAR屁股= Assembly.GetAssembly(typeof运算(EntityMap));
            VAR configInfo =新的FileInfo(_configFile);
            VAR assInfo =新的FileInfo(ass.Location);
            返回configInfo.LastWriteTime> = assInfo.LastWriteTime;
        }
    }    私有静态配置_configuration;
    私人静态字符串_configFile;
    私人常量字符串求CacheFile =hibernate.cfg.xml中;}

修改

该仓库实现我使用

 公共类资源库< T> :IIntKeyedRepository< T>其中T:类
{
    私人只读的Isession _session;    公共库()
    {
        _session = NHibernateSessionPerRequest.GetCurrentSession();
    }    #地区IRepository< T>会员    公共BOOL添加(T实体)
    {
        _session.Save(实体);
        返回true;
    }    公共BOOL添加(System.Collections.Generic.IEnumerable< T>的项目)
    {
        的foreach(在项目牛逼项)
        {
            _session.Save(项目);
        }
        返回true;
    }    公共BOOL更新(T实体)
    {
        _session.Update(实体);
        返回true;
    }    公共BOOL删除(T实体)
    {
        _session.Delete(实体);
        返回true;
    }    公共BOOL删除(System.Collections.Generic.IEnumerable< T>实体)
    {
        的foreach(在实体T实体)
        {
            _session.Delete(实体);
        }
        返回true;
    }    #endregion    #地区IIntKeyedRepository< T>会员    公共ŧFindBy(INT ID)
    {
        返回_session.Get< T>(ID);
    }    #endregion    #地区IReadOnlyRepository< T>会员    公众的IQueryable< T>所有()
    {
        返回_session.Query< T>();
    }    公共ŧFindBy(System.Linq.Ex pressions.Ex pression< System.Func< T,BOOL>>前pression)
    {
        返回FilterBy(如pression)。单();
    }    公众的IQueryable< T> FilterBy(System.Linq.Ex pressions.Ex pression< System.Func< T,BOOL>>前pression)
    {
        返回所有(),其中(EX pression).AsQueryable()。
    }    #endregion}

编辑2

我使用的基础类

 公共类BaseController:控制器
{
    私人只读IRepository< UserEntity> _userRepository;    公共BaseController()
    {
        _userRepository =新库< UserEntity>();
        BaseModel =新LayoutModel {情态动词=新的List<串GT;()};
    }    公共UserEntity LoggedUser {搞定;组; }
    公共LayoutModel BaseModel {搞定;组; }    保护覆盖无效OnActionExecuting(ActionExecutingContext CTX)
    {
        base.OnActionExecuting(CTX);        如果(HttpContext.User.Identity.IsAuthenticated)
        {
            如果(会话= NULL&放大器;!&安培;!会话[用户] = NULL)
            {
                LoggedUser =(用户)会话[用户];
            }
            VAR curUsername = HttpContext.User.Identity.Name;
            如果(LoggedUser == NULL || LoggedUser.Entity2.un!= curUsername)
            {
                LoggedUser = _userRepository.FindBy(U => u.Entity2.un == curUsername);
                会话[用户] = LoggedUser;
            }
            BaseModel.LoggedUser = LoggedUser;
            BaseModel.Authenticated = TRUE;
        }
        其他
        {
            LoggedUser =新UserEntity
            {
                实体=新实体{UN =客人},
            };
            BaseModel.LoggedUser = LoggedUser;
        }
    }
}


解决方案

扩展的问题,所有的片段 - 终于帮助找出是问题

有确实是一个大问题: 会话[用户] = LoggedUser;

这将很难工作。为什么?


  • 因为我们置于长时间运行的对象(网络会议)

  • 通过很快持久的Web请求加载一个实例

并非所有的属性将/可以加载,当我们把LoggedUser到会话。这可能只是与许多代理重新presenting引用和集合根实体。这些将永远不会被加载后,由于其马瑟会话关闭了......

解决方案?

我会使用 .Clone()的用户对象。在其实施,我们可以明确地加载所有需要的引用和集合并克隆它们。这样的对象可以被放置到Web会话

  [Serializable接口]
公共类用户,ICloneable,...
{
    ...
    公众覆盖对象的clone()
    {
        VAR实体= base.Clone()作为用户;
        entity.Role = Role.Clone()作为角色;
        ...
        返回实体;
    }

那么,什么将被放置到会话?

 会话[用户] = LoggedUser.Clone();

I get a weird behavior with NHibernate with Fluent Configuration.

Whenever a generic exception unrelated to the NHibernate occurs i.e. in the view a DivideByZeroException every request after the exception throws.

An exception of type 'NHibernate.LazyInitializationException' occurred in NHibernate.dll but was not handled in user code. Additional information: Initializing[Entity]-Could not initialize proxy - no Session.

Due to nature of the bug the bug is critical due to the fact that 1 user can make the whole website dead if he generates an exception

Following it is my HttpModule for Nhibernate with Asp.Net MVC 5 that takes care of sessions.

NHibernateSessionPerRequest.cs

public class NHibernateSessionPerRequest : IHttpModule
{
    private static readonly ISessionFactory SessionFactory;

    // Constructs our HTTP module
    static NHibernateSessionPerRequest()
    {
        SessionFactory = CreateSessionFactory();
    }

    // Initializes the HTTP module
    public void Init(HttpApplication context)
    {
        context.BeginRequest += BeginRequest;
        context.EndRequest += EndRequest;
    }

    // Disposes the HTTP module
    public void Dispose() { }

    // Returns the current session
    public static ISession GetCurrentSession()
    {
        return SessionFactory.GetCurrentSession();
    }

    // Opens the session, begins the transaction, and binds the session
    private static void BeginRequest(object sender, EventArgs e)
    {
        ISession session = SessionFactory.OpenSession();

        session.BeginTransaction();

        CurrentSessionContext.Bind(session);
    }

    // Unbinds the session, commits the transaction, and closes the session
    private static void EndRequest(object sender, EventArgs e)
    {
        ISession session = CurrentSessionContext.Unbind(SessionFactory);

        if (session == null) return;

        try
        {
            session.Transaction.Commit();
        }
        catch (Exception)
        {
            session.Transaction.Rollback();
            throw;
        }
        finally
        {
            session.Close();
            session.Dispose();
        }
    }

    // Returns our session factory
    private static ISessionFactory CreateSessionFactory()
    {
        if (HttpContext.Current != null) //for the web apps
            _configFile = HttpContext.Current.Server.MapPath(
                            string.Format("~/App_Data/{0}", CacheFile)
                            );

        _configuration = LoadConfigurationFromFile();
        if (_configuration == null)
        {
            FluentlyConfigure();
            SaveConfigurationToFile(_configuration);
        }
        if (_configuration != null) return _configuration.BuildSessionFactory();
        return null;
    }



    // Returns our database configuration
    private static MsSqlConfiguration CreateDbConfigDebug2()
    {
        return MsSqlConfiguration
            .MsSql2008
            .ConnectionString(c => c.FromConnectionStringWithKey("MyConnection"));
    }

    // Updates the database schema if there are any changes to the model,
    // or drops and creates it if it doesn't exist
    private static void UpdateSchema(Configuration cfg)
    {
        new SchemaUpdate(cfg)
            .Execute(false, true);
    }
    private static void SaveConfigurationToFile(Configuration configuration)
    {
        using (var file = File.Open(_configFile, FileMode.Create))
        {
            var bf = new BinaryFormatter();
            bf.Serialize(file, configuration);
        }
    }

    private static Configuration LoadConfigurationFromFile()
    {
        if (IsConfigurationFileValid == false)
            return null;
        try
        {
            using (var file = File.Open(_configFile, FileMode.Open))
            {
                var bf = new BinaryFormatter();
                return bf.Deserialize(file) as Configuration;
            }
        }
        catch (Exception)
        {
            return null;
        }

    }
    private static void FluentlyConfigure()
    {
        if (_configuration == null)
        {
            _configuration = Fluently.Configure()
            .Database(CreateDbConfigDebug2)
            .CurrentSessionContext<WebSessionContext>()
            .Cache(c => c.ProviderClass<SysCacheProvider>().UseQueryCache())
            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<EntityMap>()
                .Conventions.Add(DefaultCascade.All(), DefaultLazy.Always()))
            .ExposeConfiguration(UpdateSchema)
            .ExposeConfiguration(c => c.Properties.Add("cache.use_second_level_cache", "true"))
            .BuildConfiguration();
        }
    }
    private static bool IsConfigurationFileValid
    {
        get
        {
            var ass = Assembly.GetAssembly(typeof(EntityMap));
            var configInfo = new FileInfo(_configFile);
            var assInfo = new FileInfo(ass.Location);
            return configInfo.LastWriteTime >= assInfo.LastWriteTime;
        }
    }

    private static Configuration _configuration;
    private static string _configFile;
    private const string CacheFile = "hibernate.cfg.xml";

}

Edit

The Repository Implementation i use

public class Repository<T> : IIntKeyedRepository<T> where T : class
{
    private readonly ISession _session;

    public Repository()
    {
        _session = NHibernateSessionPerRequest.GetCurrentSession();
    }

    #region IRepository<T> Members

    public bool Add(T entity)
    {
        _session.Save(entity);
        return true;
    }

    public bool Add(System.Collections.Generic.IEnumerable<T> items)
    {
        foreach (T item in items)
        {
            _session.Save(item);
        }
        return true;
    }

    public bool Update(T entity)
    {
        _session.Update(entity);
        return true;
    }

    public bool Delete(T entity)
    {
        _session.Delete(entity);
        return true;
    }

    public bool Delete(System.Collections.Generic.IEnumerable<T> entities)
    {
        foreach (T entity in entities)
        {
            _session.Delete(entity);
        }
        return true;
    }

    #endregion

    #region IIntKeyedRepository<T> Members

    public T FindBy(int id)
    {
        return _session.Get<T>(id);
    }

    #endregion

    #region IReadOnlyRepository<T> Members

    public IQueryable<T> All()
    {
        return _session.Query<T>();
    }

    public T FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
    {
        return FilterBy(expression).Single();
    }

    public IQueryable<T> FilterBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
    {
        return All().Where(expression).AsQueryable();
    }

    #endregion

}

Edit 2

The base controller class I use

public class BaseController : Controller
{
    private readonly IRepository<UserEntity> _userRepository;

    public BaseController()
    {
        _userRepository = new Repository<UserEntity>();
        BaseModel = new LayoutModel {Modals = new List<string>()};
    }

    public UserEntity LoggedUser { get; set; }
    public LayoutModel BaseModel { get; set; }

    protected override void OnActionExecuting(ActionExecutingContext ctx)
    {
        base.OnActionExecuting(ctx);

        if (HttpContext.User.Identity.IsAuthenticated)
        {
            if (Session != null && Session["User"] != null)
            {
                LoggedUser = (User) Session["User"];
            }
            var curUsername = HttpContext.User.Identity.Name;
            if (LoggedUser == null || LoggedUser.Entity2.un!= curUsername)
            {
                LoggedUser = _userRepository.FindBy(u => u.Entity2.un== curUsername);
                Session["User"] = LoggedUser;
            }
            BaseModel.LoggedUser = LoggedUser;
            BaseModel.Authenticated = true;
        }
        else
        {
            LoggedUser = new UserEntity
            {
                Entity= new Entity{un= "Guest"},
            };
            BaseModel.LoggedUser = LoggedUser;
        }
    }      
}

解决方案

The extended question and all the snippets - are finally helping to find out where is the issue.

There is a really big issue: Session["User"] = LoggedUser;

This would hardly work. Why?

  • because we place into long running object (Web Session)
  • an instance loaded via very shortly lasting Web Request

Not all its properties will/could be loaded, When we place LoggedUser into session. It could be just a root entity with many proxies representing references and collections. These will NEVER be loaded later, because its Mather session is closed... gone

Solution?

I would use .Clone() of the User object. In its implementation we can explicitly load all needed references and collections and clone them as well. Such object could be placed into the Web Session

[Serializable]
public class User, ICloneable, ...
{
    ...
    public override object Clone()
    {
        var entity = base.Clone() as User;
        entity.Role = Role.Clone() as Role;
        ...
        return entity;
    }

So, what would be placed into session?

Session["User"] = LoggedUser.Clone();

这篇关于景色异常后NHibernate的延迟加载异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆