简体   繁体   中英

How to implement interface having method returning Task (non-generic) in F#

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>

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)

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM