async database call safe - asp.net-mvc

Hi Im new to MVC and still trying to understand the possible problem of using async.
I have the code below and as you can see i have placed async for all database calls (Not sure whether this is a good practice).
As you might notice this is a simple payment process. I am updating the CustomerPayment entity using the attribute of Customer and also once I added the new transaction, I am updating the balance attribute of the customer.
Using the colde below, will there be any risk of using async in regards to database calls ?
Also, I could see the _context.Database.BeginTransactionAsync() method for transaction, what will be the difference of using BeginTransactionAsync and normal BeginTransaction ?
Any chance this code can be re-written to properly use async ?
using (var dbContextTransaction = _context.Database.BeginTransaction())
{
try
{
CustomerPayment cp = vm;
Customer c = await _context.Customer.SingleOrDefaultAsync(m => m.CustomerId == vm.SelectedCustomerID);
decimal? updatedOutstandingAmount = c.CurrentOutStandingBalance - cp.Payment.PaymentAmount;
cp.OutstandingAmount = updatedOutstandingAmount;
c.CurrentOutStandingBalance = updatedOutstandingAmount;
_context.Add(cp);
_context.Update(c);
await _context.SaveChangesAsync();
dbContextTransaction.Commit();
TempData["status"] = "CustomerPaymentAdded";
return RedirectToAction("Payment", "Customer");
}
catch(Exception ex)
{
dbContextTransaction.Rollback();
}
};

Related

Is there an edge case for EF6 to not persist to db, but no exceptions thrown

I have a weird edge-case that happens probably less than 1% which I can't wrap my head around a possible cause or replicate.
A Xamarin forms mobile app calls an endpoint to insert data into the db, if successful everyone is happy, if not, retry later - the tech stack of the API is a bit old, MVC 5 and entity framework 6, but shouldn't be a problem.
The endpoint has some fundamental fixes needed, but showing for completeness:
public async Task<HttpResponseMessage> Post(SubmitDataDTO submitData)
{
db = new DBContext();
try
{
var parentId = Guid.NewGuid();
foreach (var childItem in submitData.Children)
{
var child = new Children()
{
Id = Guid.NewGuid(),
ParentId = parentId,
//properties..
};
db.Children.Add(child);
}
var parent = new Parent()
{
Id = incomingId,
//properties..
};
db.Parent.Add(parent);
await db.SaveChangesAsync();
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
//LOG VALIDATION ERROR
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
catch (Exception exc)
{
//LOG ERROR
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
The mobile app side is also pretty straight forward, if response.IsSuccessStatusCode is true, mark the submission as successfully processed. What we are seeing is that the mobile app is showing it was successfully submitted, but the data isn't available on the server.
Only explanation I can see, there is either an edge case where the SaveChanges doesn't persist with no exception and the OK is sent, or there is some edge case where IIS/endpoint sends an success, but the endpoint was never processed. I'm really stumped.

call async method from non async mvc controller and get return to a variable

public async Task<Utils.TReportReturn> GetReportDataColumnSettings(AdminTechnicianReportInput input)
{
input.CustomerId = m_customerId;
input.UserId = m_userId;
input.TimeOffset = m_timeOffset;
TReportReturn objTReportReturn = new TReportReturn();
objTReportReturn.ReportColumns = await new ReportsEntityContext().GetTechniciansReportColumns<AdminTechnicianReportInput>(input);
objTReportReturn.ReportSettings = await new ReportsEntityContext().GetGeneralSettingsData<AdminTechnicianReportInput>(input);
objTReportReturn.ReportData = await new ReportsDbContext().GetUserListDynamic<AdminTechnicianReportInput>(input);
return objTReportReturn;
}
public class HomeController : Controller
{
public ActionResult Index()
{
AdminTechnicianReportInput objInput = new AdminTechnicianReportInput();
objInput.CustomerId = 1;
objInput.UserId = 5477;
objInput.AddUpdateUserid = 0;
objInput.intRowsPerPage = 200;
objInput.intPageIndex = 0;
objInput.intTarget = 0;
objInput.intStatusId = 0;
objInput.strSearchQuery = "";
objInput.strSortColumn = "Id";
objInput.strSortDirection = "ASC";
var result = new ReportsModel(1, 5477, 0).Administration.AdminTechnicianReport.GetReportDataColumnSettings(objInput).Result;
ViewBag.Title = result.ReportSettings.CustomerName + "__" + result.ReportSettings.PrintedOn;
return View();
}
}
The GetReportDataColumnSettings function calls many async methods which is the the model library dll.
Index() method in HomeController is an mvc method and I want to get the return in the result variable. Itried getawaiter().getmethod(), but it didn't worked. I do not prefer to change the Index() method as asynk Task. Please suggest another way to get the result
I do not prefer to change the Index() method as asynk Task.
Why not? That is by far the easiest and most correct solution.
The second easiest is to just make everything synchronous. That is, if you won't go async all the way, then go sync all the way.
There are a variety of hacks for calling async from sync code that may or may not work in your situation; none of these hacks work everywhere. For more details, see my async brownfield article.
In your case, your initial attempt was to use the Blocking Hack. For this to work appropriately on the pre-Core ASP.NET, you'd have to use ConfigureAwait(false) for every await in that entire method call tree. This is usually possible (not always possible, of course, because library code may or may not use it), but it causes maintenance problems (forget just one, and you've got a race condition that may end in deadlock). I explain why the deadlock happens in detail on my blog.
If you won't go async all the way, and if you can't go sync all the way, then I'd recommend the Boolean Sync Argument Hack, which requires the entire call stack to support both synchronous and asynchronous callers. It would look something like this for the code you posted:
private async Task<Utils.TReportReturn> DoGetReportDataColumnSettingsAsync(AdminTechnicianReportInput input, bool sync)
{
input.CustomerId = m_customerId;
input.UserId = m_userId;
input.TimeOffset = m_timeOffset;
TReportReturn objTReportReturn = new TReportReturn();
objTReportReturn.ReportColumns = await new ReportsEntityContext().GetTechniciansReportColumns<AdminTechnicianReportInput>(input, sync);
objTReportReturn.ReportSettings = await new ReportsEntityContext().GetGeneralSettingsData<AdminTechnicianReportInput>(input, sync);
objTReportReturn.ReportData = await new ReportsDbContext().GetUserListDynamic<AdminTechnicianReportInput>(input, sync);
return objTReportReturn;
}
private Task<Utils.TReportReturn> GetReportDataColumnSettingsAsync(AdminTechnicianReportInput input)
{ return DoGetReportDataColumnSettingsAsync(input, sync: false); }
private Utils.TReportReturn GetReportDataColumnSettings(AdminTechnicianReportInput input)
{ return DoGetReportDataColumnSettingsAsync(input, sync: true).GetAwaiter().GetResult(); }
The key with this pattern is that any method taking a sync parameter must return a completed task when that argument is set to true.

Stuck with the Google API YouTube Search example in ASP.NET MVC4?

Been stuck for days, hoping someone can help me.
I have been trying to run the YouTube 'Search by keyword' example from Google's API examples for .net in a VS 2013 Express for Web MVC4 project, and the ExecuteAsync() calling the Google API never comes back.
I believe the example code works as I tested it in VS 2013 Express for Windows Desktop as a console application and it came back fine. Also the stats in google's developers console tell me the API request is being received.
Here is what I did:
I created a new VS 2013 Express for Web MVC4 project called GoogleTest and installed the 'Install-Package Google.Apis.YouTube.v3' package.
I then added the following model.
public class SearchYouTube
{
public int ID { get; set; }
public async Task RunYouTube()
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = " <MY DEVELOPER KEY HERE> ",
ApplicationName = this.GetType().ToString()
});
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = "googleapi examples"; // Replace with your search term.
searchListRequest.MaxResults = 50;
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = await searchListRequest.ExecuteAsync();
List<string> videos = new List<string>();
List<string> channels = new List<string>();
List<string> playlists = new List<string>();
// Add each result to the appropriate list, and then display the lists of
// matching videos, channels, and playlists.
foreach (var searchResult in searchListResponse.Items)
{
switch (searchResult.Id.Kind)
{
case "youtube#video":
videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.VideoId));
break;
case "youtube#channel":
channels.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.ChannelId));
break;
case "youtube#playlist":
playlists.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.PlaylistId));
break;
}
}
Console.WriteLine(String.Format("Videos:\n{0}\n", string.Join("\n", videos)));
Console.WriteLine(String.Format("Channels:\n{0}\n", string.Join("\n", channels)));
Console.WriteLine(String.Format("Playlists:\n{0}\n", string.Join("\n", playlists)));
}
}
Then I call the above class in the Home controller like so:
public ActionResult Index()
{
ViewBag.Message = "MVC example";
SearchYouTube searchObject = new SearchYouTube();
searchObject.RunYouTube().Wait();
return View();
}
Running this in the debugger, the program steps into but never returns from this line in the SearchYouTube class above:
var searchListResponse = await searchListRequest.ExecuteAsync();
Can anyone help explain what I am doing wrong or what I am missing??
You seem to have a deadlock on your hands because you're doing "sync over async". When you use Task.Wait you're blocking and wasting a thread. After the inner async operation (i.e. await searchListRequest.ExecuteAsync();) completes it evidently needs that same thread to continue processing the rest of the method.
All that happens because of the SynchronizationContext present in ASP.Net which is captured when await is used so that the continuation would be posted to it. When you use ConfigureAwait(false) you're configuring the continuation to not run on the captured context and use the ThreadPool instead.
In console apps there is no SC and so every continuation runs on the ThreadPool. It's as if every await had ConfigureAwait(false).
To solve this deadlock you can use ConfigureAwait(false) or even better, make the MVC method async so you don't need to block synchronously (more on async in MVC):
public async Task<ActionResult> Index()
{
ViewBag.Message = "MVC example";
SearchYouTube searchObject = new SearchYouTube();
await searchObject.RunYouTube();
return View();
}

Handling concurrency exceptions with external API calls

I have the following POST edit action method, which mainly perform two Update actions:-
Edit the object on the external system suing API calls.
Edit the object on our system database.
[HttpPost]
public ActionResult Create(RackJoin rj, FormCollection formValues)
{string controllername = RouteData.Values["controller"].ToString();
if (ModelState.IsValid)
{ var message = "";
var status = "";
long assetid = new long();
XmlDocument doc = new XmlDocument();
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
foreach (string key in formValues)
{
query[key] = this.Request.Form[key];
}
query["username"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiUserName"];
query["password"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiPassword"];
string apiurl = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiURL"];
var url = new UriBuilder(apiurl);
url.Query = query.ToString();
try
{
string xml = client.DownloadString(url.ToString());
doc.LoadXml(xml);
status = doc.SelectSingleNode("/operation/operationstatus").InnerText;
message = doc.SelectSingleNode("/operation/message").InnerText;
}
catch (WebException ex)
{
ModelState.AddModelError(string.Empty, "Error occurred:" + ex.InnerException);
}
}
if (status.ToUpper() == "SUCCESS")
{
repository.InsertOrUpdateRack(rj.Rack, User.Identity.Name, rj.Resource.RESOURCEID);
repository.Save();
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError(string.Empty, message.ToString());
}
}
}
catch (DbUpdateConcurrencyException ex)
{
As shown in the above code I will not do a repository.save() to update the object on our system, unless the API return a “success”.
But currently I am facing the following problem:-
If the API return a “success” but a concurrency exception occurred, then the API will update the object on the external system, but the object will not be updated on our system?
So is there a way to handle this situation?
There's no easy way to solve this situation. One way to handle it would be to ask the designers of the external API expose a method allowing to commit the transaction done in a previous call. Basically your first call will make modifications to the external system but with some boolean flag indicating that those changes are still pending. Then you update your system and in case of success you would call the external API to flag the data from pending to valid.
If you have no control over the external API and it makes the changes to the data from the first call irreversible, then I am afraid that you do not have much choices left. You might remember the state of the object you are modifying on the external system before calling the API and in case of an exception on your system, revert back to the previous state by calling the API with the previous values.

is an async controller apropos here?

I have a system whereby users can upload sometimes large(100-200 MB) files from within an MVC3 application. I would like to not block the UI while the file is uploading, and after some research, it looked like the new AsyncController might let me do what I'm trying to do. Problem is - every example I have seen isn't really doing the same thing, so I seem to be missing one crucial piece. After much futzing and fiddling, here's my current code:
public void CreateAsync(int CompanyId, FormCollection fc)
{
UserProfile up = new UserRepository().GetUserProfile(User.Identity.Name);
int companyId = CompanyId;
// make sure we got a file..
if (Request.Files.Count < 1)
{
RedirectToAction("Create");
}
HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
if (hpf.ContentLength > 0)
{
AsyncManager.OutstandingOperations.Increment();
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, e) =>
{
string fileName = hpf.FileName;
AsyncManager.Parameters["recipientId"] = up.id;
AsyncManager.Parameters["fileName"] = fileName;
};
worker.RunWorkerCompleted += (o, e) => { AsyncManager.OutstandingOperations.Decrement(); };
worker.RunWorkerAsync();
}
RedirectToAction("Uploading");
}
public void CreateCompleted(int recipientId, string fileName)
{
SystemMessage msg = new SystemMessage();
msg.IsRead = false;
msg.Message = "Your file " + fileName + " has finished uploading.";
msg.MessageTypeId = 1;
msg.RecipientId = recipientId;
msg.SendDate = DateTime.Now;
SystemMessageRepository.AddMessage(msg);
}
public ActionResult Uploading()
{
return View();
}
Now the idea here is to have the user submit the file, call the background process which will do a bunch of things (for testing purposes is just pulling the filename for now), while directing them to the Uploading view which simply says "your file is uploading...carry on and we'll notify you when it's ready". The CreateCompleted method is handling that notification by inserting a message into the users's message queue.
So the problem is, I never get the Uploading view. Instead I get a blank Create view. I can't figure out why. Is it because the CreateCompleted method is getting called which shows the Create view? Why would it do that if it's returning void? I just want it to execute silently in the background, insert a message and stop.
So is this the right approach to take at ALL? my whole reason for doing it is with some network speeds, it can take 30 minutes to upload a file and in its current version, it blocks the entire application until it's complete. I'd rather not use something like a popup window if I can avoid it, since that gets into a bunch of support issues with popup-blocking scripts, etc.
Anyway - I am out of ideas. Suggestions? Help? Alternate methods I might consider?
Thanks in advance.
You are doing it all wrong here. Assume that your action name is Create.
CreateAsync will catch the request and should be a void method and returns nothing. If you have attributes, you should apply them to this method.
CreateCompleted is your method which you should treat as a standard controller action method and you should return your ActionResult inside this method.
Here is a simple example for you:
[HttpPost]
public void CreateAsync(int id) {
AsyncManager.OutstandingOperations.Increment();
var task = Task<double>.Factory.StartNew(() => {
double foo = 0;
for(var i = 0;i < 1000; i++) {
foo += Math.Sqrt(i);
}
return foo;
}).ContinueWith(t => {
if (!t.IsFaulted) {
AsyncManager.Parameters["headers1"] = t.Result;
}
else if (t.IsFaulted && t.Exception != null) {
AsyncManager.Parameters["error"] = t.Exception;
}
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult CreateCompleted(double headers1, Exception error) {
if(error != null)
throw error;
//Do what you need to do here
return RedirectToAction("Index");
}
Also keep in mind that this method will still block the till the operation is completed. This is not a "fire and forget" type async operation.
For more info, have a look:
Using an Asynchronous Controller in ASP.NET MVC
Edit
What you want here is something like the below code. Forget about all the AsyncController stuff and this is your create action post method:
[HttpPost]
public ActionResult About() {
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(10000);
if (!System.IO.Directory.Exists(Server.MapPath("~/FooBar")))
System.IO.Directory.CreateDirectory(Server.MapPath("~/FooBar"));
System.IO.File.Create(Server.MapPath("~/FooBar/foo.txt"));
});
return RedirectToAction("Index");
}
Notice that I waited 10 seconds there in order to make it real. After you make the post, you will see the it will return immediately without waiting. Then, open up the root folder of you app and watch. You will notice that a folder and file will be created after 10 seconds.
But (a big one), here, there is no exception handling, a logic how to notify user, etc.
If I were you, I would look at a different approach here or make the user suffer and wait.

Resources