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)
Related
This is the first time I'm writing any asynchronous code. I am getting a compile error that says:
Cannot implicitly convert type void to int
while the function is returning an int.
public class Function
{
IAmazonS3 s3client = new AmazonS3Client(RegionEndpoint.USEast1);
public async Task<int> ListS3ObjectsAsync(string bucketName, IAmazonS3 client)
{
ListObjectsRequest request = new ListObjectsRequest();
request.BucketName = bucketName;
ListObjectsResponse response = await client.ListObjectsAsync(request);
do
{
if (response.IsTruncated)
request.Marker = response.NextMarker;
else
request = null;
} while (request != null);
var len = response.S3Objects.Count;
return len;
}
public void FunctionHandler(S3Event evnt, ILambdaContext context)
{
// Here I'm getting the compile error
int x = ListS3ObjectsAsync(evnt.Records?[0].S3.Bucket.Name, s3client).Wait();
}
}
Visual Studio screenshot:
Because ListS3ObjectsAsync method returns a Task<int>, which is an asynchronous representation of getting an int, so in order to get the result, you either need to await the task like this:
int x = await ListS3ObjectsAsync
But that will require you to make the FunctionHandler async aswell.
Or you can call .Result on the task like this
var task = ListS3ObjectsAsync
int x = task.Result
Note that this is a blocking operation and mixing async and non async code is bad practise.
.Wait method only waits for the task to complete, but does not return the Result
You shouldn't block on async code, so the best solution is to use await instead of Wait:
public void FunctionHandler(S3Event evnt, ILambdaContext context)
{
int x = await ListS3ObjectsAsync(evnt.Records?[0].S3.Bucket.Name, s3client);
}
The compiler will then tell you the next part of the solution: FunctionHandler must be made async and its return type changed to Task, i.e.:
public async Task FunctionHandlerAsync(S3Event evnt, ILambdaContext context)
{
int x = await ListS3ObjectsAsync(evnt.Records?[0].S3.Bucket.Name, s3client);
}
The "growth" of async like this is normal.
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 have an issue with a task blocking when I try to retrieve it's result.
I have the following piece of code I want executed synchronously (which is why I'm looking for the result)
I would ignore the reason each call has to be made (legacy software that requires multiple calls through different layers)
the call seems to break down after it starts the task for the final call to be made in the PostCreateProfile, I can see this request never makes it any further than this.
if (CreateProfile(demographics).Result) // Task blocks here
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}
The request will reach its destination if I was to change CreateProfile to an async void like so:
private async void CreateProfile(AppointmentController controller)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(controller);
await profileService.Create(createProfileBindingModel);
}
But I can't return the bool I want to use from this.
Can anyone point out what I am doing wrong?
You should never call .Result on a async/await chain.
Whatever code that calls CreateProfile(demographics) needs to be async too so it can do
if (await CreateProfile(demographics))
{
//dothing
}
Also, if you can you really should put .ConfigureAwait(false) wherever it is logically possible.
if (await CreateProfile(demographics).ConfigureAwait(false)) // depending on what dothing is you may not want it here.
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel).ConfigureAwait(false);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}
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
I am doing this. It's working but is this a recommended way to do this. Please comments
public async void LoadData()
{
DataTable dtAdditionsDetails = await LoadReportData(importID,
InkeyCommon.ToInt32(cmbSellers.SelectedValue),
fromDate,
toDate);
if (dtAdditionsDetails != null)
dtaGrdAdditions.ItemSource = dtAdditionsDetails.DefaultView;
}
public async Task<DataTable> LoadReportData(int? importID,
int sellerID,
DateTime? fromDate,
DateTime? toDate)
{
DataTable dtAdditionsDetails = new DataTable();
//Get Report Data
await Task.Delay(1);
dtAdditionsDetails = ReportsData.GetRptAdditions(importID,
sellerID,
fromDate,
toDate);
return dtAdditionsDetails;
}
In order to use the await keyword properly, the object being 'awaited' should really be an ...Async method, like the GetStringAsync method. As #ken2k has correctly pointed out, you cannot just await any method. Therefore, to answer your question is this a recommended way to do this?, the answer is no.
You can find out how to use the await and async keywords correctly in the Asynchronous Programming with Async and Await (C# and Visual Basic) page on MSDN, however, if you're just trying to run a synchronous method asynchronously, then you can do that like this:
public DataTable LoadData()
{
DataTable dtAdditionsDetails = ...
// Get your data here synchronously
return dtAdditionsDetails;
}
public async Task<DataTable> LoadDataAsync()
{
DataTable dtAdditionsDetails = LoadData();
return Task.Run(() => LoadData());
}
...
public async void GetDataAsynchronously()
{
DataTable dtAdditionsDetails = await LoadDataAsync();
}
Note that ...Async methods (usually) return Task<T> objects, rather than nothing and that their names end in the word Async. Also note that only the data is returned from ...Async methods and not the Task<T> and that we don't await when there is nothing to await.
Here's a simple function, to load a list of all [User] records from a database, using async and await.
[HttpGet]
public async Task<IActionResult> Get()
{
try
{
// Fetch a list of all the [User] records, just for the hell of it.
var users = await Task.Run(() => dbContextWebMgt.Users.ToList());
return new OkObjectResult(users);
}
catch (Exception ex)
{
return new BadRequestObjectResult(ex.Message);
}
}
You can try like this using NPoco ORM
[HttpGet]
public async Task<IActionResult> GetAsync()
{
IList myList = await FetchAsync();
}