2015-12-02 36 views
0

相對較新的WebAPI和異步所以請容忍我所說的「正常」的靜態方法....我可以從一個異步API控制方法

我有一些在其操作的應用程序的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); 
    } 
} 

新版本(異步我認爲&希望)

[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(); 
    }    
} 
+4

爲什麼你認爲在另一個線程上運行該方法並等待結果比同步運行更「高效且健壯」?想象一下,我克隆了你並要求你做一些事情。爲什麼要讓克隆人完成任務並等待它完成比只是自己完成任務更好? –

+0

@Daniel Kelley說得很好。 AntDC,異步編程有開銷,應該根據需要使用,而不僅僅是使用它。在這種情況下,因爲你在這裏所做的只是運行一個存儲過程,所以沒有必要。如果這是一個長期運行的程序,我會從那裏開始,並考慮讓它更有效率。我也不建議在靜態類中有任何保持狀態的數據庫連接。 –

+0

我沒有明說我們這個相對較新的給我.... 我想不會阻塞線程作爲我的API可能有大量的併發請求的.... 無論如何,所以怎麼樣,如果我不得不從我的API調用外部API。 異步操作會更適用嗎? 此外,dbConnString是我的BaseController類的一個屬性,我將它傳遞給我的靜態方法。 – AntDC

回答

1

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

通行證的工作到一個新的線程。釋放舊線程。在新線程中等待結果。現在一個線程可以繼續在這裏停止。

如果你同時運行幾個這樣的任務,但是你花精力由具有螺紋,以減輕線程做的東西,這非常有用。這是所有成本和收益。

哪裏異步勝是:

  1. 您在同一時間有一個以上的異步操作。
  2. 您有一個使用異步I/O的異步操作,以便完全釋放調用線程。

第二個是更重要的,尤其是在網絡方面。

讓我們首先考慮你的「幫手」方法。有在電話有真正的異步等價物,所以我們可以創建一個真正的異步版本:

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使用SqlDataReader有一個比較正常的周圍using
  2. 推測StaticFunctionToGetInstanceOfClassFromResults呼籲的DataReader Read()然後生成基於該一個對象。您可以添加一個異步版本,調用await ReadAsync(),然後在此處使用var A_Class = await StaticFunctionToGetInstanceOfClassFromResultsAsync(reader)以獲得更好的異步行爲。現在

,我們已經異步方法,您的控制器可以是:

[System.Web.Http.HttpGet] 
public async Task<A_Class> MyAPIMethodAsync(Guid b) 
{ 
    using (SqlConnection DB = new SqlConnection(this.dbConnString)) 
    { 
    return await MyStaticHelper.AStaticMethodAsync(DB, b); 
    } 
} 

它真正從異步行爲的好處。

+0

謝謝喬恩。 你已經改變了我對此的理解。 順便說一句 - 我的捕獲不是最優雅的,但在這種情況下做的工作。發回響應代碼爲「API錯誤」的對象。 謝謝收穫。 – AntDC

+0

我強烈地考慮看看我是否可以將它變成捕獲異常的東西,並且如果我是你的話,它會更接近API表面返回「API錯誤」響應。 –

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服務器。

+0

謝謝斯蒂芬, 是的 - 我已經採取了所有的答覆,並在我真正需要異步時「加載我的槍」。 也就是說 - 當我有多個任務可以同時獨立運行時運行。 作爲一個自己的練習,我已經使我的一個簡單的方法有一個異步選項,只是爲了練習。 謝謝 – AntDC