Calling Task-based methods from ASMX - c#

I have a recent experience I'd like to share that may be helpful to anyone having to maintain a legacy ASMX web service that must be updated to call Task-based methods.
I've recently been updating an ASP.NET 2.0 project that includes a legacy ASMX web service to ASP.NET 4.5. As part of the update, I introduced a Web API interface to allow advanced automation of the application. The ASMX service has to co-exist with the new API for backwards-compatibility.
One of the functions of the application is to be able to request data from external data sources (industrial plant historians, bespoke web services, etc.) on behalf of the caller. As part of the upgrade, I re-wrote substantial parts of the data access layer to asynchronously request data using a Task-based asynchronous pattern. Given that it's not possible to use aync/await in an ASMX service, I modified the ASMX methods to make blocking calls to the asynchronous methods i.e. calling the Task-based method and then using Task.WaitAll to block the thread until the Task completes.
When calling any ASMX method that was calling a method returning Task or Task<T> under the hood, I found that the request always timed out. When I stepped through the code, I could see that that the asynchronous code was successfully executing, but the call to Task.WaitAll never detected that the task had completed.
This caused a major headache: how could the ASMX service happily co-exist with the new asynchronous data access capabilities?

I've recently been updating an ASP.NET 2.0 project that includes a legacy ASMX web service to ASP.NET 4.5.
The first thing to do is ensure that httpRuntime#targetFramework is set to 4.5 in your web.config.
the parent task (i.e. the method call in the ASMX that returned a Task) was never detected as completing.
This is actually a classic deadlock situation. I describe it in full on my blog, but the gist of it is that await will (by default) capture a "context" and use that to resume the async method. In this case, that "context" is an ASP.NET request context, which only allows one thread at a time. So, when the asmx code further up the stack blocks on the task (via WaitAll), it is blocking a thread in that request context, and the async method cannot complete.
Pushing the blocking wait to a background thread would "work", but as you note it is a bit brute-force. A minor improvement would be to just use var result = Task.Run(() => MethodAsync()).Result;, which queues the background work to the thread pool and then blocks the request thread waiting for it to complete. Alternatively, you may have the option of using ConfigureAwait(false) for every await, which overrides the default "context" behavior and allows the async method to continue on a thread pool thread outside the request context.
But a much better improvement would be to use asynchronous calls "all the way". (Side note: I describe this in more detail in an MSDN article on async best practices).
ASMX does allow asynchronous implementations of the APM variety. I recommend that you first make your asmx implementation code as asynchronous as possible (i.e., using await WhenAll rather than WaitAll). You'll end up with a "core" method that you then need to wrap in an APM API.
The wrapper would look something like this:
// Core async method containing all logic.
private Task<string> FooAsync(int arg);
// Original (synchronous) method looked like this:
// [WebMethod]
// public string Foo(int arg);
[WebMethod]
public IAsyncResult BeginFoo(int arg, AsyncCallback callback, object state)
{
var tcs = new TaskCompletionSource<string>(state);
var task = FooAsync(arg);
task.ContinueWith(t =>
{
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(t.Result);
if (callback != null)
callback(tcs.Task);
});
return tcs.Task;
}
[WebMethod]
public string EndFoo(IAsyncResult result)
{
return ((Task<string>)result).GetAwaiter().GetResult();
}
This gets a bit tedious if you have a lot of methods to wrap, so I wrote some ToBegin and ToEnd methods as part of my AsyncEx library. Using these methods (or your own copy of them if you don't want the library dependency), the wrappers simplify nicely:
[WebMethod]
public IAsyncResult BeginFoo(int arg, AsyncCallback callback, object state)
{
return AsyncFactory<string>.ToBegin(FooAsync(arg), callback, state);
}
[WebMethod]
public string EndFoo(IAsyncResult result)
{
return AsyncFactory<string>.ToEnd(result);
}

Upon further investigation, I discovered that sub-tasks created by the initial task could be awaited without any problems, but the parent task (i.e. the method call in the ASMX that returned a Task<T>) was never detected as completing.
The investigation led me to theorise that there was some sort of incompatibility between the legacy Web Services stack and the Task Parallel Library. The solution that I came up with involves creating a new thread to run the Task-based method calls, the idea being that a separate thread would not be subject to thread/task management incompatibilities that existed in the thread processing the ASMX request. To this end I created a simple helper class that will run a Func<T> in a new thread, block the current thread until the new thread terminates and then returns the result of the function call:
public class ThreadRunner<T> {
// The function result
private T result;
//The function to run.
private readonly Func<T> function;
// Sync lock.
private readonly object _lock = new object();
// Creates a new ThreadRunner<T>.
public ThreadRunner(Func<T> function) {
if (function == null) {
throw new ArgumentException("Function cannot be null.", "function");
}
this.function = function;
}
// Runs the ThreadRunner<T>'s function on a new thread and returns the result.
public T Run() {
lock (_lock) {
var thread = new Thread(() => {
result = function();
});
thread.Start();
thread.Join();
return result;
}
}
}
// Example:
//
// Task<string> MyTaskBasedMethod() { ... }
//
// ...
//
// var tr = new ThreadRunner<string>(() => MyTaskBasedMethod().Result);
// return tr.Run();
Running the Task-based method in this way works perfectly and allows the ASMX call to complete successfully, but it's obviously a bit brute-force to spawn a new thread for every asynchronous call; alternatives, improvements or suggestions are welcome!

This may be an old topic but it contains the best answer I have been able to find to help maintain legacy code using ASMX and WebMethod to call newer async functions synchronously.
I am new to contributing to stackoverflow so I don't have the reputation to post a comment to Graham Watts solution. I shouldn't really be responding to another answer - but what other choice do I have.
Graham's answer has proved to be a good solution for me. I have a legacy application that is used internally. Part of it called an external API which has since been replaced. To use the replacement the legacy app was upgraded to .NET 4.7 as the replacement uses Tasks extensively. I know the "right" thing to do would be to rewrite the legacy code but there is no time or budget for such an extensive exercise.
The only enhancement I had to make was to capture exceptions. This may not be the most elegant solution but it works for me.
public class ThreadRunner<T>
{
// Based on the answer by graham-watts to :
// https://stackoverflow.com/questions/24078621/calling-task-based-methods-from-asmx/24082534#24082534
// The function result
private T result;
//The function to run.
private readonly Func<T> function;
// Sync lock.
private readonly object _lock = new object();
// Creates a new ThreadRunner<T>.
public ThreadRunner(Func<T> function)
{
if (function == null)
{
throw new ArgumentException("Function cannot be null.", "function");
}
this.function = function;
}
Exception TheException = null;
// Runs the ThreadRunner<T>'s function on a new thread and returns the result.
public T Run()
{
lock (_lock)
{
var thread = new Thread(() => {
try
{
result = function();
}catch(Exception ex)
{
TheException = ex;
}
});
thread.Start();
thread.Join();
if (TheException != null)
throw TheException;
return result;
}
}
}
// Example:
//
// Task<string> MyTaskBasedMethod() { ... }
//
// ...
//
// var tr = new ThreadRunner<string>(() => MyTaskBasedMethod().Result);
// return tr.Run();

Related

KeyVaultClient hangs at GetSecretAsync [duplicate]

I have a multi-tier .Net 4.5 application calling a method using C#'s new async and await keywords that just hangs and I can't see why.
At the bottom I have an async method that extents our database utility OurDBConn (basically a wrapper for the underlying DBConnection and DBCommand objects):
public static async Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function)
{
string connectionString = dataSource.ConnectionString;
// Start the SQL and pass back to the caller until finished
T result = await Task.Run(
() =>
{
// Copy the SQL connection so that we don't get two commands running at the same time on the same open connection
using (var ds = new OurDBConn(connectionString))
{
return function(ds);
}
});
return result;
}
Then I have a mid level async method that calls this to get some slow running totals:
public static async Task<ResultClass> GetTotalAsync( ... )
{
var result = await this.DBConnection.ExecuteAsync<ResultClass>(
ds => ds.Execute("select slow running data into result"));
return result;
}
Finally I have a UI method (an MVC action) that runs synchronously:
Task<ResultClass> asyncTask = midLevelClass.GetTotalAsync(...);
// do other stuff that takes a few seconds
ResultClass slowTotal = asyncTask.Result;
The problem is that it hangs on that last line forever. It does the same thing if I call asyncTask.Wait(). If I run the slow SQL method directly it takes about 4 seconds.
The behaviour I'm expecting is that when it gets to asyncTask.Result, if it's not finished it should wait until it is, and once it is it should return the result.
If I step through with a debugger the SQL statement completes and the lambda function finishes, but the return result; line of GetTotalAsync is never reached.
Any idea what I'm doing wrong?
Any suggestions to where I need to investigate in order to fix this?
Could this be a deadlock somewhere, and if so is there any direct way to find it?
Yep, that's a deadlock all right. And a common mistake with the TPL, so don't feel bad.
When you write await foo, the runtime, by default, schedules the continuation of the function on the same SynchronizationContext that the method started on. In English, let's say you called your ExecuteAsync from the UI thread. Your query runs on the threadpool thread (because you called Task.Run), but you then await the result. This means that the runtime will schedule your "return result;" line to run back on the UI thread, rather than scheduling it back to the threadpool.
So how does this deadlock? Imagine you just have this code:
var task = dataSource.ExecuteAsync(_ => 42);
var result = task.Result;
So the first line kicks off the asynchronous work. The second line then blocks the UI thread. So when the runtime wants to run the "return result" line back on the UI thread, it can't do that until the Result completes. But of course, the Result can't be given until the return happens. Deadlock.
This illustrates a key rule of using the TPL: when you use .Result on a UI thread (or some other fancy sync context), you must be careful to ensure that nothing that Task is dependent upon is scheduled to the UI thread. Or else evilness happens.
So what do you do? Option #1 is use await everywhere, but as you said that's already not an option. Second option which is available for you is to simply stop using await. You can rewrite your two functions to:
public static Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function)
{
string connectionString = dataSource.ConnectionString;
// Start the SQL and pass back to the caller until finished
return Task.Run(
() =>
{
// Copy the SQL connection so that we don't get two commands running at the same time on the same open connection
using (var ds = new OurDBConn(connectionString))
{
return function(ds);
}
});
}
public static Task<ResultClass> GetTotalAsync( ... )
{
return this.DBConnection.ExecuteAsync<ResultClass>(
ds => ds.Execute("select slow running data into result"));
}
What's the difference? There's now no awaiting anywhere, so nothing being implicitly scheduled to the UI thread. For simple methods like these that have a single return, there's no point in doing an "var result = await...; return result" pattern; just remove the async modifier and pass the task object around directly. It's less overhead, if nothing else.
Option #3 is to specify that you don't want your awaits to schedule back to the UI thread, but just schedule to the thread pool. You do this with the ConfigureAwait method, like so:
public static async Task<ResultClass> GetTotalAsync( ... )
{
var resultTask = this.DBConnection.ExecuteAsync<ResultClass>(
ds => return ds.Execute("select slow running data into result");
return await resultTask.ConfigureAwait(false);
}
Awaiting a task normally would schedule to the UI thread if you're on it; awaiting the result of ContinueAwait will ignore whatever context you are on, and always schedule to the threadpool. The downside of this is you have to sprinkle this everywhere in all functions your .Result depends on, because any missed .ConfigureAwait might be the cause of another deadlock.
This is the classic mixed-async deadlock scenario, as I describe on my blog. Jason described it well: by default, a "context" is saved at every await and used to continue the async method. This "context" is the current SynchronizationContext unless it it null, in which case it is the current TaskScheduler. When the async method attempts to continue, it first re-enters the captured "context" (in this case, an ASP.NET SynchronizationContext). The ASP.NET SynchronizationContext only permits one thread in the context at a time, and there is already a thread in the context - the thread blocked on Task.Result.
There are two guidelines that will avoid this deadlock:
Use async all the way down. You mention that you "can't" do this, but I'm not sure why not. ASP.NET MVC on .NET 4.5 can certainly support async actions, and it's not a difficult change to make.
Use ConfigureAwait(continueOnCapturedContext: false) as much as possible. This overrides the default behavior of resuming on the captured context.
I was in the same deadlock situation but in my case calling an async method from a sync method, what works for me was:
private static SiteMetadataCacheItem GetCachedItem()
{
TenantService TS = new TenantService(); // my service datacontext
var CachedItem = Task.Run(async ()=>
await TS.GetTenantDataAsync(TenantIdValue)
).Result; // dont deadlock anymore
}
is this a good approach, any idea?
Just to add to the accepted answer (not enough rep to comment), I had this issue arise when blocking using task.Result, event though every await below it had ConfigureAwait(false), as in this example:
public Foo GetFooSynchronous()
{
var foo = new Foo();
foo.Info = GetInfoAsync.Result; // often deadlocks in ASP.NET
return foo;
}
private async Task<string> GetInfoAsync()
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
The issue actually lay with the external library code. The async library method tried to continue in the calling sync context, no matter how I configured the await, leading to deadlock.
Thus, the answer was to roll my own version of the external library code ExternalLibraryStringAsync, so that it would have the desired continuation properties.
wrong answer for historical purposes
After much pain and anguish, I found the solution buried in this blog post (Ctrl-f for 'deadlock'). It revolves around using task.ContinueWith, instead of the bare task.Result.
Previously deadlocking example:
public Foo GetFooSynchronous()
{
var foo = new Foo();
foo.Info = GetInfoAsync.Result; // often deadlocks in ASP.NET
return foo;
}
private async Task<string> GetInfoAsync()
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
Avoid the deadlock like this:
public Foo GetFooSynchronous
{
var foo = new Foo();
GetInfoAsync() // ContinueWith doesn't run until the task is complete
.ContinueWith(task => foo.Info = task.Result);
return foo;
}
private async Task<string> GetInfoAsync
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
quick answer :
change this line
ResultClass slowTotal = asyncTask.Result;
to
ResultClass slowTotal = await asyncTask;
why? you should not use .result to get the result of tasks inside most applications except console applications if you do so your program will hang when it gets there
you can also try the below code if you want to use .Result
ResultClass slowTotal = Task.Run(async ()=>await asyncTask).Result;

Wrap .NET Remoting async method in TPL Task

We have a legacy .NET Remoting-based app. Our client client libary currently supports only synchronous operations. I would like to add asynchronous operations with TPL-based async Task<> methods.
As proof of concept, I have set up a basic remoting server/client solution based a modified version of these instructions.
I have also found this article that describes how to convert APM-based asynchronous operations to TPL-based async tasks (using Task.Factory.FromAsync)
What I'm unsure about is whether I'm compelled to specify the callback function in .BeginInvoke() and also to specify the .EndInvoke(). If both are required, what exactly is the difference between the callback function and .EndInvoke(). If only one is required, which one should I use to return values and also ensure that I have no memory leaks.
Here is my current code where I don't pass a callback to .BeginInvoke():
public class Client : MarshalByRefObject
{
private IServiceClass service;
public delegate double TimeConsumingCallDelegate();
public void Configure()
{
RemotingConfiguration.Configure("client.exe.config", false);
var wellKnownClientTypeEntry = RemotingConfiguration.GetRegisteredWellKnownClientTypes()
.Single(wct => wct.ObjectType.Equals(typeof(IServiceClass)));
this.service = Activator.GetObject(typeof(IServiceClass), wellKnownClientTypeEntry.ObjectUrl) as IServiceClass;
}
public async Task<double> RemoteTimeConsumingRemoteCall()
{
var timeConsumingCallDelegate = new TimeConsumingCallDelegate(service.TimeConsumingRemoteCall);
return await Task.Factory.FromAsync
(
timeConsumingCallDelegate.BeginInvoke(null, null),
timeConsumingCallDelegate.EndInvoke
);
}
public async Task RunAsync()
{
var result = await RemoteTimeConsumingRemoteCall();
Console.WriteLine($"Result of TPL remote call: {result} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
}
}
public class Program
{
public static async Task Main(string[] Args)
{
Client clientApp = new Client();
clientApp.Configure();
await clientApp.RunAsync();
Console.WriteLine("Press any key to continue...");
Console.ReadKey(false);
}
}
The difference between between the callback function and .EndInvoke() is that the callback will get executed on an arbitrary thread from the threadpool. If you must be sure that you obtain result from the read on the same thread as the one you called BeginInvoke, then you should not use the callback, but poll the IAsyncResult object and call .EndInvoke() when the operation is complete.
If you call .EndInvoke() right after .Beginnvoke(), you block the thread until the operation completes. This will work, but will scale poorly.
So, what you do seems all right!
You must specify the callback function for efficiency reasons. If the FromAsync only has an IAsyncResult to work with it cannot be notified when that async result completes. It will have to use an event to wait. This blocks a thread (or, it registers a threadpool wait maybe which is not so bad).
Efficient async IO requires callbacks at some level.
If you are going async I assume you are doing this because you have many concurrent calls or the calls are very long running. For that reason you should use the more efficient mechanism.
If you don't have many or long running calls then async is not going to help you with performance in any way but it might still make GUI programming easier.
So this is not correct:
public async Task<double> RemoteTimeConsumingRemoteCall()
{
var timeConsumingCallDelegate = new TimeConsumingCallDelegate(service.TimeConsumingRemoteCall);
return await Task.Factory.FromAsync
(
timeConsumingCallDelegate.BeginInvoke(null, null),
timeConsumingCallDelegate.EndInvoke
);
}
To make sure that your implementation indeed does not block any thread I'd do this: Insert a Thread.Sleep(100000) on the server side and issue 1000 concurrent calls on the client. You should find that the thread count does not rise.

C# ASP.NET Core Fire and forget

I have a sync controller method
public IActionResult Action(int num)
{
//operations-1
Task.Run(() => DoWork(num));
//operations-2
int a = num + 123;
return Ok(a);
}
and DoWork method
private bool DoWork(int num)
{
//operations
return true;
}
What I'm trying to do is to run DoWork method in background when calling that endpoint from Postman, but I want to get result in Postman and then debug DoWork method (from breakpoint in DoWork method) - is it possible?
For that moment, controller action and DoWork() are executing simultaneously but when I reach
return Ok(a);
applications waits for DoWork instead of returning value. I have tried also
Task.Factory.StartNew(() => DoWork());
ThreadPool.QueueUserWorkItem(o => DoWork());
but result is the same.
I want DoWork method to return value but that value is not neccessary by controller action method - it will be used in different place, not connected with that.
Use a background queue sometimes is overkill.
There are a number of sites showing a way to do when you need to access the database context. The problem of Task.Run in a controller, is that the spawned task cannot access the same context as the controller uses as it may (probably will) get disposed before that Task accesses it.
You can get round this by ensuring that the sub task only references Dependencies it knows will stay alive,
either by using a
singleton service or better for database context, using the IServiceScopeFactory .
The crux of this is to create a seperate dependency that can handle your database context or Repository. This can be injected into your controller as normal.
public void Execute(Func<IRepository, Task> databaseWork)
{
// Fire off the task, but don't await the result
Task.Run(async () =>
{
// Exceptions must be caught
try
{
using var scope = _serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<IRepository>();
await databaseWork(repository);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
}
Then call this from your controller such as
// Delegate the slow task another task on the threadpool
_fireForgetRepositoryHandler.Execute(async repository =>
{
// Will receive its own scoped repository on the executing task
await repository.DoLOngRunningTaskAsync();;
});
Note: This builds upon Adem Catamak's answer.
Hangfire can be used, but no actual database is required because it can work with memory storage:
services.AddHangfire(opt => opt.UseMemoryStorage());
JobStorage.Current = new MemoryStorage();
While it has some overhead, Hangfire allows managing these jobs as opposed to having stuff running async and requiring custom code for simple things like run time, unhandled exceptions, custom code for DI support.
Credit: Codidact
Tasks are high level threads that make sure you are not blocking any context.
You either want to use something like RabbitMQ or IHostedService from ASP.NET Core 2.0 to do fire and forget task that kick in after a request has completed.
If you use Db in project, you can use Hangfire It is easy to use background process manager. https://www.hangfire.io/
you can use it very easyly like BackgroundJob.Enqueue(() => DoWork(num));

WPF/WCF Async Service Call and SynchronizationContext

I have a feeling there's got to be a better solution than what I've come up with; here's the problem:
A WPF form will call a WCF method, which returns a bool. The call itself should not be on the UI thread, and the result of the call will need to be displayed on the form, so the return should be marshaled back on to the UI thread.
In this example I created a "ServiceGateway" class, to which the form will pass a method to be executed upon completion of a Login operation. The gateway should invoke this Login-complete delegate using the UI SynchronizationContext, which is passed upon instantiation of the gateway from the form. The Login method invokes a call to _proxy.Login using an anon. async delegate, and then provides a callback which invokes the delegate ('callback' param) provided to the gateway (from the form) using the UI SynchronizationContext:
[CallbackBehavior(UseSynchronizationContext = false)]
public class ChatServiceGateway : MessagingServiceCallback
{
private MessagingServiceClient _proxy;
private SynchronizationContext _uiSyncContext;
public ChatServiceGateway(SynchronizationContext UISyncContext)
{
_proxy = new MessagingServiceClient(new InstanceContext(this));
_proxy.Open();
_uiSyncContext = UISyncContext;
}
public void Login(String UserName, Action<bool> callback)
{
new Func<bool>(() => _proxy.Login(UserName)).BeginInvoke(delegate(IAsyncResult result)
{
bool LoginResult = ((Func<bool>)((AsyncResult)result).AsyncDelegate).EndInvoke(result);
_uiSyncContext.Send(new SendOrPostCallback(obj => callback(LoginResult)), null);
}, null);
}
The Login method is called from the form in response to a button click event.
This works fine, but I have a suspicion I'm going about the Login method the wrong way; especially because I'll have to do the same for any other method call to the WCF service, and its ugly.
I would like to keep the async behavior and ui synchronization encapsulated in the gateway. Would it be better to have the asynchronous behavior implemented on the WCF side? Basically I'm interested if I can implement the above code more generically for other methods, or if there's a better way all together.
Provided that you're targeting at least VS 2012 and .NET 4.5, async/await is the way to go. Note the lack of SynchronizationContext reference - it's captured under the covers before the await, and posted back to once the async operation has completed.
public async Task Login(string userName, Action<bool> callback)
{
// The delegate passed to `Task.Run` is executed on a ThreadPool thread.
bool loginResult = await Task.Run(() => _proxy.Login(userName));
// OR
// await _proxy.LoginAsync(UserName);
// if you have an async WCF contract.
// The callback is executed on the thread which called Login.
callback(loginResult);
}
Task.Run is primarily used to push CPU-bound work to the thread pool, so the example above does abuse it somewhat, but if you don't want to rewrite the contract implemented by MessagingServiceClient to use asynchronous Task-based methods, it is still a pretty good way to go.
Or the .NET 4.0 way (no async/await support):
public Task Login(string userName, Action<bool> callback)
{
// The delegate passed to `Task.Factory.StartNew`
// is executed on a ThreadPool thread.
var task = Task.Factory.StartNew(() => _proxy.Login(userName));
// The callback is executed on the thread which called Login.
var continuation = task.ContinueWith(
t => callback(t.Result),
TaskScheduler.FromCurrentSynchronizationContext()
);
return continuation;
}
This is a bit of a departure from the way you're currently doing things in that it is the responsibility of the caller to ensure that Login is getting called on the UI thread if they want the callback to be executed on it. That is, however, standard practice when it comes to async, and while you could keep a reference to the UI SynchronizationContext or TaskScheduler as part of your ChatServiceGateway to force the callback/continuation to execute on the right thread, it will blow out your implementation and personally (and this is just my opinion) I would say that that's a bit of a code smell.

await of async WCF call not returning to UI thread and/or blocking UI thread

I have upgraded a .NET 4.0 WinForms program to .NET 4.5.1 in the hope of using the new await on async WCF calls in order to prevent freezing the UI while waiting for data (the original was quickly written so I was hoping the old synchronous WCF calls could be made async with minimal change to existing code using the new await feature).
From what I understand, await was supposed to return to the UI thread with no extra coding, but for some reason it does not for me, so the following would give the cross thread exception:
private async void button_Click(object sender, EventArgs e)
{
using (MyService.MyWCFClient myClient = MyServiceConnectFactory.GetForUser())
{
var list=await myClient.GetListAsync();
dataGrid.DataSource=list; // fails if not on UI thread
}
}
Following the article await anything I made a custom awaiter so I could issue an await this to get back on the UI thread, which solved the exception, but then I found my UI was still frozen despite using the asynchronously Tasks generated by Visual Studio 2013 for my WCF service.
Now the program is actually a Hydra VisualPlugin running inside an old Delphi application, so if anything could mess things up, is probably happens... But does anybody have any experience with what exactly could make awaiting an async WCF not returning to the UI thread or hang the UI thread?
Maybe upgrading from 4.0 to 4.5.1 makes the program miss some reference to do the magic?
Now while I would like to understand why await does not work as advertised, I ended up with making my own workaround: A custom awaiter that forces a task to run in a background thread, and which forces the continuation to arrive back on the UI thread.
Similarly to .ConfigureAwait(false) I wrote an .RunWithReturnToUIThread(this) extension for Taks as follows:
public static RunWithReturnToUIThreadAwaiter<T> RunWithReturnToUIThread<T>(this Task<T> task, Control control)
{
return new RunWithReturnToUIThreadAwaiter<T>(task, control);
}
public class RunWithReturnToUIThreadAwaiter<T> : INotifyCompletion
{
private readonly Control m_control;
private readonly Task<T> m_task;
private T m_result;
private bool m_hasResult=false;
private ExceptionDispatchInfo m_ex=null; // Exception
public RunWithReturnToUIThreadAwaiter(Task<T> task, Control control)
{
if (task == null) throw new ArgumentNullException("task");
if (control == null) throw new ArgumentNullException("control");
m_task = task;
m_control = control;
}
public RunWithReturnToUIThreadAwaiter<T> GetAwaiter() { return this; }
public bool IsCompleted
{
get
{
return !m_control.InvokeRequired && m_task.IsCompleted; // never skip the OnCompleted event if invoke is required to get back on UI thread
}
}
public void OnCompleted(Action continuation)
{
// note to self: OnCompleted is not an event - it is called to specify WHAT should be continued with ONCE the result is ready, so this would be the place to launch stuff async that ends with doing "continuation":
Task.Run(async () =>
{
try
{
m_result = await m_task.ConfigureAwait(false); // await doing the actual work
m_hasResult = true;
}
catch (Exception ex)
{
m_ex = ExceptionDispatchInfo.Capture(ex); // remember exception
}
finally
{
m_control.BeginInvoke(continuation); // give control back to continue on UI thread even if ended in exception
}
});
}
public T GetResult()
{
if (m_ex == null)
{
if (m_hasResult)
return m_result;
else
return m_task.Result; // if IsCompleted returned true then OnCompleted was never run, so get the result here
}
else
{ // if ended in exception, rethrow it
m_ex.Throw();
throw m_ex.SourceException; // just to avoid compiler warning - the above does the work
}
}
}
Now in the above I am not sure if my exception handling is needed like this, or if the Task.Run really need to use async and await in its code, or if the multiple layers of Tasks could give problems (I am basically bypassing the encapsulated Task's own method of returning - since it did not return correctly in my program for WCF services).
Any comments/ideas regarding efficiency of the above workaround, or what caused the problems to begin with?
Now the program is actually a Hydra VisualPlugin running inside an old Delphi application
That's probably the problem. As I explain in my async intro blog post, when you await an Task and that task is incomplete, the await operator by default will capture a "current context" and later resume the async method in that context. The "current context" is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current.
So, the normal "return to UI thread" behavior is a result of await capturing the UI synchronization context - in the case of WinForms, a WinFormsSynchronizationContext.
In a normal WinForms application, SynchronizationContext.Current is set to a WinFormsSynchronizationContext the first time you create a Control. Unfortunately, this does not always happen in plugin architectures (I've seen similar behavior on Microsoft Office plugins). I suspect that when your code awaits, SynchronizationContext.Current is null and TaskScheduler.Current is TaskScheduler.Default (i.e., the thread pool task scheduler).
So, the first thing I'd try is creating a Control:
void EnsureProperSynchronizationContext()
{
if (SynchronizationContext.Current == null)
var _ = new Control();
}
Hopefully, you would only have to do that once, when your plugin is first invoked. But you may have to do it at the beginning of all your methods that can be invoked by the host.
If that doesn't work, you can create your own SynchronizationContext, but it's best to use the WinForms one if you can. A custom awaiter is also possible (and if you go that route, it's easier to wrap TaskAwaiter<T> rather than Task<T>), but the disadvantage of a custom awaiter is that is has to go on every await.

Categories

Resources