I have a ASP.Net Core 2.0 webapi code that looks like following
public class TestController : Controller
{
private readonly ISampleRepository _sampleRepository = new SampleRepository();
[HttpPost("{id}")]
public async void Post([FromRoute]int id, IEnumerable<IFormFile> files, [FromForm] NotifyViewModel model)
{
// get data from DB using async
var dbData = await _sampleRepository.GetAsync(id);
//check if email alert is enabled
if(dbData.IsEmailEnabled)
{
//create MailMessage
var mailMessage = new MailMessage() {
//options here
};
foreach (var formFile in files)
{
mailMessage.Attachments.Add(new Attachment(formFile.OpenReadStream(), formFile.FileName, formFile.ContentType));
}
// send email
_emailHelper.SendEmail(mailMessage);
}
}
}
public class SampleRepository
{
public async Task<SampleData> GetAsync(int id)
{
//get data using Dapper & return
var result = await con.QuerySingleAsync<SampleData>(sql, new {id = id}, null, null, CommandType.Text);
return result;
}
}
When I post data to this endpoint I get exception saying 'Cannot access disposed object.' (Refer screenshot)
However when I make DB operation synchronous by doing following code changes, somehow it works without any error.
// Removed
// var dbData = await _sampleRepository.GetAsync(id);
// Newly added. Made DB operations synchronous
var dbData = _sampleRepository.GetAsync(id).Result;
Can someone please help me understand this behavior? Why HTTP posted files gets disposed when async operation is involved?
Because your method is async void it immediately returns as soon as it hits the first await. By the time the code accesses request data, the request has already ended.
Change the signature of Post from async void Post to async Task Post as async Task is the asynchronous analogue of the synchronous void.
Related
I want to call the Web API through C# Task, but I am unable to get the returned result, although it does jump to the URL that I pointed out to get the values.
Do I implement the async and await method incorrectly?
Below is my code:
Web API with the route prefix of:
[RoutePrefix("api/values")]
The method is as below inside the Web API:
[Route("Developer")]
[HttpGet]
public Task<List<string>> Developer()
{
var developer = new List<string> { "Developer", "Developer" };
return Task.FromResult(developer);
}
Controller
private async Task<List<string>> Developer()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:52717/");
var response = await client.GetAsync("api/values/Developer");
if (response.IsSuccessStatusCode)
return new List<string>();
throw new Exception("Unable to get the values");
}
}
public ActionResult Index()
{
Task.WaitAll(Developer());
return View();
}
Whenever I launch the browser, it goes into the Index(), and goes to the Developer(), however, it keeps stuck and loading all the way up until the var response = await client.GetAsync("api/values/Developer") gets called and goes through all the way to return Task.FromResult(developer);, and it keeps stuck and loading all the way.
Anyone knows on how to make the Web API goes back to the caller?
Any help would be much appreciated.
Thanks
Do not block on async code; use async all the way:
public async Task<ActionResult> Index()
{
await Developer();
return View();
}
I have a .NET CORE 2 backend. In one of my controller endpoints, I'm creating invitations to be sent out via email. This seems to be a huge bottleneck on the endpoint and after thinking about it, I don't really need to wait for these invitations. If the email fails to send out, I can't really do anything about it anyway.
If I don't do await sendFn() would it essentially be a fire and forget method? I was reading on another stackoverflow thread that I'd have to do sendFn().ContinueWith(t => throw(t)) to be able to catch the exception since it'll be in another thread.
I have similar mailing functions around the code base. They each do slightly different things, but is there a service fn I can do to wrap these to make them fire and forget? I think some places I can just not use await (if that works), but some things alter the database context so if I don't await them I can potentially run into a case where something is accessing the same db context.
[HttpPost]
public async Task<IActionResult> CreateEvent([FromBody] Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitations(val); // fn in question
return Ok();
}
public async Task SendInvitation(Event event)
{
forEach (var person in event.people)
{
await _ctx.Invitation.Add(person); // This shouldn't happen while another iteration or some other async code elsewhere is using the db ctx.
_ctx.SaveChangesAsync();
await _mailService.SendMail(person.email,"you have been invited"); // don't really need to await this.
}
}
I'm posting to my server with data about an event. After I create and save the event to the database, I go and create invitations for each person. These invitations are also database items. I then send out an email. I'm mostly worried that if I drop the await, then when I'm creating invitations, it may conflict with db context elsewhere or the next iteration.
To get your code to compile and run I had to make these changes:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitation(val);
return Ok();
}
public async Task SendInvitation(Event #event)
{
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
}
I also had to write this harness code:
public OK Ok() => new OK();
public class Event
{
public List<Person> people = new List<Person>();
}
public class Person
{
public string email;
}
public interface IActionResult { }
public class OK : IActionResult { }
public class Invitation
{
public Task Add(Person person) => Task.Run(() => { });
}
public static class _ctx
{
public static List<Event> Event = new List<Event>();
public static Invitation Invitation = new Invitation();
public static Task SaveChangesAsync() { return Task.Run(() => { }); }
}
public static class _mailService
{
public static Task SendMail(string email, string message) { return Task.Run(() => { }); }
}
Then I updated SendInvitation like this:
public async Task SendInvitation(Event #event)
{
Thread.Sleep(2000);
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
Console.WriteLine("Done `SendInvitation`.");
}
Now, I can run it all like so:
var e = new Event();
e.people.Add(new Person() { email = "foo#bar.com" });
CreateEvent(e).ContinueWith(t => Console.WriteLine("Done `CreateEvent`."));
Console.WriteLine("Done `Main`.");
That outputs:
Done `Main`.
Then 2 seconds later:
Done `SendInvitation`.
Done `CreateEvent`.
If I simply change CreateEvent to this:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
Task.Run(() => SendInvitation(val));
return Ok();
}
Then I get this output:
Done `Main`.
Done `CreateEvent`.
Then 2 seconds later:
Done `SendInvitation`.
That seems to be what you want.
The short answer is that you have no guarantees that that the execution of that code will complete.
That's why ASP.NET Core has infrastructure for background work: Implementing background tasks in .NET Core 2.x webapps or microservices with IHostedService and the BackgroundService class
I'm using HttpClient trying to execute a POST method in Web API controller. The controller method is synchronous. I'm doing so this way:
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
After that I'm calling Wait:
var result = response.Wait(15000);
When running this code, I see the http finish executing, yet the result value is always false. What can I be missing?
Edit:
I now tried an async approach yet it didn't help me as well
public IHttpActionResult Add(Item item)
{
var result = _db.AddItem(item);
return Ok(result);
}
Test project:
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await
_owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
Method2(item).Continue with(task => {// Never reach here }
}
What am I doing wrong?
When debugging I see that the controller method returns a good response, yet it never reaches back to my test
You are mixing async and blocking calls (ie .Result, .Wait()) which are leading to a deadlock.
This looks more like it is a blocking issue on the test client side.
You need to make the test async all the way through in this case if you want to await on results from the server.
Convert test method to async
[TestMethod]
public async Task test1() {
//Arrange
Item item = new Item(...);
//Act
var preview = await Method2(item);
//Assert
Assert.IsNotNull(preview);
}
And update the methods to not mix async and blocking calls.
Method1 does not need asyn/await if it is not using the task after the call so it can be removed and just have the method return the Task that can be awaited
TestServer _owinTestServer;
public Task<HttpResponse> Method1(string url, object body) {
return _owinTestServer.HttpClient.PostAsJsonAsync(url, body);
}
Method2 needs to await the response from Method1 and then get its content.
public async Task<ItemPreview> Method2(object body) {
var response = await Method1("..", body );
return await response.Content.ReadAsAsync<ItemPreview>();
}
It doesn't matter if the controller method is synchronous or not. That is purely a concern on the server code. Since the PostAsJsonAsync method is asynchronous, you need to await it:
var response = await owin.HttpClient.PostAsJsonAsync(uri, body);
Which will allow your code to wait for the response from the server.
I assume your initial code looked something like this...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var result = response.Wait(15000);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
...
And yes, result will be false after waiting the 15 seconds. This is because you have setup the request, A.K.A response, but you haven't actually made the called and any response.Wait(n) will return false.
You just need to start the ReadAsAsync...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
However, I think you will find you can skip the response.Wait(n) all together, as it will return true because the ReadAsAsync() will wait to return or fail. If you're looking to configure the request timeout, you can do that in the HttpClient and your ReadAsAsync will throw an AggregateException with an InnerException of TaskCanceledException.
On a side note, you don't need to use owin.HttpClient you can just instantiate a new HttpClient. I believe the owin object you are referring to is for self hosting your WebApi, I don't know if that matters. But let's say you are calling Add(Item item) on your WebApi and that db.AddItem(item) will return and ItemPreview object, your code could look like this:
[TestMethod]
public void test1()
{
Item item = new(...);
var uri = "..";
var client = new HttpClient();
var response = client.PostAsJsonAsync(uri, item);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
/* The things to happen once you have item preview */
}
The result may always be false because the _db.AddItem is returning false all the time. If not, I've made a change in your code ideally which should work for you
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
await Method2(item).ContinueWith(task => {// Never reach here }
}
Since Method2 returns an Async Task, the ContinueWith will not wait for it to complete and hence it may required await on the method invocation.
I have case there i will make rest call and based on response i will save it into database.
private async Task<bool> ProcessParallal<T>(IList<T> t,PropertyInfo pi, string name, int Id)
{
string mainId = null,intermediateId = null;
var tasks = t.Select(async item =>
{
var response = await CallRestApi(item,(long)(pi.GetValue(item)),name,id);
//Saving Rsult based on RestAPi result
// Requiremnt is wait until databse operation finishes
await this.SaveToTable(response.id,name,id));
});
await Task.WhenAll(tasks);
return await Task.FromResult(false);
}
private async Task<string> SaveToTable(int responseId,name,id)
{
//Save details in Audit Table
_iDataAccess.AuditSave(responseId,name,id);
return await Task.FromResult("Success");
}
public async Task<ResponseResult> CallRestApi<T>(T itemObj,id,name)
{
//Fetching From DB
var info = await fetchDetailsFromDB(id, name);
//Rest Api Call
return await CreateNewEntryByRestCall<T>(itemObj,info.name);
}
But ProcessParallal doesnot wait until Database operation Finishes (ie SaveToTable method) .ProcessParallal method returns before database operation completes.
Can anyone point out issue with this code ..
If I understand you question properly with waiting on the ProcessParallal method, your asynchronous call
private async Task<bool> ProcessParallal<T>(IList<T> t,PropertyInfo pi, string name, int Id)
should then be changed to be a synchronous call:
private bool ProcessParallal<T>(IList<T> t,PropertyInfo pi, string name, int Id)
This is only the idea on what im doing in a window service.
I get the idea from this video to do it in parallel processing.
I have two different method and a model class.
Model Class code:
public class Email(){
public string Recipient { get; set; }
public string Message { get; set; }
}
Methods is something like this:
public void LoadData(){
while(Main.IsProcessRunning){
// 1. Get All Emails
var emails = new dummyRepositories().GetAllEmails(); //This will return List<Emails>.
// 2. Send it
// After sending assume that the data will move to other table so it will not be query again for the next loop.
SendDataParallel(emails);//this will function async? even though the calling method is sync.
// This will continue here or wait until it already send?
// If it will continue here even though it will not send already
// So there's a chance to get the email again for the next loop and send it again?
}
}
//This will send email at parallel
public async void SendDataParallel(IList<Email> emails){
var allTasks = emails.Select(SendDataAsync);
await TaskEx.WhenAll(allTasks);
}
//Assume this code will send email asynchronously. (this will not send email, for sample only)
public async void SendDataAsync(Email email){
using (var client = new HttpClient())
{
client.PostAsync(email);
}
}
I only want to get all queued emails then send it in parallel then wait until it already send.
I'm avoiding using foreach on every email that I get.
Lets start at the bottom:
You dispose your client before you actually finish receiving the HttpResponseMessage asynchronously. You'll need to make your method async Task and await inside:
public async Task SendDataAsync(Email email)
{
using (var client = new HttpClient())
{
var response = await client.PostAsync(email);
}
}
Currently, your SendDataParallel doesn't compile. Again, it needs to return a Task:
public Task SendEmailsAsync(IList<Email> emails)
{
var emailTasks = emails.Select(SendDataAsync);
return Task.WhenAll(allTasks);
}
At the top, you'll need to await on SendEmailsAsync:
public async Task LoadDataAsync()
{
while (Main.IsProcessRunning)
{
var emails = new dummyRepositories().GetAllEmails();
await SendEmailsAsync(emails);
}
}
Edit:
If you're running this inside a windows service, you can offload it to Task.Run and use the async keyword:
var controller = new Controller();
_processThread = Task.Run(async () => await controller.LoadDataAsync());
Doesn't your compiler highlight your code with errors?
If you mark your method as async while it doesn't return any value, you should set your return type as Task, not void:
public async Task SendDataParallel(IList<Email> emails){
var allTasks = emails.Select(SendDataAsync);
await Task.WhenAll(allTasks);
}
Your second method also shoud return a Task, otherwise what you want to (a)wait in the first method?
public async Task SendDataAsync(Email email){
using (var client = new HttpClient())
{
return client.PostAsync(email);
}
}
Now you can Select all your SendDataAsync tasks in SendDataParallel and .Wait() it's task in LoadData in synchronious mode:
public void LoadData(){
while(Main.IsProcessRunning){
var emails = new dummyRepositories().GetAllEmails(); //This will return List<Emails>.
SendDataParallel(emails).Wait();
}
}
More information you can find reading answers in other SO questions and docs on MSDN:
Can somebody please explain async / await?
Brief explanation of Async/Await in .Net 4.5
how to and when use async and await
Asynchronous Programming with Async and Await
And as you used LINQ's Select() which is based on foreach cycle next article also could be useful:
Nested task inside loop