Task.Wait locks the flow of the controller in MVC5 - c#

We are consuming a WebAPI from an MVC site. We did a proof of concept from a console application like this, consuming AddUser Method, which reguster a new user in our DB.
static void Main()
{
M_Users user = new M_Users();
var newUser = new M_Users()
{
Password = "123",
FirstName = "Andrew",
Email = "someAndrew#someplace.com",
AdminCode = 1,
IsDisabled = false,
CountryId = 48,
CityId = 149,
DepartmentId = 3,
CreationDate = DateTime.Now,
CreatorUserId = 1
};
var star = RunAsync(newUser);
star.Wait();
//THIS LINE IS REACHED ALWAYS
Console.WriteLine(star.Result.UserID);
Console.ReadLine();
}
static async Task<M_Users> AddUserAsync(M_Users newUser)
{
M_Users user = new M_Users();
string requestUri = "api/User/AddUser";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44873/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
HttpResponseMessage response = await client.PostAsJsonAsync<M_Users>(requestUri, newUser);
if (response.IsSuccessStatusCode)
{
Uri newUserUrl = response.Headers.Location;
user = await response.Content.ReadAsAsync<M_Users>();
}
return user;
}
}
This AddUserAsync method is called exactly the same in both cases,console application and MVC application, and methods in the biz layer are also the same, and not depicted here for doesn´t seem relevant.
In Console it just worked. User is registered and console printed the id which the user were saved with in the BD. This is pretty much what we needed to proof. That from a sync method we can invoke an async method. Now we try the same from the controler of our site, like this.
public ActionResult Index()
{
User spUser = null;
var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
var model = new LoginViewModel();
// here we invoke sharepoint context for getting user info to bild the object
M_Users new_user = new M_Users()
{
Password = "123",
FirstName = spUser.Title,
Email = spUser.LoginName.Split('|')[2].ToString(),
AdminCode = 1,
IsDisabled = false,
CountryId = 48,
CityId = 149,
DepartmentId = 3,
CreationDate = DateTime.Now,
CreatorUserId = 1
};
var returnedUser = AddUserAsync(new_user);
returnedUser.Wait(); //HERE IT STOPS RUNNING
//THIS LINE IS NEVER REACHED
ViewBag.UserId = returnedUser.Result.UserID;
return View(model);
}
What needs to be done for the execution to continue.
We also tried ContinueWith() and it runs, but the async method is not executed and nothing is recorded in the DB. More specific, the method is invoked but it seem to go over it and retunrs nothing. :(

This is pretty much what we needed to proof. That from a sync method we can invoke an async method.
That's the wrong lesson to learn. Console applications use a different threading framework than ASP.NET, and that's what's tripping you up.
I explain it in full on my blog; the gist of it is that the await captures a "context" and uses that to resume the async method. In a Console app, this "context" is the thread pool context, so the async method continues running on some arbitrary thread pool thread, and doesn't care that you've blocked a thread calling Wait. However, ASP.NET has a request context that only allows in one thread at a time, so the async method is attempting to resume within that request context but it can't because there's already a thread blocked in that context.
The best solution is to allow async to grow through the codebase:
public Task<ActionResult> Index()
{
User spUser = null;
var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
var model = new LoginViewModel();
M_Users new_user = new M_Users()
{
Password = "123",
FirstName = spUser.Title,
Email = spUser.LoginName.Split('|')[2].ToString(),
AdminCode = 1,
IsDisabled = false,
CountryId = 48,
CityId = 149,
DepartmentId = 3,
CreationDate = DateTime.Now,
CreatorUserId = 1
};
var returnedUser = await AddUserAsync(new_user);
ViewBag.UserId = returnedUser.UserID;
return View(model);
}

Related

System.Threading.Timer async - Merge updates on Timer Thread back to Master Thread

I am quite new to threading so I do apologise if this makes not much sense or I am missing something obvious. Basically I have a list of items List<Patients> in memory containing 4 entries.
public class PatientAreaController : Controller
{
private List<Patient> patients = new List<Patient>()
{
new Patient { ID = 0, FamilyName = "Hill", GivenName = "Chris", LastSelectedDate = DateTime.Now },
new Patient { ID = 1, FamilyName = "Stephens", GivenName = "Sion", LastSelectedDate = DateTime.Now },
new Patient { ID = 2, FamilyName = "Murray", GivenName = "Thomas", LastSelectedDate = DateTime.Now },
new Patient { ID = 3, FamilyName = "Dupre", GivenName = "Pierre", LastSelectedDate = DateTime.Now }
};
I then have a System.Threading.Timer running on async which calls a method that makes changes to the said list. This keeps running in the background forever.
public ActionResult Index(string sortOrder)
{
Timer timer = new Timer(async (e) =>
{
await Task.Delay(10000);
UpdatePatientsList();
}, null, 0, 10000);
return View(patients);
}
The method UpdatePatientsList():
private void UpdatePatientsList()
{
switch (addOrRemovePatient)
{
case "Add":
patients.Add(threadPatient);
break;
case "Remove":
Random rnd = new Random();
int randomId = rnd.Next(3);
threadPatient = patients.Find(x => x.ID.Equals(randomId));
patients.Remove(threadPatient);
break;
default:
break;
}
if (addOrRemovePatient == "Remove")
addOrRemovePatient = "Add";
else
addOrRemovePatient = "Remove";
}
The idea being that every 10 seconds this method is called which removes an item from the list then 10 seconds later it adds it back in.
On the clientside I then have a setInterval call back to a server to retrieve this list.
setInterval(function () {
$("#dvPatientListResults").hide().load('#(Url.Action("GetFullAndPartialViewModel", "PatientArea", null, Request.Url.Scheme))').fadeIn('slow');
}, 10000);
This setInterval refreshes a table every 10 seconds with the data in patientsList in the GetFullAndPartialViewModel method call:
public ActionResult GetFullAndPartialViewModel()
{
return PartialView("PatientList", patients);
}
However, the list retrieved is never showing the updates made by the timer and ALWAYS returns the 4 items. If I step through the code I can see clearly that the timer thread has the list with 3 items but the client call is always returning the full list of 4.
I guess it is because the timer is on a separate async thread and this is not what is being referenced by the ajax call back. My initial thoughts are that maybe I somehow need to merge the timer thread back into the master thread taking the updated list with it - however if this is even a thing I cannot figure out how to do it. Is there a way to achieve what I am looking for?
I have subsequently advised that SignalR is worth looking at - however I am time limited on this and don't really have time to figure out SignalR (although I may give it a go!)
So your main issue is that ASP.NET controllers are transient... meaning they are created new for each request directed at said controller.
A quick fix for your issue is to do this:
private static List<Patient> patients = new List<Patient>()
{
new Patient { ID = 0, FamilyName = "Hill", GivenName = "Chris", LastSelectedDate = DateTime.Now },
new Patient { ID = 1, FamilyName = "Stephens", GivenName = "Sion", LastSelectedDate = DateTime.Now },
new Patient { ID = 2, FamilyName = "Murray", GivenName = "Thomas", LastSelectedDate = DateTime.Now },
new Patient { ID = 3, FamilyName = "Dupre", GivenName = "Pierre", LastSelectedDate = DateTime.Now }
};
Make that list static, it will then persist for the lifetime of the application no matter how many controllers are created.
What was happening before is it was returning a new list every time you made a request.
A side note: you should look in to IHostedService or BackgroundService to do these kinds of things. Doing this inside of a controller isn't very "Controller"-like.

C# post JSON data to REST API

I am new to REST API JSON but have been able to consume a few API with asp.net. Here is the problem I am currently facing.
I have been able to send JSON data to an API using this method.
public void PostData()
{
string sName = sysName.Text;
string sDescrip = sysDescrip.Text;
var httpclient = new HttpClient();
httpclient.BaseAddress = new Uri("http://33.248.292.99:8094/bizzdesk/sysconfig/api/");
var sys = new Bizsys(){name= sName, description= sDescrip};
httpclient.PostAsJsonAsync("system", sys);
}
it work just fine.
Now I modify the code in order to accommodate more values thus:
var httpclient = new HttpClient();
// ArrayList paramList = new ArrayList();
httpclient.BaseAddress = new Uri("http://179.683.197.115:9091/tua-slte/api/organisations/");
var org = new Organisation() { id=1, name = "inno", abbreviation = "abx", type="school", sort = 7, isShow = true, createdBy=8, createdDate = "10/04/2017", editedBy = 11, editDate="11/04/2017"};
var bas = new Basic() { id=1, username = "inno", password = "123", firstName="Innocent", lastName="Ujata", email = "ea#bizz.co", mobile = "123456", photo="10201001", loginIp="127.0.0.1", loginDate="10/04/2017", locked=false, organisation = org, createdDate = "10/04/2017", editedBy = 11, editDate="11/04/2017", admin=true};
var org2 = new Organisation2() { id=1, name = "inno", abbreviation = "abx", type="school", sort = 7, isShow = true, createdBy=17, createdDate = "10/04/2017", editedBy = 09, editDate="11/04/2017"};
var hq = new HeadQuarter() { zonId=09, organisation = org2, zonCode = "123", zonName = "Abuja", zonAddress = "123456", zonCity = "Abuja", zonPostalCode = "120076", zonEmail = "answers", zonPhoneNumber = "0908765", zonFaxNumber = "1212", zonState = "FCT", createdBy=17, createdDate = "10/04/2017", editedBy = 11, editDate="11/04/2017", version=1};
var examp = new RootObject() {basic=bas, headQuarter=hq };
var status = httpclient.PostAsJsonAsync("register", examp);
return status;
It keep returning this:
Id = 19, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
I hard coded the data to see it work first before making it dynamic.
I have tried using await async method too the result is the same.
all the questions and answers I have seen here are not similar.
What does that mean, and what am I getting wrong?
You are calling an async method, but not awaiting it's results.
Change:
var status = httpclient.PostAsJsonAsync("register", examp);
to
var status = await httpclient.PostAsJsonAsync("register", examp);
Extension method PostAsJsonAsync returns Task<HttpResponseMessage>. You grab this task and see it's details. If you want to return status code of completed request, you should await for task completion and then grab status code from response message:
private async Task<HttpStatusCode> PostSomethingAsync()
{
...
var response = await httpclient.PostAsJsonAsync("register", examp);
return response.StatusCode;
}
Or you can synchronously wait for request completion:
private HttpStatusCode PostSomething()
{
...
var response = httpclient.PostAsJsonAsync("register", examp).Result;
return response.StatusCode;
}
That's cause you are not awaiting the call like
var status = await httpclient.PostAsJsonAsync("register", examp);
Or, use ConfigureAwait()
var status = httpclient.PostAsJsonAsync("register", examp).ConfigureAwait(false);

Stripe.net Method not found: 'Void Stripe.StripeCustomerCreateOptions.set_Card(Stripe.StripeCreditCardOptions)'

I'm using latest Stripe.net version
await SubscriptionsFacade.SubscribeUserAsync(user, planId, taxPercent: taxPercent);
raises
[MissingMethodException: Method not found: 'Void Stripe.StripeCustomerCreateOptions.set_Card(Stripe.StripeCreditCardOptions)'.]
Has something changed? I updated to the latest version and now my Stripe.net app is broken. Did Stripe introduce a new way of creating cards?
Here's the full code:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var userIP = GeoLocation.GetUserIP(Request).Split(':').First();
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
RegistrationDate = DateTime.UtcNow,
LastLoginTime = DateTime.UtcNow,
IPAddress = userIP,
IPAddressCountry = GeoLocationHelper.GetCountryFromIP(userIP),
BillingAddress = new BillingAddress()
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// Create Stripe user
var taxPercent = user.IPAddressCountry != null && EuropeanVat.Countries.ContainsKey(user.IPAddressCountry) ?
EuropeanVat.Countries[user.IPAddressCountry] : 0;
// if no plan set, default to professional
var planId = string.IsNullOrEmpty(model.SubscriptionPlan)
? "starter"
: model.SubscriptionPlan;
var customer = new StripeCustomerService();
var customerInfo = customer.Get(user.CustomerIdentifier);
await SubscriptionsFacade.SubscribeUserAsync(user, planId, taxPercent: taxPercent);
await UserManager.UpdateAsync(user);
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
await UserManager.EmailService.SendWelcomeEmail(user.UserName, user.Email);
TempData["flash"] = new FlashSuccessViewModel("Congratulations! Your account has been created.");
return RedirectToAction("Index", "Notes");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
and SubscribeUserAsync:
https://github.com/pedropaf/saas-ecom/blob/1370ac169807e97ffb7414610d5be4de4a3cc9ae/SaasEcom.Core/Infrastructure/Facades/SubscriptionsFacade.cs
as far as I can tell SubscribeUserAsync is requiring a card with its call.
private async Task<Subscription> SubscribeUserAsync
(SaasEcomUser user, string planId, CreditCard creditCard, int trialInDays = 0, decimal taxPercent = 0)
or
public async Task<Subscription> SubscribeUserAsync
(SaasEcomUser user, string planId, decimal taxPercent = 0, CreditCard creditCard = null)
since you are subscribing a user it probably wants a credit card to go with it. I would add either a card via creating a token or via calling and existing one with
var customer = new StripeCustomerService();
var customerInfo = customer.Get(user.CustomerIdentifier);
//then store card with customerInfo.DefaultSourceId somewhere and use it

UserManager.CreateAsync hangs on execution from Unit Test but not Postman

I'm really baffled right now. So I've been working on a Web API and unit testing the project as I'm going and just made a change with a lot of controllers recently but completely left the AccountController untouched. In my AccountController I have the function Register() which has been working great for the past 2 months everytime I test. Now all of a sudden though when I enter Register and call,
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
My program hangs and won't come back. I tested some things out and this only happens when called from my unit test (Which also hasn't changed) and when I call Register from Postman with the exact same JSON it works just fine.
Register API Function
// POST api/Account
[AllowAnonymous]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email, Active = true, CreationDate = DateTime.Now };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Test Function
[TestMethod]
public void Register_Pass()
{
// Arrange & Act
var db = new WizardSwearsDB();
new DBCleanup().RemoveRegisteredUser(userName);
string apiCall = "";
new
{
UserName = userName,
Email = email,
Password = password,
ConfirmPassword = password
}.Post(baseUrl + apiCall);
var user = (from u in db.AspNetUsers
where u.UserName == userName
select u).FirstOrDefault();
// Assert
Assert.IsNotNull(user);
Assert.AreEqual(userName, user.UserName);
}
JSON Post Functionality
public static void Post(this object obj, string url, string securityToken = null)
{
var request = WebRequest.Create(url);
obj.Request<string>(url, postJsonRequestBuilder, null, securityToken);
}
private static Func<string, object, string, WebRequest> postJsonRequestBuilder = (url, o, st) => jsonRequestBuilder(url, o, "POST", st);
private static Func<string, object, string, string, WebRequest> jsonRequestBuilder = delegate(string url, object obj, string method, string securityToken)
{
WebRequest request = WebRequest.Create(url);
if (!string.IsNullOrEmpty(securityToken)) request.Headers.Add("Authorization", string.Format("Bearer {0}", securityToken));
request.Method = method;
if (obj != null)
{
request.ContentType = "application/json";
var payload = encoding.GetBytes(JsonConvert.SerializeObject(obj));
request.ContentLength = payload.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(payload, 0, payload.Length);
}
}
return request;
};
Remove Registered User Function and Remove Employee Function
public void RemoveRegisteredUser(string userName)
{
// Open up a connection to the database
var db = new WizardSwearsDB();
// Get the user from the ASPNetUsers table
var dbUserASP = (from b in db.AspNetUsers
where b.UserName == userName
select b).FirstOrDefault();
// Remove the user from both tables and save changes
if (dbUserASP != null)
{
db.AspNetUsers.Remove(dbUserASP);
db.SaveChanges();
}
RemoveEmployee(userName);
}
public void RemoveEmployee(string userName)
{
// Open up a connection to the database
var db = new WizardSwearsDB();
// Get the user from the ASPNetUsers table
var employee = (from b in db.Employees
where b.UserName == userName
select b).FirstOrDefault();
// Remove the user from both tables and save changes
if (employee != null)
{
db.Employees.Remove(employee);
db.SaveChanges();
}
}
So none of these functions have changed at all, and my guess is it has to do something with await, but I just can't figure out why it is behaving this way.
Just solved the problem. Did a full restart of my computer and Visual Studio and voila. On a side note, anyone familiar with why this fixed the issue? Maybe just a Visual Studio bug?
I had the same issue arising from trying to create a user with an existing username - no error, just nothing, blank.

MVC 6 asynchronous action

So I've started my proof-of-concept for converting my nHibernate website to using Dapper.
My action method that I'm working appears to be working right now:
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
var invoice = invoiceRepo.GetInvoiceAsync(19992031);
var allInvoices = invoiceRepo.GetAllInvoicesAsync();
var model = new Models.AboutModel
{
Invoice = invoice.Result,
AllInvoices = allInvoices.Result
};
return View(model);
}
But then I realized/remembered, that in order for it to be asynchronous, I need to have Task on the action, like this:
public Task<IActionResult> About()
{
ViewData["Message"] = "Your application description page.";
var invoice = invoiceRepo.GetInvoiceAsync(19992031);
var allInvoices = invoiceRepo.GetAllInvoicesAsync();
var model = new Models.AboutModel
{
Invoice = invoice.Result,
AllInvoices = allInvoices.Result
};
return View(model);
}
But as soon as I do that, it tells me that I need to await something. In all of the examples I've seen, it just shows something like this:
var result = await repo.Get(param);
But I'm already doing the "awaiting" in my repos.
public async Task<Invoice> GetInvoiceAsync(int invoiceId)
{
const string query = "select InvoiceId, Name [InvoiceName] from dbo.Invoice where InvoiceId = #invoiceId";
using (var conn = GetConnection())
{
var dp = new DynamicParameters();
dp.Add("#invoiceId", invoiceId);
await conn.OpenAsync();
var invoiceResult = await conn.QueryAsync<Invoice>(query, dp, null, 30, CommandType.Text);
var invoice = invoiceResult.SingleOrDefault();
return invoice;
}
}
public async Task<List<Invoice>> GetAllInvoicesAsync()
{
const string query = "select InvoiceId, Name [InvoiceName] from dbo.Invoice where SalesPeriodId >= 17";
using (var conn = GetConnection())
{
await conn.OpenAsync();
var invoiceResult = await conn.QueryAsync<Invoice>(query, null, null, 30, CommandType.Text);
var invoices = invoiceResult.Take(1000).ToList();
return invoices;
}
}
So the whole point in me switching to asynchronous is to be able to do both of my calls asynchronously, then merge the results together when returning.
How do I change my controller action to do this asynchronously, while having the Task<IActionResult>? Like this:
public Task<IActionResult>About() {}
Update: is this correct then?
public async Task<IActionResult> About()
{
ViewData["Message"] = "Your application description page.";
var invoice = invoiceRepo.GetInvoiceAsync(19992031);
var allInvoices = invoiceRepo.GetAllInvoicesAsync();
var model = new Models.AboutModel
{
Invoice = await invoice,
AllInvoices = await allInvoices
};
return View(model);
}
Will this do both repo calls asynchronously (in parallel)?
You need to await in your controller too. Rule of thumb: Never say .Result, instead say await.
You should also declare your action method as public async as well.
Update: That would be the correct way to asynchronously call your repositories. The database calls should happen in parallel because both tasks are started before anything is awaited. You can always see this yourself by putting debug logging at the start and end of your DB methods and seeing that you get "start 1 start 2 end 1 end 2" or something similar instead of "start 1 end 1 start 2 end 2" if your queries are reasonably slow.

Categories

Resources