F.P
F.P

Reputation: 17831

NHibernate no session or session was closed

The error

Initializing[Core.Model.Account#2586]-failed to lazily initialize a collection of role: Core.Model.Account.Roles, no session or session was closed

appears when I try to login in my web app using NHibernate. What I don't understand is how the connection can be closed, because when I debug into the Method calling it, I can the isOpen property of the used NHibernate session is opened.

But as soon as the Lazy Loading kicks in, apparently NHibernate thinks the connection was closed. How can this happen?

I start my NHibernate session in the BeginRequest and close them in EndRequest, as is recommended.

Account.cs (in project/namespace/assembly Core)

public class Account
{
    // ...

    public virtual ISet<Role> Roles
    {
        get;
        set;
    }

    // ...

    public virtual bool HasPermission(Permission permission_)
    {
        foreach (Role role in Roles) {
            if (role.Permissions.Contains(Permission.ALL) || role.Permissions.Contains(permission_)) {
                return true;
            }
        }
        return false;
    }
}

Account.hbm.xml (in project/namespace/assembly Core)

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
    <class name="Core.Model.Account,Core" table="accounts">
        <!-- ... -->
        <set name="Roles" table="account_roles" lazy="true">
            <key column="account"/>
            <many-to-many class="Core.Model.Role" column="role" />
        </set>
    </class>
</hibernate-mapping>

Global.asax (in project/namespace/assembly Web)

public AccountRepository Accounts;
private static ISessionFactory _sessionFactory;
private void Application_Start(object sender_, EventArgs e_)
{
    _sessionFactory = new Configuration().Configure().BuildSessionFactory();
}


private void Application_BeginRequest(object sender_, EventArgs e_)
{
    ISession session = _sessionFactory.OpenSession();
    Accounts.SetSession(session);
    HttpContext.Current.Items.Add("HibernateSession", session);
}

private void Application_EndRequest(object sender_, EventArgs e_)
{
    ISession session = HttpContext.Current.Items["HibernateSession"] as ISession;
    if (session != null && session.IsOpen) {
        session.Close();
        HttpContext.Current.Items.Remove("HibernateSession");
    }
}

DecoratedSession.cs (in project/namespace/assembly Web)

private static HttpSessionState _session
{
    get
    {
        return HttpContext.Current.Session;
    }
}

public static T Get<T>(string key_)
{
    if (_session == null || _session[key_] == null) {
        return default(T);
    }
    return (T)_session[key_];
}

public static void Set<T>(string key_, T value_)
{
    _session[key_] = value_;
}

public static Account Account
{
    get
    {
        return Get<Account>("Account");
    }
    set
    {
        Set<Account>("Account", value);
    }
}

login.aspx (in project/namespace/assembly Web)

protected void loginButton_Click(object sender, System.EventArgs e)
{
    DecoratedSession.Account = Global.Firewall.Login(tb_user.Text, tb_user.Text);
    Response.Redirect("main.aspx", true);
}

main.aspx (in project/namespace/assembly Web)

protected void Page_Load(object sender, System.EventArgs e)
{
    // Error thrown here, when accessing the "roles" set
    if (DecoratedSession.Account.HasPermission(Permission.FULL_ACCESS))
    {
        // ...
    }
}

I can even see in the logfiles that the session is openend, then the exception occurs, and then it is closed.

Global: 15:20:08,669 DEBUG Global - Opened session [529000] for request to [/main.aspx]
Global: 15:20:08,670 DEBUG Global - STARTREQUEST Session id: [529000]
main: 15:20:08,682 DEBUG main - MAIN Session id: [529000]
NHibernate.LazyInitializationException: 15:20:08,685 ERROR NHibernate.LazyInitializationException - Initializing[Core.Model.Account#2586]-failed to lazily initialize a collection of role: Core.Model.Account.Roles, no session or session was closed
NHibernate.LazyInitializationException: Initializing[Core.Model.Account#2586]-failed to lazily initialize a collection of role: Core.Model.Account.Roles, no session or session was closed
Global: 15:20:14,160 DEBUG Global - Closed session [529000] for request to [/main.aspx]

How can I debug this?

Upvotes: 2

Views: 3585

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

The point here is (I did not dive into the code above, but based no my experience) related to static vs http request life cycles.

While there is open session, the object blaming for its session being closed comes from some previous (related to another web request)

Check some details here:

Solution I see in introducing some kind of Prototype pattern:

[Serializable]
public class Account, ICloneable, ...
{
    ...
    public override object Clone()
    {
        var entity = base.Clone() as Account;
        entity.Roles = ... (clone roles)
        ...
        return entity;
    }
    ...

And when loading Account (user, role) for Authentications, we should call Clone() to be sure that all important properties (many-to-one, one-to-many) were loaded and are later ready for use. That clone could be cached in static context

Upvotes: 3

Related Questions