the two previous responses were poor: No reviews, no praise. Very sad, why? I did not update for a long time--oh, well, I admit, this is just my excuse. However, I hope you will give us more feedback. No feedback, I can only guess: the previous two is not written too "thick" a bit? So let's try to be more detailed in this article.
What is Session Per request?
This is a pattern used to build Web projects using NHibernate, and the related articles are actually a lot. I try to make a simple explanation in my language (meaning plain English, but may not be accurate).
First, you have to understand what a session is. This is not an ASP. NET inside of that session, beginners at this point prone to stun. This is the concept of nhibernate.
- If you are particularly interested in it, you can first search the "unit of work" keyword, understand the pattern, and then gradually understand: the session is actually nhibernate to the Unit of work of the implementation.
- If you only want to know a ballpark, then you can think of it as a temporary "container" that loads the entity from the database and keeps track of its changes.
- If you still feel dizzy, think of it as an open, active database connection.
We all know that the cost of database connection is very large, for this reason. NET also introduces the concept of "connection pooling" in particular. Therefore, if you can effectively reduce the number of connections to the database, the performance of the program will have a huge lifting effect. After observation and reflection, everyone (I do not know who was the first to put this concept) felt that an HTTP request to allocate a database connection was a very good solution. As a result, session Per request quickly became popular, almost as standard for NHibernate building Web applications.
Why do you think about performance again?
After I published "Performance" post, although many praise, but the comment area controversy is still very big. But one is the comment area later Crooked Building, two is a word over and over said too boring, so I no longer distinguish. But session Per request is a good example of what it means to be " performance-based serviceability":
- If the maintainability of the code is compromised for performance, then we prefer not to performance;
- In the premise of ensuring maintainability, we should certainly strive to improve performance;
- Rather than focusing on local (non-performance bottlenecks), it is better to ensure/promote overall performance at the architectural level.
I said that the " performance of the problem first," and " Forget the Database ," and so on, is based on the correction must be positive starting point, hoping to have a amazing publication effect. But the results didn't seem very good, and in the comments I saw "select TOP 1 * from TABLE WHERE id>currrid" stuff. What does that mean? The relational database is not only rooted in your head, but it's already filling your head. This is not to say no, just so, there is no way to talk to you about the "object".
Session Per request is a design that has been widely adopted and works well to improve performance at the architectural level.
UI or service
We only from session Per request definition, what HTTP ah, request Ah, intuitively can think of the scope of the UI layer it?
Many of the examples on the Web are actually written like this. In application buildsessionfactory, configure in HttpModule: Once the Http request arrives, generate a session;http request end, call Session.flush () Synchronize all changes.
However, we have established a principle in the architecture that the UI layer does not involve database operations. More intuitively, project at the UI layer has no references to NHibernate.dll. What about that?
Now I think it's funny, but I spent a lot of brain cells: in fact, only need to encapsulate the service layer related operations, and then call the service layer at the UI layer.
The unreliable ideas that have knocked me out of the way can be ignored by everyone. If really interested, can think about: NHibernate in the context of the session, we should certainly set up the web, but the service will eventually be compiled into a DLL, this DLL can fetch HttpContext?
But how to encapsulate it in the service is also a matter of discretion.
Variation, slight performance improvement
The last scenario I used was to introduce Baseservice:
First, a static sessionfactory is set in the Baseservice, and Sessionfactory is assigned to the Baseservice static constructor (Build sessionfactory). This ensures that the sessionfactory is generated only once, because generating sessionfactory is a costly process.
Public classbaseservice{Private Staticisessionfactory sessionfactory; StaticBaseservice () {stringConnStr = configurationmanager.connectionstrings["Dev"]. ConnectionString; Sessionfactory=fluently.configure (). Database (MySQLConfiguration.Standard.ConnectionString (CONNSTR). Dialect<MySQL5Dialect>()) . Mappings (configurationprovider.action). Cache (x= = X.usesecondlevelcache (). Providerclass<syscacheprovider>()) . Exposeconfiguration (c= C.setproperty (NHibernate.Cfg.Environment.CurrentSessionContextClass,"Web")) . Buildsessionfactory (); }
introduction of Sessionfactory
Second, a static EndSession () method is exposed in Baseservice to synchronize data changes to the persistence layer (database) at the end of the request. So when the UI layer is called, you don't need to instantiate a baseservice, just call Baseservice directly:
Public class baseservice{ publicstaticvoid endsession () { }}
EndSession
Then we look back at the previous statement: "Once the HTTP request arrives, a session is generated;", so theoretically a initsession () method is needed to generate/provide a session. But I was suddenly a little bit smart: Some pages might not require database operations, such as Help, form rendering, or other pages we don't think of. So we always generate a session anyway, is it a waste of points?
The more I think about it, so wrestling, made a plan: generate session on Demand. The approximate process is:
- Try to get the session;
- If there is already a session in the "Current environment", the session is used directly;
- Otherwise, a session is generated, the session is used, and it is stored in the current environment.
It seems that nhibernate support this idea, so provide a ready-made interface, can be very convenient to implement the above ideas:
protected ISession Session { get {ISession _session; if (! = Sessionfactory.opensession (); Currentsessioncontext.bind (_session); else = Sessionfactory.getcurrentsession (); return _session; } }
get session on Demand
Currentsessioncontext is the so-called "current Environment", in our system China is a HttpContext; we use Getcurrentsession () It is always possible to ensure that the removed session is the existing session in the current HttpContext. All service is inherited from Baseservice, directly call the session in Baseservice, so it can effectively guarantee the implementation of Session Per request.
Boys and girls, do you know this? In fact, I am still a very "key" performance of the person. But is this really worth it? I'm not sure, after all, this has increased the complexity of the code to a certain extent, and the performance gains are actually limited.
Always use explicit transactions
If the students look at the source code, they will find that our session is always enabled for transactions.
protectedISession Session {Get { //...... if(!_session. transaction.isactive) {_session. BeginTransaction (); } return_session; } } Public Static voidendsession () {if(Currentsessioncontext.hasbind (sessionfactory)) {//....... using(sessionfromcontext.transaction) {Try{sessionFromContext.Transaction.Commit (); } Catch(Exception) {sessionFromContext.Transaction.Rollback (); Throw; } } } }
Always use Transactions
In our traditional concept, the use of "transaction" will increase the cost of the database and reduce performance. But that's not really the case, at least I can assure you that this is not the case in NHibernate and MySQL.
There are several reasons for this:
- Even if you do not explicitly declare a transaction, the database will explicitly generate a transaction;
- NHibernate's level two cache requires transaction assurance
For more information, please refer to: Use of implicit transactions is discouraged
In fact, since the session Per request mode is used, we should always use "transaction" even if we consider it from the business logic: many times the form commits to perform multiple database operations, some steps perform some reported exceptions, the data is incomplete?
no Session.save () and update ()
As I have said before, there is no update operation for the database in the service. We do this by changing the properties of the Load () data, and then the Save () to the database.
But when the students look at our source code, they will find: "Eh?" Why is there no such a process as Session.save ()? ”
First, you should be aware that update () in Nhibernat is not what most of our classmates imagined, and corresponds to the UPDATE statement in SQL. It is actually used for multiple session interaction scenarios, and our current system is never used.
Then, NHibernate does not use Session.save () to synchronize the data in the session to the database. The session is only occasionally used in our system. Save () to temporarily get the ID of the entity.
Finally, NHibernate is actually using Session.flush () to eventually "synchronize" the data in the memory (session) to the database. The Session.Transaction.Commit () is used in our code, which automatically calls the session. Flush ().
Because session Per request mode, we always call EndSession () at the end of the request in the UI layer, so in the service's code, there is no process of "storing" the data.
calls to the UI layer
So where do you call EndSession () at the UI layer? (BeginSession () is not required because the session is generated on demand)
Broadly speaking, there are two scenarios, one is to use HttpModule, and the other is to take advantage of the filter mechanism of ASP.
We adopted the latter, one of which is simpler, and on the other hand because: when Childaction is introduced, the Session Per action is logically more self-consistent. For example, a request may contain more than one child action, and multiple child actions can be placed in a session with unpredictable surprises.
Of course, the downside of this is that it consumes more sessions, but fortunately the session overhead is small and we use the "Generate session on demand" to reduce some of the session generation scenarios.
The code is very simple, as follows:
Public class Sessionperrequest:actionfilterattribute { publicoverridevoid onresultexecuted (resultexecutedcontext Filtercontext) { #if PROD FFLTask.SRV.ProdService.BaseService.EndSession () ; #endif Base . Onresultexecuted (Filtercontext); } }
call EndSession ()
#if prod is used for front-end separation (detailed later): only when you call Prodservice, the above code is used, and UI developers do not need to change the operation when using Uidevservice.
At the same time, in order to avoid repeated declarations, we extract the Basecontroller, inherited by all controllers, and declare sessionperrequest on Basecontroller:
[sessionperrequest] publicclass basecontroller:controller { }
sessionperrequest Statement
other
Since we implement data Synchronization (Session.Transaction.Commit ()) After action rendering, all of our Ajax calls do not use the web API, but rather inherit the Jsonresult from ActionResult. Otherwise, the Onresultexecuted event is not triggered and the database cannot be synchronized.
Public Jsonresult gettask (int taskId) { string title = _taskservice.gettitle (taskId ); return Json (new {title = title}); }
Ajax return Jsonresult
In conclusion, we actually draw on the idea of sessionperrequest, and actually adopt the implementation of a session on demand, and an action using a session. Can be described as: Sessionperactionifrequire, hehe.
With Sessionperrequest, we can find an important role of architecture: encapsulating the "technically complex" parts of the system, allowing developers to move away from complex and trivial technologies and focus on the implementation of specific businesses. In fact, with our system, even a general developer who does not understand NHibernate can quickly start writing business area code after a simple introduction/training.
+++++++++++++++++++++++++++++
Should be the 2016 Spring Festival before the last "Road of Architecture" update, first wish everyone Happy New Year, all the best!
In addition, welcome all kinds of comments (including making bricks).
O (∩_∩) o~
The road to Architecture (ix) Session Per Request