0

I have tree layer of classes to get data from database and serve it within an Action. I get data from database old way, using SqlParameter classes.

I need to/want to reconstruct this methods to asynchronous methods. What truly I want is to learn how to make these synchronous methods to Asynchronous. And make them work different threads and use them across entire app.

What I don't want to do is to Use Ado.Net or using ready methods like HttpClient.GetAsync

I read this question: Calling async methods from non-async code but couldn't apply to my structure and couldn't be sure if it is work properly, avoids deadlocks.

My old structure is like that:

My Action in My BlockController:

public ActionResult Index()
{
    return View(new BlockDb().Select());
}

The Select Method in BlockDb class:

    public List<Block> Select()
        {
            SqlDataReader dr = DbJobs.ExecuteReader("BlockSelect");
            List<Block> list = new List<Block>();
            Block cs;
            while (dr.Read())
            {
                cs = new Block();
                cs.ID = Convert.ToInt32(dr["ID"]);
                cs.Name= Convert.ToString(dr["Name"]);
                cs.Dt = Convert.ToDateTime(dr["Dt"]);
                cs.Ok = Convert.ToBoolean(dr["Ok"]);
                list.Add(cs);
            }

            dr.Close();
            dr.Dispose();
            return list;
        }

And the last one ExecuteReader Method in DbJobs static class:

    public static SqlDataReader ExecuteReader(string ProcName, params SqlParameter[] prmtr)
    {
        SqlDataReader Result = null;
        SqlConnection connect = new SqlConnection(cnn);
        try
        {
            SqlCommand command = new SqlCommand(ProcName, connect);
            command.CommandType = CommandType.StoredProcedure;

            foreach (SqlParameter item in prmtr)
                command.Parameters.Add(item);

            if (connect.State == ConnectionState.Closed)
                connect.Open();

            Result = command.ExecuteReader(CommandBehavior.CloseConnection);
            //connect.Close();
            //connect.Dispose();
        }
        catch { }

        return Result;
    }

I can't be sure bu the result of an Action could be like that:

public async Task<ActionResult> Index()
{
    return View(await new BlockDb().SelectAsync());
}

How can I achive that?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ebleme
  • 287
  • 2
  • 13

1 Answers1

3

To convert code to async, it's best to start at the lowest level and work your way up. So, starting with DbJobs, change every method to its asynchronous equivalent and await the result:

public static SqlDataReader ExecuteReader(string ProcName, params SqlParameter[] prmtr)
{
  SqlDataReader Result = null;
  SqlConnection connect = new SqlConnection(cnn);
  try
  {
    SqlCommand command = new SqlCommand(ProcName, connect);
    command.CommandType = CommandType.StoredProcedure;

    foreach (SqlParameter item in prmtr)
      command.Parameters.Add(item);

    if (connect.State == ConnectionState.Closed)
      await connect.OpenAsync(CancellationToken.None);

    Result = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection);
  }
  catch { }

  return Result;
}

This will give you a compiler error telling you to change your DbJobs.ExecuteReader signature. So, if you do what the compiler error tells you to do, you'll end up with:

public static async Task<SqlDataReader> ExecuteReaderAsync(string ProcName, params SqlParameter[] prmtr)

Now you'll get compiler errors for all the old code that called DbJobs.ExecuteReader, e.g., BlockDb.Select. So, change those to use asynchronous methods, too:

public List<Block> Select()
{
  SqlDataReader dr = await DbJobs.ExecuteReaderAsync("BlockSelect");
  List<Block> list = new List<Block>();
  Block cs;
  while (await dr.ReadAsync(CancellationToken.None))
  {
    cs = new Block();
    cs.ID = Convert.ToInt32(dr["ID"]);
    cs.Name= Convert.ToString(dr["Name"]);
    cs.Dt = Convert.ToDateTime(dr["Dt"]);
    cs.Ok = Convert.ToBoolean(dr["Ok"]);
    list.Add(cs);
  }

  dr.Close();
  dr.Dispose();
  return list;
}

Again, you'll get a compiler error telling you how to change BlockDb.Select:

public async Task<List<Block>> SelectAsync()

And finally, you'll do the same for Index:

public async Task<ActionResult> Index()
{
  return View(await new BlockDb().SelectAsync());
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thanks a lot. I didn't know it was too easy like that. Or it became easier after this awesome explanation of yours. There were hundereds of error like you said. It was a quite challange to fix them all. But Visual Studio didn't tell me to change signature. It didn't even make a propese. I don't want to use Async suffix anyway. – Ebleme Feb 14 '19 at 15:38
  • 1
    @Ebleme: If it's too much to change all at once, you can have both synchronous and asynchronous APIs together (temporarily) by using the [bool argument hack](https://msdn.microsoft.com/en-us/magazine/mt238404.aspx). – Stephen Cleary Feb 14 '19 at 16:08
  • Thank you for the article. This is what I need, really. I am going to read it. – Ebleme Feb 14 '19 at 17:47