3

Say I have interface:

interface IProductRepository
{
    Task SaveProduct(Product p);
}

previously implemented by C# class:

class CSharpProductRepository : IProductRepository
{
    public Task SaveProduct(Product p)
    {
        _db.Products.Add(p);
        return _db.SaveChangesAsync();
    }
}

Now I want to implement the same in F#:

type FSharpProductRepository =
    interface IProductRepository with
        member this.SaveProduct(p : Product) : Task = this.SaveProduct(p) // error 1

    member this.SaveProduct(p : Product) = async {
        db.Products.Add(p)
        return db.SaveChangesAsync() |> Async.AwaitTask // error 2
    }

But getting an error (1):

This expression was expected to have type Task but here has type Async<'a>

and (2):

Type constraint mismatch. The type Task is not compatible with type Task<'a>

2 Answers 2

6

Given that Task<'a> is a subtype of Task you can do smth like this:

open System.Threading.Tasks

// stubs since I don't know what the actual code looks like
type Product = class end

type IProductRepository = 
    abstract SaveProduct: product: Product -> Task

type Db = 
    abstract Products: System.Collections.Generic.ICollection<Product>
    abstract SaveProductAsync: product: Product -> Task<int>

type Repository(db: Db) = 
    interface IProductRepository with
        member this.SaveProduct(p: Product) = 
            db.Products.Add(p)
            upcast db.SaveProductAsync(p)
Sign up to request clarification or add additional context in comments.

Comments

4

you need to start the async workflow with Async.StartAsTask but sadly that does not work here as this will need a generic task Task<'a> :( - so I think you have to use the AwaitWaitHandle method with the tasks AsyncWaitHanlde

type FSharpProductRepository =
    interface IProductRepository with
        member this.SaveProduct(p : Product) : Task = 
           this.SaveProduct(p)

    member this.SaveProduct(p : Product) = 
       async {
          db.Product.Add(p)
          let task = db.SaveChangesAsync() 
          let! _ = Async.AwaitWaitHandle (task :> IAsyncResult).AsyncWaitHandle
          return ()
       } |> Async.StartAsTask :> _

return the task directly:

It makes more sense to return the task directly, as in the code above (that just mirrors the C# implementation) it just starts a thread, that starts another (db.SaveChanges()) waits for the changes to be saved and returns ... seems overkill to me.

This only would IMO only make sense if you would continue to work with the async-Workflow (remove the Async.StartAsTask - or if you would use the overload of AwaitWaitHandle that will timeout after some milliseconds).

    member this.SaveProduct(p : Product) = 
      db.Product.Add(p)
      db.SaveChangesAsync()

12 Comments

@abatishchev yes - sorry it did not see that it returned Async<bool>
But how is it efficient comparing to the C# implementation? Terrible not?
wait I just saw that the AwaitIAsyncResult does not work - you need AwaitWaitHandle - I`ll change it ASAP
Better handling of a Task without a return type is a planned change request for F#.
@abatishche efficient as in performant? well the question is why you want to put this into a async in the first place - you could just add p and then just return the task db.SaveChangesAsync() returns - but the same is true for the C# version
|

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.