I've used CSLA in several web sites, but have never done transactions, until now.
Based on the videos Rocky created for 3.8, I have started decorating all of my DataPortal calls with [Transactional(TransactionalTypes.TransactionScope)]. In addition, I am now using the Repository pattern and within each repository method, I am using the Csla.Data.ConnectionManager to reuse database connections.
I have been able to get transactions working, but not sure I am doing it the best way.
In my case, I have a Conversation (BusinessBase) with a list (BusinessListBase) of messages.
Within a "Service" class I create transaction:
connectionTransaction = TransactionManager<SqlConnection,SqlTransaction>.GetManager("FavsDB").Transaction;
Add the messages to the list and save the Conversation.
In order to get the transactions to work, I had to change the Transactional attribute of Conversation DataPortal methods to Manual:
protected override void DataPortal_Insert()
Finally, in the Repository, I changed it so it uses the transactionManger instead of the regular ConnectionManager<SqlConnection>.GetManager("")
using (var tm = TransactionManager<SqlConnection, SqlTransaction>.GetManager("FavsDB"))
Again, this all works, but now when I go to add a "reply" message, which doesn't need to be in a transaction, the newly created record doesn't get committed to the database, because the DataPortal and Repository Insert method expect to be in a transaction, which I am hoping to avoid.
Is there a way to simply create a transaction, have the DataPortal keep the TransactionalTypes.TransactionScope and have the repository methods use the ConnectionManager so they do not know/care if they are in a transaction? I would like for the "Service" to be the one that determines if the db calls need to be in transaction, and not have the DataPortal or Repository methods know this.
Again, first time using transaction, so not sure if what I am asking is possible.
You should read up on what System.Transactions.TransactionScope does for you - that's what CSLA is using.
There is no need to create an explicit transaction when using TransactionScope - it is automatic (within some limits).
Basically, if you decorate your root DataPortal_XYZ or object factory method with [Transactional] you are automatically protected.
The ConnectionManager (and similar) types are important to avoid accidentally using the DTC, which will be a perf hit and increases deployment complexity.
Chapter 18 is a good thing to read too - lots of good info about data access in that chapter.
I think transactions should be handled in your data layer and/or separate infrastructure project. I'm trying to prototype CSLA solution with separate NHiberante data layer. I'm using unit of work and repository pattern while accessing NH layer. I think the most important thing is that BO layer stays as much "clean" of any db related code.
For implementing UoW and repository pattern I'm using something very similar to this:
So, typical DataPortal method might look something like this:
using (IUnitOfWork _UnitOfWork = EnsureDependency(Ioc.Container.Resolve<IUnitOfWork>()))
var context = EnsureDependency(_repository);
var dto = new SomeDto();
// filling empty dto object
// or some others calls to children update
Everything in using statement is "protected", because only if Flush is executed, transaction will be committed. Btw, I can open explicit transaction if I wish.
I'm still evaluating this solution and searching for best implementation...
Copyright (c) Marimer LLC