-
I am using EF Core and dealing with transaction. The code is like this: public Aff<T> ExecuteAsync(Aff<T> action)
{
var strategy = _context.Database.CreateExecutionStrategy();
var result = strategy.ExecuteAsync(async () =>
{
await using var transaction = await _context.Database.BeginTransactionAsync();
return action().Run().Match(
async result => { await transaction.CommitAsync(); return FinSucc(result); },
async ex => { await transaction.RollbackAsync(); return FinFail<T>(ex); }
);
});
return AffMaybe(async () => await result);
} Currently I have to run the Aff inside transaction, then turn it back to Aff and return. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
That's actually a reasonable approach, if you think about it, you must If you wrap up the transaction part into an extension method, you can do something like I'd refactor it like this: public static class YourAddExtensions
{
public static Aff<T> WithTransaction(this Aff<T> action, Context context) =>
AffMaybe(async () =>
{
var strategy = context.Database.CreateExecutionStrategy();
return await strategy.ExecuteAsync(async () =>
{
await using var transaction = await context.Database.BeginTransactionAsync();
return action.Run().Match(
async result => { await transaction.CommitAsync(); return FinSucc(result); },
async ex => { await transaction.RollbackAsync(); return FinFail<T>(ex); }
);
});
});
} I've factored out the context so it can be static, I don't know what library you're using, so I can't really test it. If you used the runtime version of the |
Beta Was this translation helpful? Give feedback.
-
I was wondering the following about the accepted answer; when you commit/rollback the transaction you could still get an exception right? Is the I wrote something similiar myself which is a bit different to the code posted above. I use the standard DbTransaction to execute SQL on the SqlConnection. If you have any feedback on how to improve this, please let me know! :) public static Aff<Unit> ExecuteCommandsInTransaction(Func<DbTransaction, Aff<Unit>> sqlCommands, string connectionString) =>
use(
StartTransaction(connectionString), // Opens SqlConnection; returns Aff<DbTransaction>
transaction => ExecuteTransactionWithRollback(sqlCommands, transaction));
private static Aff<Unit> ExecuteTransactionWithRollback(Func<DbTransaction, Aff<Unit>> sqlCommands, DbTransaction dbTransaction)
{
var dbOperations =
from unit1 in sqlCommands(dbTransaction)
from unit2 in CommitTransaction(dbTransaction)
select Unit.Default;
return dbOperations
.IfFailAff(
error =>
{
return RollBackTransaction(dbTransaction)
.Bind(_ => LanguageExt.Aff<Unit>.Fail(error));
});
} |
Beta Was this translation helpful? Give feedback.
That's actually a reasonable approach, if you think about it, you must
Run()
, because it's a resource that has pre-and-post effects - so the lazyAff
must be forced to evaluate. Wrapping it up like this is a good approach, it hides theRun
and embelishes theAff
with transactional support.If you wrap up the transaction part into an extension method, you can do something like
aff.WithTransaction(context)
to run it. Another thing: you should probably wrap up everything inside anAffMaybe(() => ...)
rather than just the result. The effect you'll have here is to create a memoised result (which may be what you want, I dunno). By wrapping everything you get a re-runnableAff
.I'd refactor it l…