Hacker News new | ask | show | jobs
by logicalmind 2319 days ago
C# dev here as well, but from a Java background. When I first moved to C# from Java one of the best AOP usages was transaction management. Database transaction management. You could write all of the code, whether it was dependent upon the db or not, and then decorate the methods with a transaction attribute. This decoration contained all the logic to get a db connection, begin a transaction, become part of an existing one, or create a new isolated one. Any unhandled exception caused the final unwinding to rollback any work that had been done in that transaction. So many try/catch/finally's avoided and so much boilerplate code.

I have yet to find any equivalent to this .NET world. Especially of you're using EF. Either you use ADO and have your try/catch/finally with manual transaction management, or you have the EF context which is just one big blob you hope succeeds at the end.

1 comments

TransactionScope has been around forever in the .Net Franework version of EF.

It just came back to EF Core in 2.1

https://www.google.com/amp/s/codewala.net/2018/05/06/transac...

Yes, this is exactly the type of boilerplate I am talking about. All those usings and try/catch blocks which add needless code. It is possible to compose all of this into an aspect which then decorates your methods. Maybe I'm not being clear, so here is what I mean. Say you have a method that does some work, but calls some other thing to do some auditing. The auditing is nice, but it failing shouldn't halt the world.

  [Transaction]
  DoYourBusinessWork() { ...; AlsoAudit(); }

  [Transaction(RequiresNew, NoRollbackFor(AuditException)]
  AlsoAudit() { ... }
The TransactionScope is handled in the aspect. Commit/Rollback is all handled there is well. There are not usings or exception handlings within your methods unless you want to handle those specifically.
There should only be one using/try catch block on the highest level. All of the methods being called within that using block should just throw errors.

You could put the logic in attributes but I don’t consider a transaction a cross cutting concern. It would create a spooky action at a distance situation.

It's obviously highly dependent on the domain in which you work, but I would consider what you're saying to be a "business transaction" more than a "database transaction". If there is a 1:1 between the two then your way works. I tend to have situations where one business transaction is multiple database transactions. And the business transaction can succeed even if some of the underlying database transactions were to fail.