Adnan Yaseen Adnan Yaseen - 2 months ago 14
C# Question

Async Await Few Confusions

As experiencing the new async & Await features of 4.5 I want to clear some confusions before going any further. I have been reading different article and also different question on SO and it help me undertands how Async and Await works. I will just try to put my understanding and confusions here and will appreciate if someone code educate me and other people who are looking for same things. I am discussing this in very simple wordings.

So Async is used so that compiler know that method marked by Async contains Await operation (Long operation). Latest framework contains different new builtin methods for Async Operations.

The builtin Async functions like connection.OpenAsync, ExecuteScalarAsync etc are used with Await keyword. I don't know the inner working of these Async Methods but my strong guess is that under the hood they are using Tasks.

Can I make this as general rule that Await will be with any method which implements Task. So if I need to create my own method which is performing long operation then will I create it as Task and when it is called I will use Await Keyword with it?

Second most important thing is that what is the rule of thumb of creating a method as Async or creating it as task. For example,

public void SampleMain()
{
for (int i = 1; i <= 100; i++)
{
DataTable dt = ReadData(int id);
}
}

public DataTable ReadData(int id)
{
DataTable resultDT = new DataTable();

DataTable dt1 = new DataTable();
// Do Operation to Fill DataTable from first connection string
adapter.Fill(dt1);

DataTable dt2 = new DataTable();
// Do Operation to Fill DataTable from first connection string
adapter.Fill(dt2);

// Code for combining datatable and returning the resulting datatable
// Combine DataTables
return resultDT;
}

public string GetPrimaryConnectionString()
{
// Retrieve connection string from some file io operations
return "some primary connection string";
}

public string GetSecondaryConnectionString()
{
// Retrieve connection string from some file io operations
return "some secondaryconnection string";
}


Now this is a very simple scenario that I have created based on some real world application I worked in past. So I was just wondering how to make this whole process Async.

Should I make GetPrimaryConnectionString and GetSecondaryConnectionString as Tasks and Await them in ReadData. Will ReadData be also a Task? How to call ReadData in the SampleMain function?

Another way could be to create a Task for ReadData in SampleMain and run that Task and skip converting other methods as Task. Is this the good approach? Will it be truly Asynchronous?

Answer

So Async is used so that compiler know that method marked by Async contains Await operation

The async is used so that the compiler will have an indication to create a state-machine out of the method. An async method can have no await, and still work, though it will execute completely synchronously.

The builtin Async functions like connection.OpenAsync, ExecuteScalarAsync etc are used with Await keyword. I don't know the inner working of these Async Methods but my strong guess is that under the hood they are using Tasks.

Task is a promise of work to be completed in the future. There are a couple of ways to create a Task. But, Task isn't the only thing that can be represent an asynchronous operation. You can create an awaitable yourself if you wanted, all it needs it to implement a GetAwaiter method which returns a type implementing INotifyCompletion.

If you want to know how a method is implemented in the framework, you can view the source. In this particular case, they use TaskCompletionSource<T>.

Should I make GetPrimaryConnectionString and GetSecondaryConnectionString as Tasks and Await them in ReadData. Will ReadData be also a Task? How to call ReadData in the SampleMain function?

There is nothing inherently asynchronous about retrieving a connection string. You usually (not always) use async-await with naturally async IO operations. In this particular case, the only actual async operation is ReadData, and if you want to make it asynchronous, you can use SqlDataReader, which exposes async methods.

An example, taken from the ADO.NET teams blog:

public static async Task<Product> GetProductAndReviewsAsync(
            int productID, int reviewsToGet)

{
    using (SqlConnection connection = new SqlConnection(ConnectionString))
    {
        await connection.OpenAsync();
        const string commandString = GetProductByIdCommand + ";" 
                                    + GetProductReviewsPagedById;

        using (SqlCommand command = new SqlCommand(commandString, connection))
        {
            command.Parameters.AddWithValue("productid", productID);
            command.Parameters.AddWithValue("reviewStart", 0); 
            command.Parameters.AddWithValue("reviewCount", reviewsToGet);
            using (SqlDataReader reader = await command.ExecuteReaderAsync())
            {
                if (await reader.ReadAsync())
                {
                    Product product = GetProductFromReader(reader, productID);
                    if (await reader.NextResultAsync())
                    {
                        List<Review> allReviews = new List<Review>();
                        while (await reader.ReadAsync())

                        {
                            Review review = GetReviewFromReader(reader);
                            allReviews.Add(review);
                        }
                        product.Reviews = allReviews.AsReadOnly();
                        return product;
                    }
                    else
                    {
                        throw new InvalidOperationException(
                            "Query to server failed to return list of reviews");
                    }
                }
                else
                {
                    return null;
                }
            }
        }
    }
}
Comments