"Building Layered Web Applications with Microsoft ASP.NET 2.0" article published by Imar Spaanjaars differs from other articles on this topic.
- It follows a good Domain-Driven Design design instead of relying DataSets (see my previous notes)
- It presents a clean and ready-to-use code
Imar's article is intended to teach people by a good example. Obviously, he could not put everything necessary for a big application into one article. There are very interesting conversations located beneath each three parts of his article. I commented there as well and put too much stuff there. My fault. So, let me tell you here what I think about Imar's article and what I'm going to add in my implementation of his approach.
1. How to handle transactions in Business Layer (Bll) and allow DAL methods to share database connections without coupling Bll to features of database and OS. (See part 2 of Imar's article)
1) I downloaded Imar’s code and run it on Windows Server 2008 / SQL Server 2008 machine. It works fine. I could not find suggested settings for Microsoft Distributed Transaction Coordinator on Win Server 2008 machine, though. Does anyone know how to detect if it is running? Does the fact that Imar’s application, which uses TransactionScope object, runs without errors mean that MSDTC is running? Am I supposed to get errors if MSDTC is not running, or it would be silent?
2) As Imar mentioned in his answer to Math Random 's comment, in case MSDTC is not available, we would need to use SqlTransaction and therefore all DAL methods involved in saving related data would need to share the same Connection object.
We could pass connection object between DAL method calls inside Bll’s ContactPersonManager.Save() method. What I don't like here is that we're making an internal structure of Bll method ContactPersonManager.Save() dependant on external circumstances (whether MSDTC is available and whether we need to pass SQL Server connection around). Ideally, Bll method shouldn’t care about such things not related to business logic; it only should care about integrity of its objects and for its objects being able to save/retrieve themselves consistently. It would be nice to de-couple Bll method from such external things.
I guess, we need to add a level of indirection here. I would add one more [static ?] DAL class called ConnectionManager. This class would be responsible for providing connections to individual DAL objects (their methods) and for managing transactions. If necessary, Bll methods would call ConnectionManager.BeginTransaction() method to start a transaction. Internally, ConnectionManager would either use MSDTC -> TransactionScope if it is available, or open a SQL Server connection and start SqlTransaction on it otherwise. (Bll methods wouldn't care about those details.)
Then, Bll methods would call individual DAL methods the same way as ContactPersonManager.Save() calls AddressDB.Save(), EmailAddressDB.Save(), and PhoneNumberDB.Save() right now. There would be no need to pass around connection object: each DAL method would obtain a connection from ConnectionManager using ConnectionManager.GetConnection() method. It could be the same shared connection, or it could be a new connection every time. For example, in case we're using SqlTransaction, ConnectionManager would provide DAL methods with the same connection which was opened during ConnectionManager.BeginTransaction() method call.
DAL methods would not call myConnection.Close() methods directly, as they do in Imar 's code. Instead, they will call ContactPersonManager.CloseConnection(myConnection). ContactPersonManager object would then either close a connection or keep it open, depending on if it is still needed (for a pending SqlTransaction).
We also can wrap SqlConnection into our CustomConnection class overriding Close() and Dispose() methods in such a way, that they will ask ContactPersonManager object if connection should be really closed. That would allow us to work with "using" blocks.
It looks like such a design would allow us to decouple Bll layer from the specifics of a particular server (like availability of MSDTC transactions or using SQL Server or Oracle transactions instead.
What do you think? Am I re-creating a wheel here? I have a feeling that such solution already exists, but I don’t know it because of my ignorance.