Reputation: 7681
I have a ton of transactional db access code intermixed with business logic in legacy code. I want to separate it to services and repositories, with transaction control outside services and abstract away transactions for testability.
The problem is, i need to have IDbConnection parameter, enlisted in transaction, for every method signature both in service and repository layers, and that is what i certainly don't like:
void Process(Stuff s, IDbConnection connection);
I want to figure out a way to have something like this in a top level method:
void Process(Stuff stuff, User u)
{
using(var UoW = ???) // Start transaction
{
// various operations in the same transaction
if( AuthorizationService.AuthorizesUserForStuff(u,stuff) )
{
StuffService.Process(stuff);
AuditService.Success(u, stuff);
}
else
AuditService.Failure(u,stuff);
UoW.Commit();
}
}
Repository:
void Process(Stuff s)
{
IDbCommand command = ??? //aware of current UnitOfWork
...
command.ExecuteNonQuery();
}
The legacy code does not use any kind of ORM, it's a lot of stored procedure and custom sql code, also some operations require roundtrips to database, so i can't just register the objects with unitofwork, and carry out operations in commit method.
Please suggest what pattern can be used in this situation for some persistence ignorance and will allow to control transactions at the top level.
Upvotes: 0
Views: 339
Reputation: 1140
The only way i see your repository without the actual unit of work, is through the Static gateway pattern. I understand your frustration, but i dont think doing it this way is much better. Also unit testing will be harder, and code readability as well.
I would be very intested in any other method.
Upvotes: 1
Reputation: 3211
We use OR-mapping for this, so our solution for this looks different. But one way to do accomplish the same thing could be to use an abstracted persistance context which deals with concrete database stuff, inject this into all the service classes that use it and create your unit of work from this context at the 'process' level, and then allow the UoW instance to manage transaction state and connections by collaborating with the context instance.
Something like
using (var unitOfWork = Context.CreateUnitOfWork())
{
// access injected services and repositories to modify stuff
unitOfWork.Complete();
}
When actually doing DB-stuff, in your situation you would aquire a DB connection from your (injected) context instance, something like this
using (var conn = Context.AquireDbConnection())
using (var command = conn.CreateDbCommand())
{
// do db stuff
command.ExecuteNonQuery();
}
your concrete unit of work/context implementation should be smart enough here to recognize if the connection returned should be a new or ambient connection, based on if there is an active ambient business transaction there or not.
Obviously, you would need per-request isolation for this mechanism and life time of context instance would be per request.
Upvotes: 0