I'm trying to develop a web forms application using NHibernate and the Session Per Request model. All the examples I've seen have an HTTPModule that create a session and transaction at the beging of each request and then commits the transaction and closes the session at the end of the request. I've got this working but I have some concerns.
The main concern is that objects are automatically saved to the database when the web request is finished. I'm not particularly pleased with this and would much prefer some way to take a more active approach to deciding what is actually saved when the request is finished. Is this possible with the Session Per Request approach?
Ideally I'd like for the interaction with the database to go something like this:
Retreive object from the database or create a new one
Modify it in some way
Call a save method on the object which validates that it's indeed ready to be commited to the database
Object gets saved to the database
I'm able to accomplish this if I do not use the Sessions Per Request model and wrap the interactions in a using session / using transaction blocks. The problem I ran into in taking this approach is that after the object is loaded from the database the session is closed an I am not able to utilize lazy loading. Most of the time that's okay but there are a few objects which have lists of other objects that then cannot be modified because, as stated, the session has been closed. I know I could eagerly load those objects but they don't always get used and I feel that in doing so I'm failing at utilizing NHibernate.
Is there some way to use the Session Per Request (or any other model, it seems like that one is the most common) which will allow me to utilize lazy loading AND provide me with a way to manually decide when an object is saved back to the database? Any code, tutorials, or feedback is greatly appreciated.
Yes, this is possible and you should be able to find examples of it. This is how I do it:
Use session-per-request but do not start a transaction at the start of the request.
Set ISession.FlushMode to Commit.
Use individual transactions (occasionally multiple per session) as needed.
At the end of the session, throw an exception if there's an active uncommitted transaction. If the session is dirty, flush it and log a warning.
With this approach, the session is open during the request lifetime so lazy loading works, but the transaction scope is limited as you see fit. In my opinion, using a transaction-per-request is a bad practice. Transactions should be compact and surround the data access code.
Be aware that if you use database assigned identifiers (identity columns in SQL Server), NHibernate may perform inserts outside of your transaction boundaries. And lazy loads can of course occur outside of transactions (you should use transactions for reads also).
Related
we are building a WinForms desktop application which talks to an SQL Server through NHibernate. After extensive research we settled on the Session / Form strategy using Ninject to inject a new ISession into each Form (or the backing controller to be precise). So far it is working decently.
Unfortunately the main Form holds a lot of data (mostly read-only) which gets stale after some time. To prevent this we implemented a background service (really just a seperate class) which polls the DB for changes and issues an event which lets the main form selectively update the changed rows.
This background service also gets a separate session to minimize interference with the other forms. Our understanding was that it is possible to open a transaction per session in parallel as long as they are not nested.
Sadly this doesn't seem to be the case and we either get an ObjectDisposedException in one of the forms or the service (because the service session used an existing transaction from on of the forms and committed it, which fails the commit in the form or the other way round) or we get an InvalidOperationException stating that "Parallel transactions are not supported by SQL Server".
Is there really no way to open more than one transaction in parallel (across separate sessions)?
And alternatively is there a better way to update stale data in a long running form?
Thanks in advance!
I'm pretty sure you have messed something up, and are sharing either session or connection instances in ways you did not intend.
It can depend a bit on which sort of transactions you use:
If you use only NHibernate transactions (session.BeginTransaction()), each session acts independently. Unless you do something special to insert your own underlying database connections (and made an error there), each session will have their own connection and transaction.
If you use TransactionScope from System.Transactions in addition to the NHibernate transactions, you need to be careful about thread handling and the TransactionScopeOption. Otherwise different parts of your code may unexpectedly share the same transaction if a single thread runs through both parts and you haven't used TransactionScopeOption.RequiresNew.
Perhaps you are not properly disposing your transactions (and sessions)?
I build a new application from scratch this days in a web application.
(The technologies are Asp.Net and the ORM I'm using is Entity Framework. if it matters)
I'm uncertain if the widely used pattern session per request is really a good one.
As I see it, the advantage of the pattern is that the cache isn't increases until the database session crash\ being too big and thus inefficient.
But isn't a new session for every request is too much? It means every server call reset the cache, even simple ajax request like auto-complete has a brand new cache, in fact for every key stroke the cache resets.
The chances you will query the same object-entity-row in one request is small.
Isn't Session per session is a better pattern? it has both the advantages meaning
The cache won't grow for ever.
The cache can actually be used...
So... Why is session per request is so widely used and session per session is not?
Clarifications:
When I wrote ORM session it applies both to NHibernate's session and EntityFramework's DbContext.
I do mean to flush-commit-SaveChanges of the session\dbcontext on each request.
Session per request pattern is more natural and robust for using with ORM. It has smaller chances to get dirty entities and has more predictable resource management.
If I got you right and you mean DbContext instance under Session than Session Per Session can be used only in application without data modification, otherwise you would get unexpected data submitting by a request while other request performs data modification. Also I'm not sure Entity Framework context is thread safe - while processing requests is multithread.
I not totally sure but I think Entity Framework doesn't use cache (== identity mapping) as wide as you expect. On selecting entity set it queries database even if all data are in cache - it can only avoid constructing new entities but using existing ones from identity map.
For caching there are other solutions and they are better.
For me, it all about providing consistency by constraining a unit of work to a single request. I'm not sure how a session per session would work when things go wrong.
For example, what would you do if several requests have been handled and then you get an optimistic concurrency exception on the commit? you could have several merge conflicts at that point.
So a session per request just limits your conflict exposure and makes unit of work on the request scope.
I see there are 2 possible scenarios as to the session handling:
Open one single ISession per request. Open it at request start and close it at request end.
Open one ISession per conceptual "unit of work". Many sessions are created for a request.
The approach #1 is the one I'm doing now. I'm a little bit worried about it because, although it works, it's a little bit difficult to debug. For instance, I have an object not being saved (even though I ordered it to) and I'm having trouble debugging since there's a LOT of things happening during a complete request life-cycle.
The approach #2 seems to be the standard best-practice (not sure about ASP.NET) and I'm sure it's pretty easier to debug. The problem I see is about inter-session communication. For instance: My Page class holds a reference to the User, which is a persistent object. Many of the operations receive the user as parameter. As the user belongs to a different session, I can't pass it as a parameter.
I'm biased to #2, but I don't know if it's the best practice, nor how to deal with cross-session object.
Thanks.
Most people do Session-Per-Request for the reasons you outline and for simplicity.
However, you can open and commit transactions for each "unit of work". So you will have many transactions for each session. (It is also usual practice to make sure that when the transaction is committed, the session is flushed at the same time).
For example, after clicking the save button, open and commit a transaction.
The session will take care of keeping track of all your entities. The transaction will take care of flushing to the database when necessary.
With this setup it should be easier to debug your problem.
For the ASP.NET project I'm working on now, I use a combination of these approaches.
I open an ISession at the beginning of a request and close it at the end of the request, as you do with your first approach, and I use the session to load any entities that need to remain attached to a session for the duration of the request.
However, when I need to save or update or delete an entity, I create a new transient object and hand it to a new ISession, separate from the one tied to the request. For additional units of work, I create additional sessions.
You may find NHibernate Burrow helpful, or at least interesting in this regard, as it is designed to assist with session management in ASP .NET applications, implementing the concept of a "long-running conversation" that spans multiple requests.
I think your real question is why cant I get my objects to save.
Even thought you are using a single ISession you still need to either Flush the session or commit transaction for some Save/Update/Delete actions to be commited.
I understand that each page refresh, especially in 'AjaxLand', causes my back-end/code-behind class to be called from scratch... This is a problem because my class (which is a member object in System.Web.UI.Page) contains A LOT of data that it sources from a database. So now every page refresh in AjaxLand is causing me to making large backend DB calls, rather than just to reuse a class object from memory. Any fix for this? Is this where session variables come into play? Are session variables the only option I have to retain an object in memory that is linked to a single-user and a single-session instance?
You need ASP.Net Caching.
Specifically Data Caching.
If your data is user-specific then Session would be the way to go. Be careful if you have a web farm or web garden. In which case you'll need a Session server or database for your session.
If your data is application-level then Application Data Cache could be the way to go. Be careful if you have limited RAM and your data is huge. The cache can empty itself at an inopportune moment.
Either way, you'll need to test how your application performs with your changes. You may even find going back to the database to be the least bad option.
In addition, you could have a look at Lazy Loading some of the data, to make it less heavy.
Take a look at this MS article on various caching mechanisms for ASP.NET. There is a section named "Cache arbitrary objects in server memory" that may interest you.
Since you mention Ajax, I think you might want to consider the following points:
Assume this large data set is static and not transient, in the first call to Ajax, your app queries the database, retrieves lots of data and returns to the client (i.e. the browser/JavaScript running on the browser, etc), the client now has all of that in memory already. Subsequently, there's no need to go back to the server for the same data that your client already has in memory. What you need to do is using JavaScript to rebuild the DOM or whatever. All can be done on the client from this point on.
Now assume the data is not static but transient, caching on the server by putting them is the session won't be the solution that you want anyway. Every time your client sends a request to the server, and the server just returns what's in the cache (session), the data is already stale and there's no difference from the data that the client already has in memory.
The point is if the data is static, save round trips to the server once you already have data in memory. If the data is transient, I am afraid there's no cheap solution except re-querying or re-retrieving the data somehow, and send everything back to the client.
i have a client server application, the server uses nhibernate.
i wanna know how should i use the session?
per call?
per client?
single?
other way?
and how can i keep the session cache in the server ?
and also i wanna know if the session is thread safe?
You should use one session per unit of work. If that includes multiple operations, so be it.
Use the session.BeginTransaction() to wrap the unit of work and commit once all the items are done.
Sessions are NOT thread safe, but the session factory is (which you definitely want to keep around).
NHiberate has various cache options for data, but the sessions are meant to be used and disposed.
Normally it's done one per request. You can create HttpApplication, which opens the session at the beginning of request and closes at the end of request (example).
Per call should be the usual solution
There really is no one right answer to the question of session lifetime. You can make any session lifetime work, it depends on your requirements. Sessions are not thread safe, but session factories are.
To keep the cache around, you need to keep the session around. It is likely to be fairly challenging to keep the cache around and keep the cache correct in anything but simple single user, single process applications.
There's a great example I've used from NHibernate Best Practices.
The code example uses a session per ASP.NET request.