从多个SQL异步填充对象的属性C#

时间:2019-06-20 14:53:03

标签: c# object asynchronous properties

我有一个带有多个属性的对象,每个属性都由单独的SQL select语句填充。我想同时执行select语句,但是等待返回整个对象,直到每个属性都有数据为止。我对使用C#进行异步处理非常陌生,但是我很难将其包裹起来。这是我如何同步完成此操作的示例。

public MyObj GetMyObj()
{
    MyObj obj = new MyObj();

    using (SqlConnection con = new SqlConnection("connectionString"))
    {
        con.Open();

        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.Connection = con;
            cmd.CommandText = "Select ...";
            cmd.CommandType = CommandType.Text;

            obj.Prop1 = cmd.ExecuteScalar();
        }
    }

    using (SqlConnection con = new SqlConnection("connectionString"))
    {
        con.Open();

        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.Connection = con;
            cmd.CommandText = "Select ...";
            cmd.CommandType = CommandType.Text;

            obj.Prop2 = cmd.ExecuteScalar();
        }
    }

    return obj;
}

理想情况下,我想为每个选择块创建任务方法,并等待完成后返回完整的对象(我的基本理解来自Microsoft Async article):

public MyObj GetMyObj()
{
    MyObj obj = new MyObj();

    var prop1Task = GetProp1();
    var prop2Task = GetProp2();

    var allTasks = new List<Task>{prop1Task, prop2Task};
    while (allTasks.Any())
    {
        Task finished = await Task.WhenAny(allTasks);
        if (finished == prop1Task)
        {
            allTasks.Remove(prop1Task);
            obj.Prop1 = await prop1Task;
        } else if (finished == prop2Task)
        {
            allTasks.Remove(prop2Task);
            obj.Prop2 = await prop2Task;
        } else
                allTasks.Remove(finished);
    }

    return obj;
}

private async Task<int> GetProp1()
{
    int prop1 = 0;
    using (SqlConnection con = new SqlConnection("connectionString"))
    {
        con.Open();

        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.Connection = con;
            cmd.CommandText = "Select ...";
            cmd.CommandType = CommandType.Text;

            prop1 = cmd.ExecuteScalar();
        }
    }

    return prop1;
}

在构建GetProp1()方法时,我收到一个智能提示,即“ 该异步方法缺少'await'运算符,将同步运行

我可以正确执行各个“ GetProp”方法吗?智能警告是否错误?我正在执行几个select语句,因此将此操作更改为异步运行将大大提高性能。

在此先感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

GetProp1中没有任何异步执行。警告的含义与所讲的完全相同。有一个标记为async的方法不会异步执行任何操作,因此它将同步执行。重写GetPropX方法以在SqlConnection / SqlCommand上使用异步方法。

private async Task<int> GetProp1()
{
    int prop1 = 0;
    using (SqlConnection con = new SqlConnection("connectionString"))
    {
        await con.OpenAsync();

        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.Connection = con;
            cmd.CommandText = "Select ...";
            cmd.CommandType = CommandType.Text;

            prop1 = await cmd.ExecuteScalarAsync();
        }
    }

    return prop1;
}

也就是说,如果各种select语句都针对同一个数据库执行,则可以一次执行多个语句,请使用ExecuteReaderNextResult

像这样...

private async Task<MyObj> GetMyObjAsync()
{
   MyObj obj = new MyObj();
   using (SqlConnection con = new SqlConnection("connectionString"))
   {
      await con.OpenAsync();
      string sqlStatements = @"
SELECT Prop1 FROM Table1;
SELECT Prop2 FROM Table2;
SELECT Prop3 FROM Table3;
--etc";

      using (SqlCommand cmd = new SqlCommand(sqlStatements, con))
      using (SqlDataReader reader = await cmd.ExecuteReaderAsync())
      {
         if (await reader.ReadAsync()) 
         {
            obj.Prop1 = Convert.ToInt32(reader["Prop1"]);
         }
         await reader.NextResultAsync();
         if (await reader.ReadAsync()) 
         {
            obj.Prop2 = Convert.ToInt32(reader["Prop2"]);
         }
         await reader.NextResultAsync();
         if (await reader.ReadAsync()) 
         {
            obj.Prop3 = Convert.ToInt32(reader["Prop3"]);
         }
         // etc...
      }
   }
   return obj;
}

这样,您只有一个与数据库的连接,而不必为MyObj上的每个属性创建单独的连接。