F # with asynchronous workflow and try / with
I have a headache trying to put together a simple functionality.
Consider the following definitions:
type Entity = {Id:int;Data:string}
type IRepository =
abstract member SaveAsync: array<Entity> -> Task<bool>
abstract member RollBackAsync: array<Entity> -> Task<bool>
type INotification =
abstract member SaveAsync: array<Entity> -> Task<bool>
Usage Task<T>
because these are libraries developed in other .NET languages.
(I created this code for example)
Basically, I want to store the data in the repository service and then store the data in the notification service. But if this second operation fails and throws exceptions, I want to undo the operation on the repository. Then there are two situations where I would like to trigger a rollback operation, the first if it notification.SaveAsync
returns false and the second if it throws an exception. And of course I would like to program this call to rollback once, but I cannot find a way.
Here's what I've tried:
type Controller(repository:IRepository, notification:INotification) =
let saveEntities entities:Async<bool> = async{
let! repoResult = Async.AwaitTask <| repository.SaveAsync(entities)
if(not repoResult) then
return false
else
let notifResult =
try
let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
nr
with
| _-> false
if(not notifResult) then
let forget = Async.AwaitTask <| repository.RollBackAsync(entities)
return false
else
return true
}
member self.SaveEntitiesAsync(entities:array<Entity>) =
Async.StartAsTask <| saveEntities entities
But unfortunately I am getting a compiler error in let! nr = ...
: This construct can only be used in computation expressions
What would be the correct way to do this?
source to share
The problem is that when an expression is used let v = e
in computation expressions, the expression e
is a regular expression that cannot contain additional asynchronous constructs. What exactly is happening here:
let notifResult =
try
let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
nr
with _-> false
You can turn this into a nested block async
:
let! notifResult = async {
try
let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
return nr
with _-> return false }
source to share