4

Starting with a synchronous I/O bound method (below), how do I make it asynchronous using async/await?

public int Iobound(SqlConnection conn, SqlTransaction tran)
{
    // this stored procedure takes a few seconds to complete
    SqlCommand cmd = new SqlCommand("MyIoboundStoredProc", conn, tran);
    cmd.CommandType = CommandType.StoredProcedure;

    SqlParameter returnValue = cmd.Parameters.Add("ReturnValue", SqlDbType.Int);
    returnValue.Direction = ParameterDirection.ReturnValue;
    cmd.ExecuteNonQuery();

    return (int)returnValue.Value;
}

MSDN examples all presume the preexistence of an *Async method and offer no guidance for making one yourself for I/O-bound operations.

I could use Task.Run() and execute Iobound() within that new Task, but new Task creation is discouraged since the operation is not CPU-bound.

I'd like to use async/await but I'm stuck here on this fundamental problem of how to proceed with the conversion of this method.

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
MikeZ
  • 1,155
  • 1
  • 13
  • 20

1 Answers1

7

Conversion of this particular method is pretty straight-forward:

// change return type to Task<int>
public async Task<int> Iobound(SqlConnection conn, SqlTransaction tran) 
{
    // this stored procedure takes a few seconds to complete
    using (SqlCommand cmd = new SqlCommand("MyIoboundStoredProc", conn, tran)) 
    {
        cmd.CommandType = CommandType.StoredProcedure;
        SqlParameter returnValue = cmd.Parameters.Add("ReturnValue", SqlDbType.Int);
        returnValue.Direction = ParameterDirection.ReturnValue;
        // use async IO method and await it
        await cmd.ExecuteNonQueryAsync();
        return (int) returnValue.Value;
    }
}
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Evk
  • 98,527
  • 8
  • 141
  • 191
  • @MikeZ Then you usually don't really need to make this method async. Async is for async IO (as you seem to be aware), and all IO has async version nowadays (files, sockets, databases, web requests and so on). If you have counter example - please provide one. – Evk Nov 26 '17 at 22:38
  • Thanks! But I should have picked a different example. In this case, there is a pre-existing *Async method, ExecuteNonQueryAsync(), assume a different body and no pre-existing *Async method for the I/O-bound call. How would one convert Iobound() to IoboundAsync() without wrapping the synchronous code in Task.Run(), which is not recommended for I/O-bound code? – MikeZ Nov 26 '17 at 22:40
  • 2
    @MikeZ I've answered above. Please provide an example of such IO that does not have async version. If you mean some another synchronous IO method written by you - you should convert it to async first. So async should go all the way down. – Evk Nov 26 '17 at 22:43
  • "All IO has async version nowadays" Hmm. I didn't know that. Maybe that's the piece I'm missing. – MikeZ Nov 26 '17 at 22:44
  • (sorry for the formatting, I haven't figured out how to format in comments yet) I have a 3rd party HTTP REST service exposing a synchronous WCF contract method: **public string Method();** On the client I have synchronous **return Channel.Method();** I could make this async with **return Task.Run(() => Channel.Method();** but Task.Run is not advised for I/O bound. What other options are there? – MikeZ Dec 01 '17 at 07:44
  • 1
    @MikeZ you need to generate asynchronous client code. Even if WCF service is synchronous on server - client may choose to call it asynchronously (at client side). See here: https://learn.microsoft.com/en-us/dotnet/framework/wcf/synchronous-and-asynchronous-operations. So your goal is create/generate Channel.MethodAsync() and of course without any Task.Run – Evk Dec 01 '17 at 07:53
  • The link contains sample code using Task.Factory.StartNew() at the service side, I could move that to the client side, but isn't that just a different way to code Task.Run(), which I'm trying to avoid? – MikeZ Dec 01 '17 at 14:27
  • @MikeZ no that's not what I mean. You have third party WCF service. How did you generate code for client side for that (if any)? Where did you get that `public string Method()`? – Evk Dec 01 '17 at 14:30
  • I have the service contract interface which specifies **public string Method();** and from that I wrote the client-side code by instantiating a ClientBase of that interface type. – MikeZ Dec 03 '17 at 22:35
  • Can I just change the client side from **public string Method() { return Channel.Method(); } :** to **public async Task MethodAsync() { return Channel.Method(); }** to get async behavior at the client? – MikeZ Dec 03 '17 at 23:09