c# - 我的桌面应用程序在使用 async 和 await 调用 Web API 服务时挂起

标签 c# winforms asynchronous asp.net-web-api async-await

我在数据库中保存数据时遇到很多延迟。我有一个 exe(桌面应用程序),它从串行端口读取数据并通过 Web API 服务将该条目推送到数据库,但我的应用程序在这一行挂起:

httpClient.PostAsync("api/MyController/Save", httpConent).Result;

这个 exe 负责调用我的 web API 服务方法并将数据保存到我的数据库。

这是我的代码:

void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
       {
           int dataLength = _serialPort.BytesToRead;
           byte[] data = new byte[dataLength];
           int nbrDataRead = _serialPort.Read(data, 0, dataLength);

           if (nbrDataRead == 0)
               return;

           // Send data to whom ever interested
           if (NewSerialDataRecieved != null)
           {
               NewSerialDataRecieved(this, new SerialDataEventArgs(data));
           }
       }

 void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
        {
            if (this.InvokeRequired)
            {
                // Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
                //// Fired-off asynchronously; let the current thread continue.
                this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
                return;
            }
            //data is converted to text
            string str = Encoding.ASCII.GetString(e.Data);
            if (!string.IsNullOrEmpty(str))
            {
               CallWebservice(str)
            }
        }

public void CallWebservice(string xmlRequest)
        {
            using (var httpClient = new HttpClient())
            {
               httpClient.BaseAddress = new Uri("WebService Url");
                httpClient.DefaultRequestHeaders.Accept.Clear();
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                StringContent httpConent = new StringContent(xmlRequest, Encoding.UTF8);
                HttpResponseMessage responseMessage = null;
                try
                {
                    responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;
                }
                catch (Exception ex)
                {
                    if (responseMessage == null)
                    {
                        responseMessage = new HttpResponseMessage();
                    }
                    responseMessage.StatusCode = HttpStatusCode.InternalServerError;
                    responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
                }
            }
        }

我的 web api 方法:

 public async Task<HttpResponseMessage> Save(HttpRequestMessage request)
        {
               var requestdata = request.Content.ReadAsStringAsync().Result;//extract Users Id's from this
                var users=context.User.Where(t => (t.Stats == userId1) || (t.Stats == userId2)).ToList();
                var objUsersMapping= new UsersMapping();
                objUsersMapping.Work1 = users[0].Work1;
                objUsersMapping.Work2 = users[1].Work1;
                await this.SaveUsersMapping(objUsersMapping); 
        }

 public async Task<UsersMapping> SaveUsersMapping(UsersMapping objUsersMapping)
        {
            using (var context = new MyEntities())
            {
                try
                {
                    context.UsersMapping.Add(objUsersMapping);
                    await context.SaveChangesAsync();
                    return objUsersMapping;
                }
                catch (Exception foExe)
                {
                    return null;
                }
            }
        }

我在 Windows 应用程序上的工作不多,所以我不明白为什么我的应用程序挂起。

注意:数据将不断进入我的串行端口,因此通过网络服务保存数据不应干扰 _serialPort_DataReceived 事件。

最佳答案


这是我在 OP 问题下的评论摘要


您正在同步调用异步方法。这将导致当前线程阻塞。去掉 .Result 并相应地更改其余代码(例如也包括 asyncawait)。

例如改变这一行

responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;

...到:

 responseMessage = await httpClient.PostAsync("api/MyController/Save", httpConent);

您的方法签名需要更改如下:

public async Task CallWebservice(string xmlRequest)
{

}

任何调用它的方法也需要是 async 并使用 await 例如你的 _spManager_NewSerialDataRecieved() 方法。

请注意,它已从 void 更改为 async void。还要注意 CallWebservice() 之前的 await

async void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
    {
        if (this.InvokeRequired)
        {
            // Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
            //// Fired-off asynchronously; let the current thread continue.
            this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
            return;
        }
        //data is converted to text
        string str = Encoding.ASCII.GetString(e.Data);
        if (!string.IsNullOrEmpty(str))
        {
           await CallWebservice(str)
        }
    }

关于 async void 的注释

因为上面的方法是一个事件处理程序,所以该方法可以是 async void。通常,您希望避免在 事件处理程序代码中使用async void。有关详细信息,请参阅 this brilliant article Stephen Cleary 先生。

Is this the only problem sir??

您也应该修复服务器上的 async Save() 方法,因为它也有一个 .Result()。这将阻塞服务器上的当前线程。用 await 作为前缀。通常,您希望避免使用 .Result 作为等待任务完成的方式。在等待之后用作获取结果的方法是安全的,但还有更优雅的方法来等待并在一行代码中获取结果。例如x = await FooAsync();

关于c# - 我的桌面应用程序在使用 async 和 await 调用 Web API 服务时挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37248388/

相关文章:

java - FragmentPagerAdapter - 设置异步计数和标题

c# - C# 中的方法可以覆盖另一个方法并同时是虚拟的吗?

c# - 解码 Opera 缓存内容

vb.net - 使用 RingCentral URI rcmobile 时停止 RingCentral 软电话自动拨号

c# - 在窗体上绘制自定义图标

html - 异步加载图像

c# - 等待与请求序列号匹配的响应

c# - 流利;用于创建 session 的 SessionSource 或 SessionFactory?

c# - ASPX 身份验证 cookie 过期时间始终为 30 分钟

c# - 在后台工作程序中向 GUI 添加控件