我正在尝试创建一个在两个其他系统之间同步的系统。数据
我使用VB处理ASP.NET Web窗体(无法更改)
主要系统功能将采用Web API的形式,由计划任务或其他事件(如网站表单或登录页面)触发。
这些函数中的每一个都调用系统1的REST API和系统2的REST API,操作数据,然后调用每个系统REST API以更新双方。
我选择使用ASP.NET Web Api 2作为主系统,使用HttpClient调用函数。
每天将有数十万笔交易。这是很多!所以我选择对HttpClient使用async / await方法(因为同时有很多长处理请求),但是ASP.NET似乎迫使我从下到上转换所有函数,包括Web Api函数和数据库访问到异步!
所以我最终将所有函数更改为Async,并且在调用它们的所有函数中,我使用Await来处理所有内容。
一切似乎都有效。我不知道我所做的是否正确,而且我发现我无法检查我的功能是否实际上是异步工作。
关于ConfigureAwait(False)
我添加了它,因为我没有与上下文相关的操作。如果有,我将删除此声明。我读过它建议在可能的时候使用它。 (我是对的吗?)
小评论: 为什么我的解决方案很好???我试图通过创建更多的工作线程来避免阻塞主线程。为什么这是更好的解决方案?我创造了许多等待每个人开始工作的人。这不是一个比共时解决方案更糟糕的做法吗?
有人可以告诉我,我在做什么是正确的,如果没有 - 请解释为什么或者是否有其他方法来解决这个问题?
以下是代码示例(仅限相关部分)。对你所看到的一点解释:
WebAPI控制器
Public Class WebApiController
Inherits ApiController
Public Async Function Login() As Threading.Tasks.Task(Of IHttpActionResult)
Dim result As MyResult= Await System1.DoSomething().ConfigureAwait(False)
End Function
System1类
Public Class System1
Public Shared Async Function DoSomething() As Task(Of MyResult)
Try
Using client As New HttpClient
client.BaseAddress = New Uri("blablabla")
Await SetCredentials(client).ConfigureAwait(False)
Dim response As HttpResponseMessage = Await client.GetAsync(urlParameters).ConfigureAwait(False)
... More code
End Using
Catch ex As Exception
End Try
End Function
Private Shared Async Function SetCredentials(client As HttpClient) As Task
Dim auth As BasicAuthenticationData = Await MyLoginManager.GetCredentials.ConfigureAwait(False)
Dim credentials As String = Cryptography.EncodeBase64(String.Format("{0}\{1}:{2}", auth.userName, auth.userPassword))
client.DefaultRequestHeaders.Add("Authorization", "Basic " & credentials)
... More code
End Function
End Class
MyLoginManager类
Public Class MyLoginManager
Public Shared Async Function GetCredentials() As Threading.Tasks.Task(Of BasicAuthenticationData)
Dim auth As New BasicAuthenticationData
Dim dbConn As String = DBConnection.GetConnection(True)
Dim q As String = "SELECT * FROM BlaBlaBla"
Using sdr As SqlDataReader = Await DBHelper.ExecuteReaderAsync(dbConn, q, Nothing).ConfigureAwait(False)
... More code
End Using
Return auth
End Function
End Class
DBHelper类
Public Class DBHelper
Public Shared Async Function ExecuteReaderAsync(ByVal dbConnection As String, ByVal commandText As String, ByVal params() As SqlParameter) As Threading.Tasks.Task(Of SqlDataReader)
Dim dbConnectionAsync As String = New SqlConnectionStringBuilder(dbConnection) With {
.AsynchronousProcessing = True
}.ToString
Dim objConn As New SqlConnection(dbConnectionAsync)
Dim oc As New SqlCommand(commandText, objConn)
Dim sdr As SqlDataReader
' Throws a custom exception if there is a problem
Try
Await oc.Connection.OpenAsync.ConfigureAwait(False)
sdr = Await oc.ExecuteReaderAsync(CommandBehavior.CloseConnection).ConfigureAwait(False)
Return sdr
Catch ex As Exception
End Try
End Function
End Class
答案 0 :(得分:2)
您应该向ConfigureAwait(false)
添加一个调用,以便在任何控制器操作产生的任何等待之后等待。
如Mister Epic said,如果您调用HttpContext.Current
,则会遗漏ConfigureAwait(false)
之类的内容,而您不应该对控制器操作方法执行此操作。但这也意味着每次你不进行上下文切换。
你应该做的是将控制器动作方法中的逻辑提取到他们自己的方法(最好是在他们自己的类中)并传递他们工作所需的每一件事。
所以,你唯一做错的就是在控制器动作方法中调用ConfigureAwait(false)
。
答案 1 :(得分:1)
不要在您的网络API项目中添加ConfigureAwait
。在库代码中使用它。
最重要的是,当您调用使用ConfigureAwait
的方法时,您将失去上下文。您的上下文包含会话等重要详细信息,因此您需要确保在调用使用HttpContext
的库代码之前从ConfigureAwait
获取所需的任何详细信息。