New Profiler Feature: Avoid Writes from Multiple Sessions In The Same Request
Because I keep getting asked, this feature is available for the following profilers:
- NHibernate Profiler
- Entity Framework Profiler
- Linq to SQL Profiler
- LLBLGen Profiler
- Hibernate Profiler
This new feature detects a very interesting bad practice, write to the database from multiple session in the same web request.
For example, consider the following code:
public void SaveAccount(Account account) { using(var session = sessionFactory.OpenSession()) using(session.BeginTransaction()) { session.SaveOrUpdate(account); session.Transaction.Commit(); } }public Account GetAccount(int id) { using(var session = sessionFactory.OpenSession()) { return session.Get<Account>(id); } }
It is bad for several reasons, micro managing the session is just one of them, but the worst part is yet to come…
public void MakePayment(int fromAccount, int toAccount, decimal ammount) { var from = Dao.GetAccount(fromAccount); var to = Dao.GetAccount(toAccount); from.Total -= amount; to.Total += amount; Dao.SaveAccount(from); Dao.SaveAccount(to); }
Do you see the error here? There are actually several, let me count them:
- We are using 4 different connections to the database in a single method.
- We don’t have transactional safety!!!!
Think about it, if the server crashed between the fifth and sixth lines of this method, where would we be?
We would be in that wonderful land where money disappear into thin air and we stare at that lovely lawsuit folder and then jump from a high window to a stormy sea.
Or, of course, you could use the profiler, which will tell you that you are doing something which should be avoided:
Isn’t that better than swimming with the sharks?
Comments
Ayende,
I don't know if it's a "problem" in the NHProf, but when using LINQ to perform queries, when I use the FirstOrDefault(), the profiller shows me an alert saying that "the min/max number of records was not specified. doing it in memory". It does not happen when I use SingleOrDefault().
Is it the expected behaviour??
Bisneto,
What SQL is generated for each?
Hi!
Sorry for the delay... The SQL statements are the same...
The LINQ query is:
<user()
<user,> (z => z.Wallets)
just changing the Single for First....
This is the SQL generated using SingleOrDefault():
select user0_.UserId as InvestorId4_0_,
from Users user0_
where user0_.Login = 'cidico' /* :p0 */
This is the SQL generated using FirstOrDefault():
select user0_.UserId as InvestorId4_0_,
from Users user0_
where user0_.Login = 'cidico' /* :p0 */
Your blog has removed the "greater than" and "less than" from my post.
I was using the generic method Query() and FetchMany()
:)
Don't use Query <t, it's highly buggy.
Using MAX/MIN causes it to do casting, using the Criteria stuff it works perfectly. Theres too many bugs around Query, it's definitely not ready for production.
If someone doesn't understand how to handle transactions he shouldn't be allowed to write any code. So maybe the next useful function would be to make profiler find such guys and exterminate them. Ayende, how does this fit into your design?
I find this everywhere people are using MS Enterprise Library. Every method of every DAO will contain Database.CreateConnection().
I much prefer having the connection passed around explicitly instead of hidden away,
Should this not be closer to db in a stored proc where multiple connection problem does not arise?
Govind,
If you are using the ORM properly, it is not an issue
What would happen if a single web request updated data in multiple sessions, but they were against different data sources? eg different servers, different databases or even different DB schemas?
I would expect that scenario to not report an error.
Having multiple schemas in the same database is very useful for partitioning the security domain from application domain in a defense-in-depth way. Yet both domains may need to be updated in the same request.
Comment preview