Set HttpContext.Current is not the same in continuation of await - asp.net

From this answer from Stephen Cleary, I understand that the HttpContext flows with the SynchronizationContext. Therefore, given the following piece of code:
public async Task<ActionResult> Index()
{
var outerContext = System.Web.HttpContext.Current = new HttpContext(
new HttpRequest(null, "http://test.com", null),
new HttpResponse(new StreamWriter("C:/Path")));
Console.WriteLine(outerContext.Equals(System.Web.HttpContext.Current));
await Task.Run(
delegate
{
Console.WriteLine(outerContext.Equals(System.Web.HttpContext.Current));
});
Console.WriteLine(outerContext.Equals(System.Web.HttpContext.Current));
return View();
}
I would expect the console output to be:
True
False
True
However, the actual output is:
True
False
False
After debugging, it appears that the context in the continuation is set to the original context of the request, the one it had before I manually set it by calling HttpContext.Current = .... What is the reason for this? Does it get set to the request context somewhere before the code after the await gets executed?

The HttpContext.Current value is not saved and restored by await. await will capture SynchronizationContext.Current and use that to resume execution.
When the request starts, a new request context is created (which includes a specific HttpContext.Current instance). Whenever AspNetSynchronizationContext resumes execution on that request context, it sets HttpContext.Current to the instance for that request context.
So, after the await, HttpContext.Current will be the same instance it was at the beginning of Index, not whatever instance your code assigns to it.

Related

Web API Post not returning response

I have a Web API that gets call by this method:
public async Task<Model> AddModel(string token, Model newModel)
{
HttpContent content = await HttpHelper.Request(token, baseUrl, url, newModel, HttpRequestType.POST);
return await content.ReadAsAsync<Model>();
}
The Web API is successfully called and I can add a Model. This is the Web API method that gets called:
[Route("webapi/models/addModel")]
[HttpPost]
public async Task<ModelDto> AddWithoutResetingDefault(ModelDto newModel)
{
ModelService modelService = new ModelService();
return modelService.AddModel(newModel);
}
The problem is after the successful add, it doesn't return to the calling code anymore (I have a breakpoint that doesn't get hit). There are no console errors in the browser, I enclosed in a try-catch the calling code and the called code but there were no exceptions thrown.
Also, after the first click to add, if I try to refresh the browser, it takes a really long time to reload the browser (I don't know if being async has something to do with it).
(I don't know if being async has something to do with it) - Yes it has
your Api method
public async Task<ModelDto> AddWithoutResetingDefault(ModelDto newModel)
{
ModelService modelService = new ModelService();
return modelService.AddModel(newModel);
}
is marked as Async method, but the code inside is Sync. And that is the problem, if your modelService.AddModel(newModel); is async, then do
return await modelService.AddModel(newModel);
if its not then there is no point in making the AddWithoutResetingDefault
method async, hence remove Aysnc and simply do a sync method like
public ModelDto AddWithoutResetingDefault(ModelDto newModel)
{
ModelService modelService = new ModelService();
return modelService.AddModel(newModel);
}

Using Session with Threads in asp.net

I want to use a Session object through all my pages in my asp.net site, first I Save an object in my session like this, this line of code is in an HttpHandler
HttpContext.Current.Session["DocumnetInfo"] = doc;
after that I created a a thread to manipulate this doc and send the session as parameter to the thread as follows
HttpContext ctx = HttpContext.Current;
Thread t = new Thread(new ThreadStart(() =>
{
// HttpContext.Current = ctx;
SomeMethod(ctx);
}));
t.Start();
and In SomeMethod I read the Session as follows:
private void SomeMethod ( HttpContext ctx)
{
DocResultsBLL doc = (DocResultsBLL)ctx.Session["DocumnetInfo"];
// Here is the logic of the manipulation
// then save the doc in the session back
ctx.Session["DocumnetInfo"]=doc;
Response.Redirect("ResultsPage.aspx");
}
The problem is that I couldn't read the session in the results page.. HttpContext.Current is null.
1-How can I work with session , to send it to a thread, then to get it back outside the thread.
2- Is there any other scenario other than session that is better?
3- How can I stop the Thread if the Client Closed his browser?
The only you can do is like passing http context see the following link and you will understand why its not possible to have session available in multi threading application.
http://blogs.msdn.com/b/webtopics/archive/2009/01/30/why-can-t-i-execute-two-requests-from-the-same-session-simultaneously-for-an-asp-net-application.aspx

Asp.net Web API: HttpClient Download large files breaks

I have a web service (made in Asp.net Web API) that returns an xml file of about 10MB size.
The service has been tested with Fiddler and it is working
I am trying to download the file using HttpClient class. The problem is that the compilator never gets outside the await client.GetAsync() method, even if the API project returned the HttpResponseMessage.
This is my function
public async Task<XDocument> DownloadXmlAsync(string xmlFileName)
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:51734/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
// When the copiler enters this next command, it doesn't get outside anymore
HttpResponseMessage response = await client.GetAsync("api/applications/ApplicationXml?fileName=" + xmlFileName);
response.EnsureSuccessStatusCode();
string stringResponse = await response.Content.ReadAsStringAsync();
XDocument xDoc = new XDocument(stringResponse);
return xDoc;
}
}
I updated also the maxRequestLength in web.config
<httpRuntime maxRequestLength="15360" />
What i am doing wrong?
Edit
Calling the function
public async Task<ActionResult> Index()
{
var xmlTask = DownloadXmlAsync("1.xml");
// doesn't reach here
var result = xmlTask.Result;
return View();
}
You're causing a classic deadlock by calling Result. Instead, you should await the task:
public async Task<ActionResult> Index()
{
var xmlTask = DownloadXmlAsync("1.xml");
// doesn't reach here
var result = await xmlTask;
return View();
}
I explain this deadlock in full on my blog, but the general idea is like this:
ASP.NET only allows one thread to be processing a request at a time.
When you await a Task, the compiler will capture a "context" and use it to resume the method when the Task completes. In the case of ASP.NET, this "context" is a request context.
So when DownloadXmlAsync (asynchronously) waits for GetAsync to complete, it returns an incomplete task to Index.
Index synchronously blocks on that task. This means the request thread is blocked until that task completes.
When the file is received, GetAsync completes. However, DownloadXmlAsync cannot continue because it's trying to resume that "context", and the "context" already has a thread in it: the one blocked on the task.
Hence, deadlock.

Parallel exceptions are being caught

somehow my exceptions seem to being caught by method they are executing in. Here is the code to call the method. As you can see I create a cancellation token with a time out. I register a method to call when the cancellation token fires and then I start a new task. The cancellation token appears to be working OK. As does the registered method.
var cancellationToken = new CancellationTokenSource(subscriber.TimeToExpire).Token;
cancellationToken.Register(() =>
{
subscriber.Abort();
});
var task = Task<bool>.Factory.StartNew(() =>
{
subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
return true;
})
.ContinueWith(anticedant =>
{
if (anticedant.IsCanceled)
{
Counter.Increment(12);
Trace.WriteLine("Request was canceled");
}
if (anticedant.IsFaulted)
{
Counter.Increment(13);
Trace.WriteLine("Request was canceled");
}
if (anticedant.IsCompleted)
{
Counter.Increment(14);
}
The next piece of code is the method that seems to be not only throwing the excetion (expected behavior. but also catching the exception.
public async override Task<bool> ProcessAsync(Message input, CancellationToken cancellationToken)
{
Random r = new Random();
Thread.Sleep(r.Next(90, 110));
cancellationToken.ThrowIfCancellationRequested();
return await DoSomethingAsync(input);
}
The exception is being thrown by the cancellation token but according to intellitrace it is being caught at the end of the method. I have tried a number of different options including throwing my own exception, but no matter what the continuewith function always executes the IsComleted or ran to completion code.
Any ideas on what I am doing wrong?
Thanks
I assume that RunAsync is the same as ProcessAsync.
The exception is being thrown by the cancellation token but according to intellitrace it is being caught at the end of the method.
Yup. Any async method will catch its own exceptions and place them on its returned Task. This is by design.
no matter what the continuewith function always executes the IsComleted or ran to completion code.
Well, let's take another look at the code:
var task = Task<bool>.Factory.StartNew(() =>
{
subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
return true;
})
.ContinueWith(
Consider the lambda passed to StartNew: it calls RunAsync, it ignores the Task that it returns, and then it returns true (successfully). So the Task returned by StartNew will always return successfully. This is why the ContinueWith always executes for a successfully-completed task.
What you really want is to await the Task returned by RunAsync. So, something like this:
var task = Task.Run(async () =>
{
await subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
return true;
})
.ContinueWith(
You're still ignoring the return value of RunAsync (the bool it returns is ignored), but you're not ignoring the Task itself (including cancellation/exception information).
P.S. I'm assuming there's a lot of code you're not showing us; using StartNew/Run to just kick off some async work and return a value is very expensive.
P.P.S. Use await Task.Delay instead of Thread.Sleep.

Problem with null object reference in Url.Action in MVC3 project

I am trying to set up a mocking scenario for my payment processor on a web site. Normally, my site redirects to the processor site, where the user pays. The processor then redirects back to my site, and I wait for an immediate payment notification (IPN) from the processor. The processor then posts to my NotifyUrl, which routes to the Notify action on my payments controller (PayFastController). To mock, I redirect to a local action, which after a conformation click, spawns a thread to post the IPN, as if posted by the processor, and redirects back to my registration process.
My mock processor controller uses the following two methods to simulate the processor's response:
[HttpGet]
public RedirectResult Pay(string returnUrl, string notifyUrl, int paymentId)
{
var waitThread = new Thread(Notify);
waitThread.Start(new { paymentId, ipnDelay = 1000 });
return new RedirectResult(returnUrl);
}
public void Notify(dynamic data)
{
// Simulate a delay before PayFast
Thread.Sleep(1000);
// Delegate URL determination to the model, vs. directly to the config.
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
}
// Use a canned IPN message.
Dictionary<string, string> dict = _payFastIntegration.GetMockIpn(data.paymentId);
var values = dict.ToNameValueCollection();
using (var wc = new WebClient())
{
// Just a reminder we are posting to Trocrates here, from PayFast.
wc.UploadValues(notifyUrl, "POST", values);
}
}
However, I get an 'Object reference not set to an instance of an object.' exception on the following line:
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
data.paymentId has a valid value, e.g. 112, so I'm not passing any null references to the Url.Action method. I suspect I have lost some sort of context somewhere by calling Notify on a new thread. However, if I use just notifyUrl = Url.Action("Notify", "PayFast");, I avoid the exception, but I get a relative action URL, where I need the overload that takes a protocol parameter, as only that overload gives me the absolute URL that WebClient.UploadValues says it needs.
When you are inside the thread you no longer have access to the HttpContext and the Request property which the Url helper relies upon. So you should never use anything that relies on HttpContext inside threads.
You should pass all the information that's needed to the thread when calling it, like this:
waitThread.Start(new {
paymentId,
ipnDelay = 1000,
notifyUrl = Url.Action("Notify", "PayFast", new { paymentId }, "http")
});
and then inside the thread callback:
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = data.notifyUrl;
}

Resources