Visual Studio 2015 async variables not showing in debugger - asp.net-mvc

I've got a .NET Core ASP.NET MVC 6 application and what I'm convinced is a bug in Visual Studio. If I place a break point after an await statement, the object does not show up in Locals, and I cannot mouse over to inspect. But if I use the variable, it still works fine, and it's definitely populated.
Something as simple as this:
public async Task<IActionResult> Index()
{
var location = await _listingLocationService.GetLocationByAddress("123 Fake Street");
return Content(location.Latitude.ToString() + " " +location.Longitude.ToString());
}
If I place the break point on the return statement, I cannot inspect location. It doesn't show up anywhere. I can even remove the await & place a .Result at the end, and still nothing shows. But when I continue, the view displays location.latitude, and location.longitude fine. So I know it's being populated.
For the sake of completeness, I'll include the GetLocationByAddress function as well, which does the same thing, if I place a breakpoint anywhere after the awaits, I can't inspect the variables (even the deserialized list!).
public async Task<Geolocation> GetLocationByAddress(string address)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://maps.googleapis.com/maps/api/geocode/json");
var request = new HttpRequestMessage(HttpMethod.Get, "?address=" + WebUtility.UrlEncode(address) + "&key=...");
var response = await client.SendAsync(request);
var contents = await response.Content.ReadAsStringAsync();
var locationResult = JsonConvert.DeserializeObject<GoogleLocationResult>(contents);
if (locationResult.status == "OK")
{
var result = locationResult.results.First().geometry.location;
return new Geolocation
{
Latitude = result.lat,
Longitude = result.lng
};
}
else
{
return null;
}
}
}

Well, after loosing all day with this issue I've finally found the source of the problem and also the solution...
In my case, the problem has started after updating my project to use the .net core 1.1 Preview 1 following the steps shown here: Link to MSFT .NET Blog
The problem was caused by this line of code in the project.json file:
"buildOptions": {
"debugType": "portable", <---- THIS
"preserveCompilationContext": true,
"emitEntryPoint": true
}
after setting "debugType" to "full", awaited variables started to show again while debugging.
Hope it helps to somebody outthere!

It's not a bug. When the debugger hits the return line, it's because the previous line immediately returned a Task object. Your GetLocation method isn't done running yet (which makes sense since it's making an outbound HTTP call that will absolutely take longer than the milliseconds it took to return a Task). When you hit the return line, your debugger has stopped there, but the incomplete task means that location won't be ready. A better place to put a breakpoint is probably in the return line of your GetLocation method.
And by the way, you'll get a null ref error in your action method if the call made to Google Maps fails.

Related

Force garbage collection Azure Blobstorage after deletion container in ASP.net Core

I have the following code to delete a container on Azure Blobstorage:
public IActionResult DeleteDownloadLink(string container)
{
var connectionString = _configuration["StorageConnectionString"];
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient(container);
_moduleRepository.DeleteDownloadLink(container);
blobContainer.Delete();
return RedirectToAction("ManageDownloadLinks", "Admin");
}
This doesn't delete the container straight away, and I found the following in the documentation:
When a blob is successfully deleted, it is immediately removed from
the storage account’s index and is no longer accessible to clients.
The blob’s data is later removed from the service during garbage
collection.
However, I can still go to the link and download it. No error is returned. So I guess I have two questions:
Is there something wrong with my delete code?
If nothing is wrong with the code, can I force garbage collection so the link is no longer accessible?
I create below sample to test, I can delete container successfully. The only difference between my code and yours is that there is no _moduleRepository.DeleteDownloadLink(container); this line of code.
1. My first suggestion is that you can comment out this line and try again.
2. Check your `StorageConnectionString`, make sure you delete correct container.
[HttpGet("blobtest")]
public async Task<string> DeleteDownloadLink(string container)
{
var connectionString = "DefaultEn***indows.net";
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient(container);
if (await blobContainer.ExistsAsync())
{
Console.WriteLine("Created container {0}", blobContainer.Name);
try
{
var a= blobContainer.Delete();
return "delete successed";
}
catch (Exception)
{
return "delete failed";
throw;
}
}
return "don't exist";
//blobContainer.Delete();
//return RedirectToAction("ManageDownloadLinks", "Admin");
}

DNN 8.0 (DotNetNuke) With async controller, await behavior is not as expected

I am experimenting with an asynchronous call to an Azure Rest Api from a DNN module (this is for a client that uses the DNN platform and an older version of it namely 8.0). I need this call to retrieve the data before returning the View.
I made the controller async in order to be "await" getting the data from the azure rest api.
The behavior is unexpected. The code stops at this breakpoint(as in browser window with the blank view pops up but code after this point does not get executed) and the view is blank and never continues to the code populating the viewmodel for the page:
var result = await httpClient.GetStringAsync(url);
Sometimes result (the db user) is returned properly from the db, but the view is still blank even though the other breakpoints after await have been hit in the controller... The view seems to be returned before the await call is completed? The View window gets focus before await is completed but as a blank page.
Any suggestions about what is going on please?
In the DNN Controller class:
public async Task<ActionResult> Index() : DnnController
{
ViewModel pageVM = new ViewModel();
AuthenticationApi authService = new AuthenticationApi();
var outcome = await authService.GetDbUser(); // AWAIT
// populate the pageVM
.................
return View(pageVM);
}
In the Services folder, in the AuthenticationApi class:
public class AuthenticationApi
{
public async Task<DbUser> GetDbUser()
{
string url = "https://mywebservicename.azurewebsites.net/api/applicants/46";
var httpClient = new HttpClient();
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
try
{
var result = await httpClient.GetStringAsync(url);
return JsonConvert.DeserializeObject<DbUser>(result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
The main response pipeline within DNN Platform is web-forms based and does not currently (even with version 9.6.2) support the usage of async/await in the main pipeline.
There is roadmap plans for this to be supported in version 10.x, however, additional changes are necessary to ensure that language features are not broken.
Some users have had success by adding this
aspnet:UseTaskFriendlySynchronizationContext
Application setting with a value of true, however, it is known to break certain features of DNN Platform such as Localization.

async database call safe

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();
}
};

Mark page as "async = true" for a Visual Studio MVC Project

I am using Edge.js so that I can call Node.js from C#. According to the documentation in the link I would do that similar to the following:
[HttpPost]
public ActionResult Input(InputModel obj)
{
validateInput(obj);
return View();
}
private async void validateInput(object obj)
{
var func = Edge.Func(#"
return function (data, callback){
var username = data.username,
email = data.email;
callback(null, username);
}
");
ViewBag.Msg = (string)await func(obj);
}
However, I get the following run time error:
Additional information: An asynchronous operation cannot be started at this time.
Asynchronous operations may only be started within an asynchronous handler or module or during
certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the
Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an
"async void" method, which is generally unsupported within ASP.NET request processing. Instead,
the asynchronous method should return a Task, and the caller should await it.
My question is two-fold:
1.How do I make the page, async=true. I know how to do this for a web forms project but not a MVC project.
2.Is there a better way to do what I am trying to do? A red flag will probably go up when you see that I am returning void however this is do to the fact that Edge.js is being used. Even so, I have tried returning a Task and then task.Wait() in the calling method but the task never finishes.
After trying some different things, the following solution worked for me.
Even though I answered my own question, and it seems trivial, I am not removing this question as there are not a lot of knowledge on the web about Edge.js.
[HttpPost]
public async Task<ActionResult> Input(InputModel obj)
{
ViewBag.Msg = await validateInput(obj);
return View();
}
private async Task<string> validateInput(object obj)
{
var func = Edge.Func(#"
return function (data, callback){
var username = data.username,
email = data.email;
callback(null, username);
}
");
return (string)await func(obj);
}

Monotouch - EASession output stream gives null error

Looking for some help over here.
When i run this code (below), it crashes when i process it for the second time.
it crashes with an object reference not set.. on the session.outputstream
var session= new EASession(accessory, "net.gebe");
session.OutputStream.Open();
the second time session.outputstream is null. Even when disposing session.
Richard
public void PrintIt()
{
var _accessoryList = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories;
accessory = null;
foreach(var obj in _accessoryList)
{
if(obj.ProtocolStrings.Contains("net.gebe"))
{
accessory = obj;
//break;
}
}
if(accessory == null)
{
//tmg.InfoAlert ("No Printer");
return;
}
var session= new EASession(accessory, "net.gebe");
session.OutputStream.Open();
string str2 = "HELLO THERE PRINTER 1 2 3 4 5";
byte[] printdata2;
ASCIIEncoding encoding2 = new ASCIIEncoding();
printdata2 = encoding2.GetBytes(str2);
uint nlen2 = Convert.ToUInt32 (str2.Length+1);
session.OutputStream.Write(printdata2,nlen2 );
session.OutputStream.Close ();
session.Dispose ();
}
I got mine working now. What I did:
Save the session as a variable in the class
Only create the session if session is null
You may not want to call session.OutputStream.Close() after every print. At least it's something to keep in mind while debugging for your situation.
This will allow for multiple print jobs on the same page without blowing up. session.OutputStream was not null in this case.
I also found that the ViewDidLoad/Unload events weren't great for detecting when the device becomes available/unavailable via the EAAccessoryDidConnectNotification and EAAccessory DidDisconnectNotification observers. Instead I used ViewDidAppear/ViewDidDisappear. In those methods, I tear down the session, and when I come back in to print a new job, the session gets created and OutputStream is assigned.
Lastly, I wasn't getting events fired for my device via session.OutputStream.OnEvent. Not sure if it's my device, a MonoTouch bug, or just a generic bug of mine yet.
UPDATE: Check out this nicely wrapped implementation of AccessoryAdapter
You need to list the external accessories you're going to use in your Info.plist.
There is some documentation on this on apple's site:
http://developer.apple.com/library/ios/#featuredarticles/ExternalAccessoryPT/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009502
Here is a screenshot of how to set this value in Info.plist:
http://screencast.com/t/AYmOWjf8wkL
(This is from here: https://bugzilla.xamarin.com/show_bug.cgi?id=1635#c1)

Resources