Database transaction boundaries in three tier
-
Consider the following simplified case where I store several users at once. Transaction trans = cn.begintrans foreach (name in nameArray) { Person p = new Person(name); Person.Insert() } trans.commit(); I want Person.Insert() to: - Use the transaction created. (I can't access the transaction object inside the insert() call) This way, the users are not created when the BL produces an error. - Still make it reusable in a non transactonal context (such as creating a single user) I could do it with a using statment using a transactionscope instead, but actually I am wondering how it was done before .NET 2.0? How can I make the insert method available in transactional and none transactional context without violating three tier principles?
-
Consider the following simplified case where I store several users at once. Transaction trans = cn.begintrans foreach (name in nameArray) { Person p = new Person(name); Person.Insert() } trans.commit(); I want Person.Insert() to: - Use the transaction created. (I can't access the transaction object inside the insert() call) This way, the users are not created when the BL produces an error. - Still make it reusable in a non transactonal context (such as creating a single user) I could do it with a using statment using a transactionscope instead, but actually I am wondering how it was done before .NET 2.0? How can I make the insert method available in transactional and none transactional context without violating three tier principles?
I would just overload the insert() method so that, if you pass in a transaction object, it uses it.
-
I would just overload the insert() method so that, if you pass in a transaction object, it uses it.
That is an option, but ultimately I'm looking for a way to create business logic methods which are reusable in other BL methods in a transactional context without too much hassle. Writing two versions of each method (one with transactional support and one without) seems hard to maintain. I find it weird that only "recently" (using transactionscope) this is possible without repetitive code.
-
Consider the following simplified case where I store several users at once. Transaction trans = cn.begintrans foreach (name in nameArray) { Person p = new Person(name); Person.Insert() } trans.commit(); I want Person.Insert() to: - Use the transaction created. (I can't access the transaction object inside the insert() call) This way, the users are not created when the BL produces an error. - Still make it reusable in a non transactonal context (such as creating a single user) I could do it with a using statment using a transactionscope instead, but actually I am wondering how it was done before .NET 2.0? How can I make the insert method available in transactional and none transactional context without violating three tier principles?
You just created a Person object inside the foreach loop after starting a transaction. Why should the person know about the transaction if you are not providing it? Joe is right below, somehow you have to make the person aware of the transaction. In other words, how is that instantiation any different than if you did it before beginning transaction? How is this a three tier architecture?
-
You just created a Person object inside the foreach loop after starting a transaction. Why should the person know about the transaction if you are not providing it? Joe is right below, somehow you have to make the person aware of the transaction. In other words, how is that instantiation any different than if you did it before beginning transaction? How is this a three tier architecture?
Thank you for your reply. The sample I provided serves as an example a for what I'm trying to achieve, I don't consider it "correct". You are right about the sample not being three tier also. I'm only looking for a way to make a method reusable in both a transactional and a non transactional context (such as insertPerson) without writing two versions of it...if that is possible? You might write this method just to insert a person into the db and later on you notice that you need to reuse this method in a transaction elsewhere in your application. (for example to insert multiple persons at once) That would basicly mean you need to completely rewrite this method using a transaction for this purpose only. On the other hand, you can use a transactionscope in .NET which automatically attaches the active transaction to any ADO.NET database call. This way you don't have to rewrite to insertPerson method to be used in transactions. I'm just wondering what others do in such a situation? Rewrite any method they need to reuse in a transaction with another method signature (e.g. adding a transaction argument like you said.)...that just seems a bit redundant to me.
-
Thank you for your reply. The sample I provided serves as an example a for what I'm trying to achieve, I don't consider it "correct". You are right about the sample not being three tier also. I'm only looking for a way to make a method reusable in both a transactional and a non transactional context (such as insertPerson) without writing two versions of it...if that is possible? You might write this method just to insert a person into the db and later on you notice that you need to reuse this method in a transaction elsewhere in your application. (for example to insert multiple persons at once) That would basicly mean you need to completely rewrite this method using a transaction for this purpose only. On the other hand, you can use a transactionscope in .NET which automatically attaches the active transaction to any ADO.NET database call. This way you don't have to rewrite to insertPerson method to be used in transactions. I'm just wondering what others do in such a situation? Rewrite any method they need to reuse in a transaction with another method signature (e.g. adding a transaction argument like you said.)...that just seems a bit redundant to me.
Here is what I would do in such a case: Person class will have a method called Save(). When this method is called it will call the DAL's Save method and pass itself in. DAL will ask the person if it should be inserted or updated--no need to create separate methods for update and insert but you can. If it is to be inserted then DAL should call its own private method and insert the person and do the same if update. For multiple persons, I would create a PersonCollection class and when Save() is called it will call the PersonCollectionDAL and pass itself in. The PersonCollectionDAL can either ask each person to Save() and pass a transaction over to it. Make this Save() method internal so it is only accessible from DAL Layer not UI Layer. Alternatively, the PersonCollectionDAL can throw all the persons in a DataTable and ask a dataAdapter to insert them. There is no right and wrong way, it is about software craftsmanship. If you are worried about reusing the insert methods code you mentioned above, then simply make the code a private method and the two public methods (Single and Transactional) can both use it. Obviously, this is just a suggestion and when you start your implementation you will have other obstacles to overcome. Finally, although transactions can be used to speed things up for multiple inserts, yet I do not like using them because if one person is not inserted then why should the other ones roll back. Transactions are supposed to be for steps which depend on each other and either all pass or all fail. It is pretty obvious that is not the case here.
-
Consider the following simplified case where I store several users at once. Transaction trans = cn.begintrans foreach (name in nameArray) { Person p = new Person(name); Person.Insert() } trans.commit(); I want Person.Insert() to: - Use the transaction created. (I can't access the transaction object inside the insert() call) This way, the users are not created when the BL produces an error. - Still make it reusable in a non transactonal context (such as creating a single user) I could do it with a using statment using a transactionscope instead, but actually I am wondering how it was done before .NET 2.0? How can I make the insert method available in transactional and none transactional context without violating three tier principles?
I think using TransactionScope Class[^] will solve most of your problems.
Hesham A. Amin My blog