I am trying to put my asp.net app to production. But when I run the application in Production, a certain route doesnt work that well. It says that post.Author == null. When I run the application in debug, it runs well, even without breakpoints.
I tried the application in a release build or just with the start button in rider, both doesnt work.
Thats the route I am trying to debug. I have the problem with some other routes, too.
[HttpGet("public/{orgId}")]
public async Task<ActionResult<List<PostModel>>> GetAllPublicPostsByOrgId(string orgId)
{
List<PostModel> posts = await _postRepository.GetAllPostsByOrgId(orgId);
posts.ForEach(async post =>
{
UserModel author = await _userRepository.GetUserById(post.AuthorId);
UserModel saveAuthor = new UserModel();
saveAuthor.FirstName = author.FirstName;
saveAuthor.LastName = author.LastName;
post.Author = saveAuthor;
});
return Ok(from post in posts where post.Published select post);
}
The author should be null, it should be an object with the attributes.
Edit: I tried to use [MethodImpl(MethodImplOptions.NoOptimization)] on that router. That didnt work, too.
The problem is that the lambda you use in the ForEach is basically a async void method which means it's fire and forget. So your code is going to get to the return before it finishes setting all the Author properties. Instead you should use a normal foreach(var post in posts) so the method will wait for all the Author properties to be set. Note the reason this likely only fails in release is because this is a race condition and debug probably slows stuff down just enough for it to not happen.
Alternatively you could make the code into a method and instead execute it in parallel and then wait for all the tasks to complete before returning.
[HttpGet("public/{orgId}")]
public async Task<ActionResult<List<PostModel>>> GetAllPublicPostsByOrgId(string orgId)
{
List<PostModel> posts = await _postRepository.GetAllPostsByOrgId(orgId);
var authorSetTasks = new List<Task>();
foreach(var post in posts)
{
authorSetTasks.Add(SetAuthor(post));
}
await Task.WhenAll(authorSetTasks);
return Ok(from post in posts where post.Published select post);
}
private async Task SetAuthor(PostModel post)
{
UserModel author = await _userRepository.GetUserById(post.AuthorId);
UserModel saveAuthor = new UserModel();
saveAuthor.FirstName = author.FirstName;
saveAuthor.LastName = author.LastName;
post.Author = saveAuthor;
}
Related
I'm not sure if this code is Asynchronous. I call this function using await from my main controller and within the function I use await on the LINQ query and .ToListAsync() - but after the query I have the foreach loop which may defeat the purpose of async on the query.
Main Controller Call:
case "getassets":
reply = await GetAssets();
break;
Function:
public async Task<ReplyObj> GetAssets()
{
ReplyObj obj = new ReplyObj();
obj.Result = new List<dynamic>();
dynamic AssetRecords = await _context.Asset.FromSql("SELECT * FROM Asset").ToListAsync();
foreach (var objAsset in AssetRecords)
{
obj.Result.Add(new Asset()
{
AssetId = objAsset.AssetId,
Name = objAsset.Name,
Description = objAsset.Description,
PriceDecimals = objAsset.PriceDecimals
});
}
obj.Success = true;
obj.Message = "";
return obj;
}
This call will have many requests hitting it, I want to know for sure that its using async correctly. Thank you!
To begin, here's a couple of references for async/await in C# that I'd suggest reviewing:
Microsoft Docs
SO Community Answer
The simple (high-level) answer is that awaiting your sql call will return control up the call stack and continue execution. In this case, that means it will go up to:
reply = await GetAssets();
Which will in turn return control to whatever function called that, etc. etc..
With that said, if all of your async calls in your call stack are immediately being awaited, then async won't end up buying you anything/changing the flow of control. To say, keep in mind that async != threading.
Few things that I want to point out:
dynamic AssetRecords = await _context.Asset.FromSql("SELECT * FROM Asset").ToListAsync(); When you are using _context.Asset it will return all the Asset rows that you have. Why would you execute another query on the table when the Asset it self is giving all that you need? Hence, to me this is redundant thing.
And if you use select method and then get the list asyncronously then you will have eliminated the the foreach loop and it will cost only one await call and query processing. See code sample below:
public async Task<ReplyObj> GetAssets()
{
ReplyObj obj = new ReplyObj();
return obj.Result = await _context.Asset.Select(s => new
{
AssetId = s.AssetId,
Name = s.Name,
Description = s.Description,
PriceDecimals = s.PriceDecimals
}).ToListAsync();
}
This could now be your true async method.
PS:
If you want to show your code to experts for review, I would suggest joining StackExchange CodeReview Community
I am calling this action (ASP.Net Core 2.0) over AJAX:
[HttpGet]
public async Task<IActionResult> GetPostsOfUser(Guid userId, Guid? categoryId)
{
var posts = await postService.GetPostsOfUserAsync(userId, categoryId);
var postVMs = await Task.WhenAll(
posts.Select(async p => new PostViewModel
{
PostId = p.Id,
PostContent = p.Content,
PostTitle = p.Title,
WriterAvatarUri = fileService.GetFileUri(p.Writer.Profile.AvatarId, Url),
WriterFullName = p.Writer.Profile.FullName,
WriterId = p.WriterId,
Liked = await postService.IsPostLikedByUserAsync(p.Id, UserId),// TODO this takes too long!!!!
}));
return Json(postVMs);
}
But it took too long to response (20 seconds!!!) in case I have many post objects in posts
array (e.g. 30 posts).
That is caused because of this line await postService.IsPostLikedByUserAsync.
Digging into the source code of this function:
public async Task<bool> IsPostLikedByUserAsync(Guid postId, Guid userId)
{
logger.LogDebug("Place 0 passed!");
var user = await dbContext.Users
.SingleOrDefaultAsync(u => u.Id == userId);
logger.LogDebug("Place 1 passed!");
var post = await dbContext.Posts
.SingleOrDefaultAsync(u => u.Id == postId);
logger.LogDebug("Place 2 passed!");
if (user == null || post == null)
return false;
return post.PostLikes.SingleOrDefault(pl => pl.UserId == userId) != null;
}
The investigations showed, after some seconds, ALL "Place 1 passed!" logging methods are executed together for every post object. In other words, it seems that every post awaits until the previous post finishes executing this part:
var user = await dbContext.Users
.Include(u => u.PostLikes)
.SingleOrDefaultAsync(u => u.Id == userId);
And then -when every post finishes that part- the place 1 of log is executed for all post objects.
The same happens for logging place 2, every single post seems to await for the previous post to finish executing var post = await dbContext.Pos..., and then the function can go further to execute log place 2 (after few seconds from log 1, ALL log 2 appear together).
That means I have no asynchronous execution here. Could some one help me to understand and solve this problem?
UPDATE:
Changing the code a bit to look like this:
/// <summary>
/// Returns all post of a user in a specific category.
/// If the category is null, then all of that user posts will be returned from all categories
/// </summary>
/// <param name="userId"></param>
/// <param name="categoryId"></param>
/// <returns></returns>
[Authorize]
[HttpGet]
public async Task<IActionResult> GetPostsOfUser(Guid userId, Guid? categoryId)
{
var posts = await postService.GetPostsOfUserAsync(userId, categoryId);
var i = 0;
var j = 0;
var postVMs = await Task.WhenAll(
posts.Select(async p =>
{
logger.LogDebug("DEBUG NUMBER HERE BEFORE RETURN: {0}", i++);
var isLiked = await postService.IsPostLikedByUserAsync(p.Id, UserId);// TODO this takes too long!!!!
logger.LogDebug("DEBUG NUMBER HERE AFTER RETURN: {0}", j++);
return new PostViewModel
{
PostId = p.Id,
PostContent = p.Content,
PostTitle = p.Title,
WriterAvatarUri = fileService.GetFileUri(p.Writer.Profile.AvatarId, Url),
WriterFullName = p.Writer.Profile.FullName,
WriterId = p.WriterId,
Liked = isLiked,
};
}));
return Json(postVMs);
}
That shows, that this line "DEBUG NUMBER HERE AFTER RETURN" is printed for ALL select methods together, that means that ALL select methods waits for each other before going further, how can I prevent that?
UPDATE 2
Substituting the previous IsPostLikedByUserAsyncmethod, with the following one:
public async Task<bool> IsPostLikedByUserAsync(Guid postId, Guid userId)
{
await Task.Delay(1000);
}
Showed no problem in async running, I had to wait only 1 second, not 1 x 30.
That means it is something specific to EF.
Why does the problem happen ONLY with entity framework (with the original function)? I notice the problem even with only 3 post objects! Any new ideas?
The deductions you've made are not necessarily true.
If these methods were firing in a non-asynchronous fashion, you would see all of the logs from one method invocation reach the console before the next method invocation's console logs. You would see the pattern 123123123 instead of 111222333. What you are seeing is that the three awaits seem to synchronize after some asynchronous batching occurs. Thus it appears that the operations are made in stages. But why?
There are a couple reasons this might happen. Firstly, the scheduler may be scheduling all of your tasks to the same thread, causing each task to be queued and then processed when the previous execution flow is complete. Since Task.WhenAll is awaited outside of the Select loop, all synchronous portions of your async methods are executed before any one Task is awaited, therefore causing all of the "first" log invocations to be called immediately following the invocation of that method.
So then what's the deal with the others syncing up later? The same thing is happening. Once all of your methods hit their first await, the execution flow is yielded to whatever code invoked that method. In this case, that is your Select statement. Behind the scenes, however, all of those async operations are processing. This creates a race condition.
Shouldn't there be some chance of the third log of some methods being called before the second log of another method, due to varying request/response times? Most of the time, yes. Except you've introduced a sort of "delay" in to the equation, making the race condition more predictable. Console logging is actually quite slow, and is also synchronous. This causes all of your methods to block at the logging line until the previous logs have completed. But blocking, by itself, may not be enough to make all of those log calls sync up in pretty little batches. There may be another factor at play.
It would appear that you are querying a database. Since this is an IO operation, it takes considerably longer to complete than other operations (including console logging, probably). This means that, although the queries aren't synchronous, they will in all likelihood receive a response after all of the queries/requests have already been sent, and therefore after the second log line from each method has already executed. The remaining log lines are processed eventually, and therefore fall in to the last batch.
Your code is being processed asynchronously. It just doesn't look quite how you might expect. Async doesn't mean random order. It just means some code flow is paused until a later condition is met, allowing other code to be processed in the mean time. If the conditions happen to sync up, then so does your code flow.
Actually async execution works, but it doesn't work as you expect. Select statement starts tasks for all posts and then they all work concurrently that leads you to performance problems you.
The best approach to achieve expected behavior is to reduce the degree of parallelism. There are no build-in tools to do that so I can offer 2 workarounds:
Use TPL DataFlow library. It is developed by Microsoft but not very popular. You can easily find enough examples though.
Manage parallel tasks by yourself with SemaphoreSlim. It would look like this:
semaphore = new SemaphoreSlim(degreeOfParallelism);
cts = new CancellationTokenSource();
var postVMs = await Task.WhenAll(
posts.Select(async p =>
{
await semaphore.WaitAsync(cts.Token).ConfigureAwait(false);
cts.Token.ThrowIfCancellationRequested();
new PostViewModel
{
PostId = p.Id,
PostContent = p.Content,
PostTitle = p.Title,
WriterAvatarUri = fileService.GetFileUri(p.Writer.Profile.AvatarId, Url),
WriterFullName = p.Writer.Profile.FullName,
WriterId = p.WriterId,
Liked = await postService.IsPostLikedByUserAsync(p.Id, UserId),// TODO this takes too long!!!!
}
semaphore.Release();
}));
And don't forget to use .ConfigureAwait(false) whenever it's possible.
I am return a list which basically calls the two async operations:
[HttpPost]
public ActionResult List(DataSourceRequest command, ProductListModel model)
{
var categories = _productService.GetAllProducts(model.SearchProductName,
command.Page - 1, command.PageSize);
var gridModel = new DataSourceResult
{
Data = categories.Select(async x =>
{
var productModel = x.ToModel();
var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
var category = await _categoryService.GetCategoryById(x.CategoryId);
productModel.Category = category.Name;
productModel.Manufacturer = manufacturer.Name;
return productModel;
}),
Total = categories.TotalCount
};
return Json(gridModel);
}
It is an ajax request (from client side) but on front-end it never returns. Is there any a deadlock ?
Building up my answer from several comments and #usr's answer:
Data in the code above is actually IEnumerable<Task<ProductModel>>, not IEnumerable<ProductModel>. This is because the lambda passed to Select is async.
Most likely, the JSON serializer is going over this structure and enumerating the properties on the Task<ProductModel> instances, including Result.
I explain on my blog why accessing Result will cause a deadlock in this case. In a nutshell, it's because the async lambda will attempt to resume execution on the ASP.NET request context after its await. However, the ASP.NET request context is blocked on the call to Result, locking a thread inside that request context until the Task<T> completes. Since the async lambda cannot resume, it cannot complete that task. So both things are waiting for each other, and you get a classic deadlock.
There are some suggestions to use await Task.WhenAll, which I would normally agree with. However, in this case you're using Entity Framework and got this error:
A second operation started on this context before a previous asynchronous operation completed.
This is because EF cannot perform multiple asynchronous calls concurrently within the same db context. There are a couple ways around this; one is to use multiple db contexts (essentially multiple connections) to do the calls concurrently. IMO a simpler way is to make the asynchronous calls sequentially instead of concurrently:
[HttpPost]
public async Task<ActionResult> List(DataSourceRequest command, ProductListModel model)
{
var categories = _productService.GetAllProducts(model.SearchProductName,
command.Page - 1, command.PageSize);
var data = new List<ProductModel>();
foreach (var x in categories)
{
var productModel = x.ToModel();
var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
var category = await _categoryService.GetCategoryById(x.CategoryId);
productModel.Category = category.Name;
productModel.Manufacturer = manufacturer.Name;
data.Add(productModel);
}
var gridModel = new DataSourceResult
{
Data = data,
Total = categories.TotalCount
};
return Json(gridModel);
}
The way to debug is is to pause the debugger during the hang. There you will find that some serializer is blocking on Task.Result or similar.
You fill the Data property with a IEnumerable<Task>. The serializer probably calls Result at some point and that's the classic ASP.NET deadlock. You probably should wrap the Select call with await Task.WhenAll(x.Select(...)).
And even then it might be unsafe to concurrently run those lambdas.
After upgrading from Beta3 to Beta4, I'm now getting what looks like a deadlock(site hanges indefinitely) when calling TryUpdateModelAsync
public ActionResult Edit(ContactModel contactModel)
{
var contact = db.Contact.Find(contactModel.ContactId);
if (ModelState.IsValid)
{
TryUpdateModelAsync(contact);
var result = db.SaveChanges(() => { });
if (result.Success)
return RedirectToAction("Details", "Client", new { id = contact.ClientId });
}
return View(contactModel);
}
original code above was ported from MVC 5 so I was just trying to run synchronously. I get the same result after converting the controller method to Async like this
public async System.Threading.Tasks.Task<ActionResult> Edit(ContactModel contactModel)
{
var contact = db.Contact.Find(contactModel.ContactId);
if (ModelState.IsValid)
{
var updateResult = await TryUpdateModelAsync(contact);
var result = db.SaveChanges(() => { });
if (result.Success)
return RedirectToAction("Details", "Client", new { id = contact.ClientId });
}
return View(contactModel);
}
I took a quick look through the sample projects and I couldn't find any usage of this method, anyone have a working example? or know why this is happening?
Here is a sample of usage of try update model async (in MVC test code), Note this is from the dev branch. You might have to switch to whatever branch you are on.
https://github.com/aspnet/Mvc/blob/dev/test/WebSites/ModelBindingWebSite/Controllers/TryUpdateModelController.cs
Your first sample is bad because updating the model async without an await will create a task, but continue over it to the next method.
To figure out why you have a deadlock you want to break into the deadlock and see where is the stack at, does it have to do with tryupdatemodel at all?
If you can create a simple repro in a small project please file a bug in https://github.com/aspnet/mvc/issues
I have an online database from Parse.com. There's a table "Vakantie", that contains 3 objects, so the table data isn't big.
I'm trying to get all the objects from that table so I can display them through my VakantieController in a View.
Parse has an own documentation where sample code is provided. I copied their sample code and edited it so that it fits for my website.
The сode:
VakantieController:
public ActionResult Index()
{
var vakantiesTask = vakantieRepository.FindAll();
IEnumerable<Vakantie> vakanties = vakantiesTask.Result;
return View(vakanties);
}
VakantieRepository (class that contains code to retrieve from en write to the database):
public async Task<IList<Vakantie>> FindAll()
{
var query = from v in ParseObject.GetQuery("Vakantie")
orderby v.Get<string>("titel") ascending
select v;
IEnumerable<ParseObject> objects = await query.FindAsync();
IList<Vakantie> vakanties = new Vakantie[] { };
foreach (ParseObject vakantieObject in objects)
{
Vakantie vakantie = GetVakantie(vakantieObject);
vakanties.Add(vakantie);
}
return vakanties;
}
The method `GetVakantie(vakantieObject() is a method I wrote that converts the ParseObject I get from the query result to a Vakantie object from my domain.
When I run this code, I get until the line:
IEnumerable<ParseObject> objects = await query.FindAsync();
and when Visual Studio actually performs this command, my website just keeps loading and loading. I never get over this line to the next one. It is stuck right there.
Does anyone has a solution for this?
You're running into a classic deadlock scenario that I describe in full on my blog and an MSDN article. In summary, await will capture the current "context" and use that to resume the async method. Furthermore, the ASP.NET context will only allow one thread at a time. So when a request thread is blocked (on Task<T>.Result), the await inside of FindAll cannot resume the async method on that context, and you end up with a deadlock.
To correct it, use async "all the way"; i.e., don't use Task<T>.Result:
public async Task<ActionResult> Index()
{
var vakantiesTask = vakantieRepository.FindAll();
IEnumerable vakanties = await vakantiesTask;
return View(vakanties);
}