How to flatten the nested try-catch block in F#?

How to flatten the nested try-catch block in F# for the following code? I am using EF Core with F#. The nested is required because I need to Rollback the transaction if there are errors in saving or committing the transaction!

let createTenant (ctx: AppContext) (tenant: Db.Tenant) (firstUser: Db.User) =
async {
try
let! db = ctx.Aquire()

use! transaction = db.Database.BeginTransactionAsync(IsolationLevel.Serializable) |> Async.AwaitTask

let! result =
async {
try
let! _ = db.SaveChangesAsync() |> Async.AwaitTask

return Ok tenant.Id
with
| _ ->
return Error "internal error"
}

ctx.Release()

return result

with
| _ -> return Error "internal error"
}


On a side note, is this the clean/idiomatic way to handle exceptions in F#? Is there a better way to do this?

• The current question title of your question is too generic to be helpful. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?. Sep 27 '20 at 18:36
• You don't need the inner try/catch. If SaveChangesAsync() errors then the transaction will be rolled back anyway and you jump into the outer try/catch. Sep 29 '20 at 6:38
• @GertArnold I am new to EF Core. Does it automatically rollback the transaction if there are errors in SaveChangesAsync() or CommitAsync() method? Sep 29 '20 at 7:34
• Yep. It's all or nothing with these methods. Sep 29 '20 at 7:38
• Not related to question, but since you're working mostly with TPL and Task class, it would be better to use Ply or TaskBuilder to avoid usage of Async.AwaitTask which might be slow and gives better interop experience when your library is called from other languages Mar 12 at 5:49