对于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);
}
}
[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();
}
}
答案 0 :(得分:1)
不,这种做法是错误的。通常,您应该避免在ASP.NET上使用Task.Run
(以及队列工作到线程池的任何其他方法)。
您应该从最低级别开始,然后向上,而不是从控制器启动并“向下”工作。也就是说,首先检查静态方法并确定是否存在任何自然异步操作。这些通常是I / O.两个立即跳到我身边:opening the database connection和retrieving 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));
将工作传递给新线程。释放旧线程。在新线程中等待结果。现在一个线程可以继续进行。
如果您同时运行多个此类任务,这可能很有用,但您需要花费精力通过让线程执行某些操作来缓解线程。这都是成本而且没有收获。
异步获胜的地方是:
第二个更重要,特别是在网络术语中。
让我们先考虑你的“助手”方法。有些调用具有真正的异步等价物,因此我们可以创建一个真正的异步版本:
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);
}
}
两个注释:
try…finally
的{{1}}替换为更正常的SqlDataReader
。using
在datareader上调用StaticFunctionToGetInstanceOfClassFromResults
,然后根据它构建一个对象。您可以添加一个调用Read()
的异步版本,然后在此使用await ReadAsync()
以获得更好的异步行为。现在我们有一个异步方法,你的控制器可以是:
var A_Class = await StaticFunctionToGetInstanceOfClassFromResultsAsync(reader)
它真正受益于异步行为。