3

I am having 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>

The use Task<T> because they are libraries developed in other .NET languages.

(I have created this code for the sake of the example)

Basically, I want to save data in the repository service, and then save the data in the notification service. But if this second operation fails, and that includes exceptions, I want to rollback the operation in the repository. Then there are two situations where I would want to call the rollback operation, the first if notification.SaveAsync returns false, and the second if it throws an exception. And of course, I would like to code that call to rollback once, but I cannot find the way.

This is what I have 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 get a compiler error on the let! nr = ... saying: This construct may only be used within computation expressions

Which would be the right way of doing this?

knocte
18.2k14 gold badges87 silver badges134 bronze badges
asked Nov 25, 2014 at 16:24

1 Answer 1

9

The problem is that when you use let v = e in computation expressions, the expression e is an ordinary expression that cannot contain further asynchronous constructs. That's exactly what happens here:

let notifResult =
 try
 let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
 nr
 with _-> false

You can turn this into a nested async block:

let! notifResult = async {
 try
 let! nr = Async.AwaitTask <| notification.SaveAsync(entities) 
 return nr
 with _-> return false }
answered Nov 25, 2014 at 16:31
Sign up to request clarification or add additional context in comments.

1 Comment

That is interesting, I didn't notice you can nest them. Thanks!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.