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?

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 }
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

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.