How to check whether DbContext has transaction?
I think you're looking for the CurrentTransaction
property of the DbContext:
var transaction = db.Database.CurrentTransaction;
Then you can do a check like this:
using(var transaction = db.Database.CurrentTransaction ?? db.Database.BeginTransaction())
{
...
}
However I'm not sure how you can know when to commit the transaction if it's being used by concurrent methods.
Instead of using the transaction from the DbContext of Entity Framework you could or maybe should use the TransactionScope class which creates an ambient transaction scope and manages transactions of all connections made to the (SQL) database under the covers.
It even would put a direct SqlCommand
in the same transaction if you would use the exact (case-sensitive) connectionstring for the SqlCommand
. Messages writen to the MessageQueue are also encapsulated in the same transaction
It even could manage connections to different databases at the same time. It uses the DTC windows service for this. Beware that this is a pain to configure if needed. Normally, with a single DB connection (or multiple connections to the same DB) you won't need the DTC.
The TransactionScopeCommandHandlerDecorator
implementation is trivial:
public class TransactionScopeCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decoratee;
public TransactionScopeCommandHandlerDecorator(ICommandHandler<TCommand> decoratee)
{
this.decoratee = decoratee;
}
public void Handle(TCommand command)
{
using (var scope = new TransactionScope())
{
this.decoratee.Handle(command);
scope.Complete();
}
}
}
But: As qujck already mentioned in the comments, you are missing the concept of ICommandHandler
as an atomic operation. One commandhandler should never reference another commandhandler. Not only is this bad for transactions, but also consider this:
Imagine the application grows and you would refactor some of your commandhandlers to a background thread, which will run in some windows service. In this windows service a PerWcfOperation
lifestyle is not available. You would need a LifeTimeScope
lifestyle for you commandhandlers now. Because your design allows it, which is great by the way!, you would typicaly wrap your commandhandlers in a LifetimeScopeCommandHandler
decorator to start the LifetimeScope
. In your current design where a single commandhandler references other commandhandlers you will run into a problem, because every commandhandler will be created in its own scope a thus gets an other DbContext injected than the other commandhandlers!
So you need to do some redesign and make your commandhandlers holistic abstractions and create a lower level abstraction for doing the DbContext operations.