Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

NHibernate with Blazor Server #3231

Unanswered
mxmissile asked this question in General
Discussion options

Anyone use NHibernate with Blazor Server? If so, how are you handling ISession and razor component lifetimes?

You must be logged in to vote

Replies: 1 comment

Comment options

I found your post awhile ago when struggling through using ISession in a Blazor Server app. I still haven't found anyone talking about it online, so I'll post my solution here. I had to implement my own session management in a .razor component, so it's hard to explain in a few sentences. Please forgive the length of this post.

An ISession is an inexpensive, non-threadsafe object that should be used once, for a single business process, and then discarded.
https://nhibernate.info/doc/nhibernate-reference/transactions.html

Blazor injects scoped services, which live for the lifetime of the SPA (until reload). In our experience, injecting an ISession into each page is not feasible, because Blazor is asynchronous. This means that it will often run methods that both utilize the same ISession to call the database in parallel, which will result in a concurrent session error.

A solution to this would be to inject the ISessionFactory instead and wrap each transaction in an OpenSession().

using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction()) {
...

Since we wrap the session in a using block, the session will get closed and disposed at the end of that unit of work.

NHibernate uses LazyLoading by default, which is great for performance. You can't lazy load an object if the session is already closed. This means that we cannot lazy-load anything in Blazor, because we dispose of the session before the UI renders. To use this method, we must use a lot of Fetch statements in our query, and potentially turn off LazyLoading to offset the amount of required Fetch statements. This is not ideal.

A solution to this problem would be to not dispose the session; rather we leave it open so that when Blazor asynchronously updates the UI, the session is still open so that the object in question may be LazyLoaded on demand.

var session = sessionFactory.OpenSession();
using (var transaction = session.BeginTransaction()) {

There is an obvious issue with this: each time the routine encapsulating a database query is called, it opens a new session which will never be disposed. LazyLoading will work, however.

The solution to this issue is to implement our own session management. Track each open session in a List<ISession> and implement the IDisposable interface on each Blazor page so that when the user navigates to another page, it will close and delete all of the sessions. Each session needs to be open while any updates render on the page, but it can be closed after the render.

@using NHibernate
@implements IDisposable
@code {
 public NHibernate.ISessionFactory sessionFactory { get; set; }
 public List<NHibernate.ISession> sessions { get; set; }
 
 /// <summary>
 /// Get the sessionFactory
 /// </summary>
 protected override void OnInitialized()
 
 /// <summary>
 /// Dispose all sessions with closed transactions after rendering
 /// This allows sessions to remain open long enough to lazy-load during rendering
 /// </summary>
 /// <param name="firstRender"></param>
 protected override void OnAfterRender(bool firstRender)
 
 /// <summary>
 /// Open a new session and maintain an internal handle to it
 /// </summary>
 /// <param name="protect">If true, the session will not be automatically discarded.</param>
 /// <returns>An open NHibernate ISession</returns>
 public NHibernate.ISession GetSession(bool protect = false)
 
 /// <summary>
 /// Dispose of all remaining sessions when navigating to another page
 /// </summary>
 public void Dispose()
}

Every page in my Blazor application that needs to do a database transaction inherits from the above component. I can share my implementation of the stub above if anyone is interested. Here is a simple example of how to use it.

@inherits NhibernateBase
...
var session = GetSession();
using (var transaction = session.BeginTransaction())
{
 MyList = await session.Query<MyClass>().ToListAsync();
 await transaction.CommitAsync();
}
You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /