I am getting lots of delay when saving data in database. I have one exe (Deskptop Application) which reads data from serial port and push that entry in to database through web API service but my application get hangs on this line:
httpClient.PostAsync("api/MyController/Save", httpConent).Result;
This exe is responsible to call my web API service method and save data to my database.
This is my code:
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);
}
}
}
My web api method:
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;
}
}
}
I haven't work much on Windows application so I am not understanding why my application is hanging.
Note: data will be continuously coming to my serial port so saving data through web service should not disturb _serialPort_DataReceived
event.
What is an async hang? Many modern ASP.NET applications are developed using the asynchronous task pattern. This pattern helps reduce the thread pool exhaustion issues traditionally associated with synchronous applications. That said, async ASP.NET applications can and do still experience hangs!
Request a Product. For example, client.GetAsync<Product> ("api/products/4");. In Visual Studio, create a new Windows console app named HttpClientSample and paste in the following code: The preceding code is the complete client app. RunAsync runs and blocks until it completes.
Most HttpClient methods are async, because they perform network I/O. All of the async tasks are done inside RunAsync. Normally an app doesn't block the main thread, but this app doesn't allow any interaction. Use NuGet Package Manager to install the Web API Client Libraries package.
When the async method attempts to continue, it first re-enters the captured "context" (in this case, an ASP.NET SynchronizationContext ). The ASP.NET SynchronizationContext only permits one thread in the context at a time, and there is already a thread in the context - the thread blocked on Task.Result. Use async all the way down.
This is a summary of my comments beneath the OP's question
You are calling an asynchronous method synchronously. That will cause the current thread to block. Get rid of the .Result
and alter the rest of the code accordingly (like including async
and await
there too).
e.g. change this line
responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;
...to:
responseMessage = await httpClient.PostAsync("api/MyController/Save", httpConent);
Your method signature will need to be changed as follows:
public async Task CallWebservice(string xmlRequest)
{
}
Any method that calls it will also need to be async
and use await
for example your _spManager_NewSerialDataRecieved()
method.
Note it has been changed from void
to async void
. Note too the await
prior to CallWebservice()
.
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)
}
}
Because the above method is an event handler it is fine for the method to be async void
. Generally you want to avoid async void
in non event handler code. For more info see this brilliant article by Mr Stephen Cleary.
Is this the only problem sir??
You should fix your async Save()
method on the server too as it also has a .Result()
. That will block the current thread on the server. Prefix it with a await
. Generally you want to avoid .Result
as a means to wait for the task to complete. It is safe to use as a means to obtain the result after you have awaited it, but there are more elegant ways to await and get the result in a single line of code. e.g. x = await FooAsync();
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With