I have a method that is ran in the main application thread, but creates new Task
for long-running operation and await
s it's result.
public async Task<CalculatorOutputModel> CalculateXml(CalculatorInputModel input)
{
// 1
return await Task.Factory.StartNew(
new Func<CalculatorOutputModel> (() =>
{
// 2
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms)) // 3
{
var result = Parser.Parse(rawResult);
return result;
}
}
}),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
My problem is that the AppContext in points (1) and (2) are "a bit" different. In point 1 there is usual application context in Current
property, that has all the data I need. In point 2, as you understand, there is null
reference in AppContext.Current
, so I can't access any data. The problem with accessing context in point 2 seems to be easy, just "catch" current context in local variable or pass it as the parameter. The problem for me is more difficult because I need to access the context somewhere in the deep of line marked as "3".
The class itself is derived from System.ServiceModel.ClientBase<TChannel>
and the place where I need to get access to the context is the class that implements IClientMessageInspector
.
class CalculatorMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (AppContext.Current != null)
{
// never goes here
}
}
}
Just to clarify, here is the call stack (I can't pass my context inside required method):
So:
Channel
's method because it does not make any sense;Channel
because it is proxy class;CalculatorMessageInspector
because it is created within the place where current context is already null. Can anyone advise any method how can I stay within the same context in another thread? Or, at least, how can I pass parameter from the place marked "2" inside the methods hierarchy? Maybe, I can use SynchronizationContext
somehow to achieve it? Thanks a lot for any suggestions.
Update
AppContext.Current
consider to be the same as HttpContext.Current != null ? HttpContext.Current.Items[AppContextKey] : null
Update 2
So it seems, that in this case there is no common solution to persist the context. Hence, the only applicable solution in this concrete situation (that is quite specific) is to capture required parameters using closure and save then in an object, that will be available in required method (for me worked adding properties to the implementator of IEndpointBehavior
, but that solution is a bit odd). Thereby, the most applicable solution is to throw away the asynchronous wrapping over the sync call, so the AppContext
never "goes away". Mark Stephen's answer as the right then.
To use async
and await
on ASP.NET, you must target .NET 4.5 and turn off quirks mode. The best way to do this is to set /configuration/system.web/httpRuntime@targetFramework="4.5"
in your web.config.
Also, you shouldn't use Task.Run
or Task.Factory.StartNew
on ASP.NET. So your code should look more like this:
public CalculatorOutputModel CalculateXml(CalculatorInputModel input)
{
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms))
{
var result = Parser.Parse(rawResult);
return result;
}
}
}