我可以从Async API Controller方法调用“普通”静态方法吗?

时间:2015-12-02 13:11:13

标签: c# asynchronous asp.net-web-api

对于WebAPI和Async来说相对较新,所以请耐心等待....

我有一个WebAPI应用程序,其中包含许多操作。从本质上讲,这些调用SQL Server存储过程。

它一直运行良好,但我希望通过将方法转换为异步来提高效率和强大。

让我展示一个方法 - 因为它们都很相似......

旧版本

[System.Web.Http.HttpGet]
public A_Class MyAPIMethod(Guid b)
{
    using (SqlConnection DB = new SqlConnection(this.dbConnString))
    {
        return MyStaticHelper.A_Static_Method(DB, b);
    }
}

新版本(Async I think& hope)

[System.Web.Http.HttpGet]
public async Task<A_Class> MyAPIMethodAsync(Guid b)
{
    var db = new SqlConnection(this.dbConnString);
    try
    {
        var Result = await Task.Run(() => MyStaticHelper.A_Static_Method(db, b));
        return Result;
    }
    finally
    {
        db.Dispose();       
    }
}

我认为这一点没问题 - 我只是不确定我需要对我的静态助手方法做什么。 我需要将其转换为异步吗? 我打电话给这一切,这一切似乎都运转正常 - 我可以做一个完整性检查。 任何建议都赞赏....

静态辅助方法....

public static A_Class A_Static_Method(SqlConnection dbConn, Guid A_Param)
{
    SqlDataReader reader = null;
    try
    {
        try
        {
            if (dbConn.State != ConnectionState.Closed) dbConn.Close();

            using (var cmd = new SqlCommand("MyStoredProc", dbConn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@sp_Param", SqlDbType.UniqueIdentifier).Value = A_PAram

                dbConn.Open();
                reader = cmd.ExecuteReader();

                var A_Class = StaticFunctionToGetInstanceOfClassFromResults( reader );
                A_Class.rc = 1;
                return A_Class 
            }
        }
        catch (Exception)
        {
            //server error
            return new A_Class(A_ClassError.beApiError);
        }
    }
    finally
    {
        if (reader != null) reader.Close();
        dbConn.Close();
    }            
}

2 个答案:

答案 0 :(得分:1)

不,这种做法是错误的。通常,您应该避免在ASP.NET上使用Task.Run(以及队列工作到线程池的任何其他方法)。

您应该从最低级别开始,然后向上,而不是从控制器启动并“向下”工作。也就是说,首先检查静态方法并确定是否存在任何自然异步操作。这些通常是I / O.两个立即跳到我身边:opening the database connectionretrieving results from a query(很可能StaticFunctionToGetInstanceOfClassFromResults有更多)。您应该首先调用await 的那些,然后允许async自然地向您的控制器增长(编译器将指导您)。

另外,正如@StephenBrickner所评论的那样,您可能想退后一步,确定async是否会对您有所帮助。我有一个涵盖主要考虑因素的article on async ASP.NET。特别是,如果您的后端不能扩展(例如,如果它是单个SQL服务器实例,而不是像Azure SQL那样),那么(通常)没有必要使您的Web服务器扩展。

答案 1 :(得分:1)

  

var Result = await Task.Run(()=&gt; MyStaticHelper.A_Static_Method(db,b));

将工作传递给新线程。释放旧线程。在新线程中等待结果。现在一个线程可以继续进行。

如果您同时运行多个此类任务,这可能很有用,但您需要花费精力通过让线程执行某些操作来缓解线程。这都是成本而且没有收获。

异步获胜的地方是:

  1. 您同时有多个异步操作。
  2. 您有一个使用异步I / O的异步操作,以便完全释放调用线程。
  3. 第二个更重要,特别是在网络术语中。

    让我们先考虑你的“助手”方法。有些调用具有真正的异步等价物,因此我们可以创建一个真正的异步版本:

    public static async Task<A_Class> AStaticMethodAsync(SqlConnection dbConn, Guid A_Param)
    {
      try
      {
        if (dbConn.State != ConnectionState.Closed) dbConn.Close();
    
        using (var cmd = new SqlCommand("MyStoredProc", dbConn))
        {
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.Parameters.Add("@sp_Param", SqlDbType.UniqueIdentifier).Value = A_PAram
    
          await dbConn.OpenAsync();
          using(SqlDataReader reader = await cmd.ExecuteReader())
          {
            var A_Class = StaticFunctionToGetInstanceOfClassFromResults( reader );
            A_Class.rc = 1;
            return A_Class
          }
        }
      }
      catch (Exception)
      {
        //server error
        // Why are you wrapping an exception instead of just passing it up the stack? This is weird.
        return new A_Class(A_ClassError.beApiError);
      }
    }
    

    两个注释:

    1. 我将try…finally的{​​{1}}替换为更正常的SqlDataReader
    2. 大概using在datareader上调用StaticFunctionToGetInstanceOfClassFromResults,然后根据它构建一个对象。您可以添加一个调用Read()的异步版本,然后在此使用await ReadAsync()以获得更好的异步行为。
    3. 现在我们有一个异步方法,你的控制器可以是:

      var A_Class = await StaticFunctionToGetInstanceOfClassFromResultsAsync(reader)

      它真正受益于异步行为。