user2861226
user2861226

Reputation: 169

Asynchronous function working as synchronous call

Below is the code I am using. Intended to make it an asynchronous call to time consuming function.

async Task<DataSet> GetDataAsync()
    {
        System.Threading.Thread.Sleep(5000);
        SqlDataAdapter adpator = new SqlDataAdapter("Select * from table1;select * from table2;select * from table3;select * from table4"
            , @"Data Source=ANKIT\SQLEXPRESS;Initial Catalog=IM_DB;Integrated Security=True");
        DataSet ds = new DataSet();
        adpator.Fill(ds);
        return ds;
    }

    protected async void btnFillData_Click(object sender, EventArgs e)
    {
        lblStatus.Text = "Going to run a blocking thread....";
        Task<DataSet> dsAsync = GetDataAsync();
        lblStatus.Text = "Going to await the same......";
        DataSet ds = await dsAsync;
        lblStatus.Text = "released from await";
        gvIngredient.DataSource = ds.Tables[0];
        gvIngredient.DataBind();
    }

Function GetDataAsync working as a synchronous call. I want it to be asynchronous as it is having a database call to fetch thousands of records which is time consuming.

There is something that I missed in the async model on asp.net.

Upvotes: 1

Views: 302

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 457302

Function GetDataAsync working as a synchronous call.

Yes. In fact, there is a compiler warning that will tell you GetDataAsync will run synchronously.

It is possible to make it asynchronous, but not using the DataSet.Fill approach. That is a Very Old way of doing things, and was not updated to use the new asynchronous APIs. You'd have to use something like Entity Framework 6 or asynchronous DbCommands.

But before you head down that path...

protected async void btnFillData_Click(object sender, EventArgs e)
{
  lblStatus.Text = "Going to run a blocking thread....";
  Task<DataSet> dsAsync = GetDataAsync();
  lblStatus.Text = "Going to await the same......";
  DataSet ds = await dsAsync;
  lblStatus.Text = "released from await";
  gvIngredient.DataSource = ds.Tables[0];
  gvIngredient.DataBind();
}

This code shows a misunderstanding of how ASP.NET works. When the button is clicked, there is a single HTTP request sent to the ASP.NET server, and the server can only send a single HTTP response. This response is only sent at the end of the click event method. So, setting lblStatus.Text multiple times won't have any effect - only the last update will be seen by the user.

Put another way, await on ASP.NET only yields to the ASP.NET runtime; it does not yield to the client (browser). The HTTP response is not sent until all awaits have completed.

So, what you're really asking is how to start a long-running operation in ASP.NET and then have the client update later with progress or completion information. For this you'll need a different technology, such as SignalR, AJAX, or UpdatePanel.

Upvotes: 0

dyatchenko
dyatchenko

Reputation: 2343

The first problem:

The modifier async doesn't make your method asynchronous, it just gives you an opportunity to use the await keyword inside. Thus, the function GetDataAsync is not asynchronous - it uses the same thread which it has been called. To make this function asynchronous you have to use the Task class and the StartNew method like this:

Task<DataSet> GetDataAsync()
{
    Function<DataSet> func = () =>
    {
        System.Threading.Thread.Sleep(5000);
        SqlDataAdapter adpator = new SqlDataAdapter("Select * from table1;select * from table2;select * from table3;select * from table4"
               , @"Data Source=ANKIT\SQLEXPRESS;Initial Catalog=IM_DB;Integrated Security=True");
        DataSet ds = new DataSet();
        adpator.Fill(ds);
        return ds;
    };

    return Task.Factory.StartNew(func);
}

Check this really useful article up to understand how async\await works.

The second:

Your code keeps the SqlDataAdapter in the memory after each call of the GetDataAsync. This behavior leads to the memory leaks. You'd better use the using statement:

DataSet ds = new DataSet();
using(var adapter = new SqlDataAdapter(query, connectionString))
    adapter.Fill(ds);

Hope this helps.

Upvotes: 2

Related Questions