What is Difference between 'public async Task<IActionResult>' and 'public ActionResult' in MVC - asp.net-core-1.0

I don't know what is Difference Between Both Methods in Asp.Net Core MVC6
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var studentToUpdate = await _context.Students.SingleOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
try
{
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
return View(studentToUpdate);
}
And
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var studentToUpdate = _context.Students.SingleOrDefaultAsync(s => s.ID == id);
if (TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
try
{
_context.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
return View();
}
I see that the MVC code now has async but what is the difference. Does one give much better performance than the other? Is it easier to debug problems with one than the other? Should I make changes to other controllers for my application to add Async ?

An action method that just returns an ActionResult is inherently synchronous. Therefore, any long running method that is executed within the MVC action will hold the thread and not make it available to serve other web requests. However, when you use async Task<ActionResult> and you call in a method within the action that is long running and asynchronous the thread is freed up and a callback is initiated to take over when the long running method returns.
Having said that, if you do not use async programming within the action method I believe it doesn't make any difference. But if you use EF or any library worth its salt, it will be async.
As a developer, you don't need to do anything special really, other than calling and awaiting the async method, so there is really no value in not using it.
Performance wise you won't really see any big change on Dev but on prod or load testing you will see the gain.
To sum it up, if you see an async implementation, use it, unless you really have to use synchronous for a special purpose.

Related

do we need to synchronise the DB calls if multithreading is involved?

I have a scenario where two threads invoke a method and this method generated a sequence using postgres nextval(test_sequence).
test_sequence is initailly assigned to 1.
public String createNotification() {
logger.info("createNotification ENTRY");
Future<String> futRes = this.threadPool.submit(new Callable<String>() {
#Override
public String call() {
String notificationID = getNotificationId();//DB CALL TO GENERATE THE NEXT SEQUENCE.
boolean isInsertSuccess = notificationDaoService.insertNotificationIntoDB(notificationID);
if (isInsertSuccess == true) {
return notificationID;
} else {
return null;
}
}
});
try {
return futRes.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
logger.error("Issue while getting value from future with exception :", e);
return null;
}
}
So in the above snippet, getNotificationId() will generate the sequence and insertNotificationIntoDB() wil insert the generated notification id to the table.
I some times observing the primary key voilation exception when multiple threads try to invoke createNotification().
So i am thinking to synchronise the db calls as mentioned below,
synchronised(object)
{
String notificationID = getNotificationId();
boolean isInsertSuccess = notificationDaoService.insertNotificationIntoDB(notificationID);
}
is this solution ok?
and also i want to ask if i can generalise that if multiple threads are accessing a function and if that function has DB calls that does basic CRUD, then all the DB calls needs to be synchronised. Is this right inference?

Updating one-to-many relationships with Razor Pages

I cannot figure out how to update related data using Razor Pages asp.net core 2.0.
In the below example, I can update the "Project" data, but I cannot see the related "Action" data (one project => many actions) on OnPostAsync().
EditModel : PageModel
[BindProperty]
public Project Project { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
//THIS PART WORKS, I CAN SEE ACTIONS WITH THE PROJECT
Project = await _context.Project.Include(a=>a.Action).SingleOrDefaultAsync(m => m.Id == id);
if (Project == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int? id)
{
//MODEL IS NOT RETURNING ACTIONS (One project many actions)
if (!ModelState.IsValid)
{
return Page();
}
//How do I update actions?
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProjectExists(Project.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
OnModelCreating:
modelBuilder.Entity<Action>(entity =>
{
entity.HasOne(d => d.Project)
.WithMany(p => p.Action)
.HasForeignKey(d => d.ProjectId)
.HasConstraintName("FK_Action_Project");
});
I think you have not understood how BindProperty works. By using BindProperty for a property you ask from the framework the value of the property to be available in the view, and in case that you have an input containing the value it will go back to the property when you submit the contents of the input.
This means that even for the project object you will not get values if you do not have inputs which will host the values of the mode.
You have two options. The first option is to put inputs for all the your action items of the list which is not a good idea and the second option is to query the list from the database again after the submition of the form.

How does navigation work with LUIS subdialogs?

I have a question... Unfortunately all the samples on the web are too shallow and don't really cover this well:
I have a RootDialog that extends the LuisDialog. This RootDialog is responsible for figuring out what the user wants to do. It could be multiple things, but one of them would be initiating a new order. For this, the RootDialog would forward the call to the NewOrderDialog, and the responsibility of the NewOrderDialog would be to figure out some basic details (what does the user want to order, which address does he like to use) and finally it will confirm the order and return back to the RootDialog.
The code for the RootDialog is very simple:
[Serializable]
public class RootDialog : LuisDialog<object>
{
public RootDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"], domain: "westus.api.cognitive.microsoft.com")))
{
}
[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
await context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, context.Activity, CancellationToken.None);
context.Wait(MessageReceived);
}
private async Task OnPlaceOrderIntentCompleted(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync("Your order has been placed. Thank you for shopping with us.");
context.Wait(MessageReceived);
}
}
I also had some code in mind for the NewOrderDialog:
[Serializable]
public class NewOrderDialog : LuisDialog<object>
{
private string _product;
private string _address;
public NewOrderDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"], domain: "westus.api.cognitive.microsoft.com")))
{
}
[LuisIntent("Order.RequestedItem")]
public async Task RequestItemIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation item;
if (result.TryFindEntity("Item", out item))
{
_product = item.Entity;
await context.PostAsync($"Okay, I understood you want to order: {_product}.");
}
else
{
await context.PostAsync("I couldn't understand what you would like to buy. Can you try it again?");
}
context.Wait(MessageReceived);
}
[LuisIntent("Order.AddedAddress")]
public async Task AddAddressIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation item;
if (result.TryFindEntity("Address", out item))
{
_address = item.Entity;
await context.PostAsync($"Okay, I understood you want to ship the item to: {_address}.");
}
else
{
await context.PostAsync("I couldn't understand where you would like to ship the item. Can you try it again?");
}
context.Wait(MessageReceived);
}
}
The code as listed doesn't work. Upon entering the Order.Place intent, it immediately executes the 'success' callback, and then throws this exception:
Exception: IDialog method execution finished with multiple resume handlers specified through IDialogStack.
[File of type 'text/plain']
So I have a few questions:
How do I solve the error that I get?
How can I, upon entering the NewOrderDialog, check if we already know what the product and address is, and if not prompt them for the correct info?
Why does the NewOrderDialog get closed even though I don't call anything like context.Done()? I only want it to be closed when all the info has been gathered and the order has been confirmed.
So the first issue is that you are doing a context.Forward and a context.Wait in "Order.Place", which by definition is wrong. You need to choose: forward to a new dialog or wait in the current. Based on your post, you want to forward, so just remove the Wait call.
Besides that, you have 1 LUIS dialog and you are trying to forward to a new LUIS dialog... I have my doubts if that will work on not; I can imagine those are two different LUIS models otherwise it will be just wrong.
Based on your comment, I now understand what you are trying to do with the second dialog. The problem (and this is related to your second question) is that using LUIS in tha way might be confusing. Eg:
user: I want to place an order
bot => Forward to new dialog. Since it's a forward, the activity.Text will likely go to LUIS again (to the model of the second dialog) and nothing will be detected. The second dialog will be in Wait state, for user input.
Now, how the user will know that he needs to enter an address or a product? Where are you prompting the user for them? See the issue?
Your third question I suspect is a side effect of the error you are having in #1, which I already provided the solution for.
If you clarify a bit more I might be even more helpful. What you are trying to do with LUIS in the second dialog it doesn't look ok, but maybe with an explanation might have sense.
A usual scenario would be: I get the intent from LUIS ("Order.Place") and then I start a FormFlow or a set of Prompts to get the info to place the order (address, product, etc) or if you want to keep using LUIS you might want to check Luis Action Binding. You can read more on https://blog.botframework.com/2017/04/03/luis-action-binding-bot/.
Do you know about the Bing Location Control for Microsoft Bot Framework? It can be used to handle the 'which address does he like to use' part in obtaining and validating the user's address.
Here is some sample code:
[LuisModel("xxx", "yyy")]
[Serializable]
public class RootDialog : LuisDialog<object>
{
...
[LuisIntent("Find Location")]
public async Task FindLocationIntent(IDialogContext context, LuisResult result)
{
try
{
context.Call(new FindUserLocationDialog(), ResumeAfterLocationDialog);
}
catch (Exception e)
{
// handle exceptions
}
}
public async Task ResumeAfterLocationDialog(IDialogContext context, IAwaitable<object> result)
{
var resultLocation = await result;
await context.PostAsync($"Your location is {resultLocation}");
// do whatever you want
context.Wait(this.MessageReceived);
}
}
And for "FindUserLocationDialog()", you can follow the sample from Line 58 onwards.
Edit:
1) Can you try using:
[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, context.Activity, CancellationToken.None);
// or this
// context.Call(new NewOrderDialog(), OnPlaceOrderIntentCompleted);
}
2) I would say it depends on how you structured your intent. Does your "Order.Place" intent include entities? Meaning to say, if your user said "I want to make an order of Product X addressed to Address Y", does your intent already pick up those entities?
I would suggest that you check the product and address under your "Order.Place" intent. After you obtained and validated the product and address, you can forward it to another Dialog (non-LUIS) to handle the rest of the order processing.
[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation item;
if (result.TryFindEntity("Item", out item))
{
_product = item.Entity;
}
if (result.TryFindEntity("Address", out item))
{
_address = item.Entity;
}
if (_product == null)
{
PromptDialog.Text(context, this.MissingProduct, "Enter the product");
}
else if (_address == null)
{
PromptDialog.Text(context, this.MissingAddress, "Enter the address");
}
// both product and address present
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}
private async Task MissingProduct(IDialogContext context, IAwaitable<String> result)
{
_product = await result;
// perform some validation
if (_address == null)
{
PromptDialog.Text(context, this.MissingAddress, "Enter the address");
}
else
{
// both product and address present
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}
}
private async Task MissingAddress(IDialogContext context, IAwaitable<String> result)
{
_address = await result;
// perform some validation
// both product and address present
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}
Something along these lines. Might need to include try/catch.
3) I think it has got to do with your 'context.Wait(MessageReceived)' in 'Order.Place' LUIS intent

Get Result of System.Threading.Tasks.Task (ApplicationUser MVC)

I just want to get my ApplicationUser in MVC. I'm trying this code:
public async System.Threading.Tasks.Task<ApplicationUser> GetApplicationUser()
{
return await _userManager.GetUserAsync(HttpContext.User);
}
var user = GetApplicationUser();
var user2 = user.Result;
However, when I try to access user.Result I get exception
"One or more errors occurred. (Object reference not set to an instance of an object.)"
"Object reference not set to an instance of an object."
Not sure how to access the result.
Make sure you controller action is aync Task, like so:
public async Task<IActionResult> Test()
{
ApplicationUser user = await GetCurrentUserAsync();
if (user != null) {
// do more stuff here
}
// do more stuff here
return View();
}
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
From the code you posted (without any exception details), I would guess that the problem is that _userManager is null.
However, you'll run into another problem if you fix that. Specifically, you should be using await instead of Result; the latter can cause deadlocks.
I had to call the .Wait() function manually, and then I can access the .Result successfully. In addition, my HttpContext was null so I had to use it inside of Index() like
public IActionResult Index()
{
DoSomethingWith(HttpContext);
return View();
}
and
System.Threading.Tasks.Task<ApplicationUser> user = GetApplicationUser(context);
user.Wait();
var user2 = user.Result;

Windows 8 - await Task<Bool> - async call back on completed listener required

I have a function which opens a file from storage and returns back a Boolean specified that the file opened just fine.
private async Task<bool> SaveImage()
{
try
{
await filesave.openAsync(FileAccessMode.ReadWrite)
}
catch()
{
return false;
}
return true;
}
I want to call the await SaveImage() function, but somehow want a listener/event handler which tells me when this has completed.. upon completion I want to update my layout with new data. How is this possible using the new WINRT async/ await async methodology for windows 8? is there a work around/substitute.
How can I setup a event handler type scenario? (on complete)
You just await a call to your method, and follow it with the code that should run when it's completed. You don't need to manually register an event handler.
var succeeded = await SaveImage();
// Because of the "await" keyword in the above line, the current method
// will not continue until "SaveImage" has completed its async work and
// signaled its Task
if (succeeded) { ... }
Of course, since the above code uses the await keyword, it needs to be placed inside a method that's also marked async. If that method needs to signal its completion to its caller, then it should also return a Task or Task<T>; for example:
private async Task<string> MyHelperMethod() {
var succeeded = await SaveImage();
if (succeeded)
return "Success";
else
return "Failure";
}
// then, in some other method:
var message = await MyHelperMethod();
Or, if the method calling SaveImage is the end of the line -- say it's the handler for a Button's Click event -- then it can be async void:
private async void ButtonClick(object sender, EventArgs args) {
var succeeded = await SaveImage();
if (succeeded) { ... }
}
Joe's answer looks great, though if you insist on using an event - for example if your SaveImage() calls are in various areas of code unrelated to updating the layout - you can just raise an event when your operation completes. You can use the plain old CLR events or use a pub-sub pattern implementation like the EventAggregator from Prism or Messenger from MVVM Light. The POCE version could look like this
public event EventHandler<Tuple<StorageFile,bool>> SaveImageCompleted;
private async Task<bool> SaveImage(StorageFile file)
{
try
{
await file.OpenAsync(FileAccessMode.ReadWrite);
}
catch
{
if (SaveImageCompleted != null)
SaveImageCompleted(this, new Tuple<StorageFile, bool>(file, false));
return false;
}
if (SaveImageCompleted != null)
SaveImageCompleted(this, new Tuple<StorageFile, bool>(file, true));
return true;
}
This takes a little more code but is a VERY cool and useful way to deal with async operations, progress, cancellations and complete status in general. This was compiled in a VS2012 Winrt Store App and I ran it off a button click as you see here.
private void Save_File_Click(object sender, RoutedEventArgs e)
{
// create your op, bool = return type, string = progress report
IAsyncOperationWithProgress<bool, string> op;
// Call our async operation with progress sending the file name
op = OpenFileWithProgress("test.txt");
// not implemented here
//op.Cancel();
// when we get a progress update...
op.Progress = (info, progress) =>
{
// I'm just giving text feed back to user here
Debug.WriteLine(progress);
};
op.Completed = (info, status) =>
{
// check status for completion or cancellation...
switch (status)
{
case AsyncStatus.Completed:
// Do your completed work here
Debug.WriteLine("Completed");
break;
case AsyncStatus.Canceled:
// Operation canceled - not implemented...
Debug.WriteLine("Canceled");
break;
default:
// default stuff here
break;
}
};
}
public IAsyncOperationWithProgress<bool, string> OpenFileWithProgress(string fileName)
{
return System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run<bool, string>((token, progress) =>
Task.Run<bool>(async () =>
{
progress.Report("Starting");
try
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(fileName);
}
catch (Exception ex)
{
return false;
}
progress.Report("Finished");
return true;
}, token));
}

Resources