Intercept the call to an async method using DynamicProxy - c#

Below is the code from the Intercept method on a custom type that implements IInterceptor of the Castle Dynamic Proxy library. This snippet is from an AOP based logging proof-of-concept console app that is posted here.
public void Intercept(IInvocation invocation)
{
if (Log.IsDebugEnabled) Log.Debug(CreateInvocationLogString("Called", invocation));
try
{
invocation.Proceed();
if (Log.IsDebugEnabled)
if (invocation.Method.ReturnType != typeof(void))
Log.Debug("Returning with: " + invocation.ReturnValue);
}
catch (Exception ex)
{
if (Log.IsErrorEnabled) Log.Error(CreateInvocationLogString("ERROR", invocation), ex);
throw;
}
}
This is working as expected on regular method calls, but not when tried with async methods (using the async/await keywords from C# 5.0). And I believe, I understand the reasons behind this as well.
For the async/await to work, the compiler adds the functional body of the method into a state machine behind the scenes and the control will return to the caller, as soon as the first awaitable expression that cannot be completed synchronously, is encountered.
Also, we can interrogate the return type and figure out whether we are dealing with an async method like this:
if (invocation.Method.ReturnType == typeof(Task) ||
(invocation.Method.ReturnType.IsGenericType &&
invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)))
Log.Info("Asynchronous method found...");
This works for only those async methods that returns either Task or Task<> and not void but I am fine with that.
What changes have to made within the Intercept method so that the awaiter would return to there rather than the original caller?

Presumably the "problem" is that it's just logging that it's returning a task - and you want the value within that task?
Assuming that's the case, you still have to return the task to the caller, immediately - without waiting for it to complete. If you break that, you're fundamentally messing things up.
However, before you return the task to the caller, you should add a continuation (via Task.ContinueWith) which will log the result (or failure) when the task completes. That will still give the result information, but of course you'll be logging it potentially after some other logging. You may also want to log immediately before returning, leading to a log something like this:
Called FooAsync
Returned from FooAsync with a task
Task from FooAsync completed, with return value 5
The business of getting the result out of the task (if it completed successfully) would have to be done with reflection, which is a bit of a pain - or you could use dynamic typing. (Either way it will be a bit of a performance hit.)

Thanks to Jon's answer, this is what I ended up with:
public void Intercept(IInvocation invocation)
{
if (Log.IsDebugEnabled) Log.Debug(CreateInvocationLogString("Called", invocation));
try
{
invocation.Proceed();
if (Log.IsDebugEnabled)
{
var returnType = invocation.Method.ReturnType;
if (returnType != typeof(void))
{
var returnValue = invocation.ReturnValue;
if (returnType == typeof(Task))
{
Log.Debug("Returning with a task.");
}
else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
Log.Debug("Returning with a generic task.");
var task = (Task)returnValue;
task.ContinueWith((antecedent) =>
{
var taskDescriptor = CreateInvocationLogString("Task from", invocation);
var result =
antecedent.GetType()
.GetProperty("Result")
.GetValue(antecedent, null);
Log.Debug(taskDescriptor + " returning with: " + result);
});
}
else
{
Log.Debug("Returning with: " + returnValue);
}
}
}
}
catch (Exception ex)
{
if (Log.IsErrorEnabled) Log.Error(CreateInvocationLogString("ERROR", invocation), ex);
throw;
}
}

Trying to clarify with a generic and clean solution for:
Intercepting async methods adding custom code as a continuation task.
I think the best solution is to use the dynamic keyword to bypass the compiler type checking and resolve the difference between Task and Task<T> at run time:
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var method = invocation.MethodInvocationTarget;
var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
{
invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue);
}
}
private static async Task InterceptAsync(Task task)
{
await task.ConfigureAwait(false);
// do the logging here, as continuation work for Task...
}
private static async Task<T> InterceptAsync<T>(Task<T> task)
{
T result = await task.ConfigureAwait(false);
// do the logging here, as continuation work for Task<T>...
return result;
}

Below is my async interceptor adapter implementation that correctly handles async methods.
abstract class AsyncInterceptor : IInterceptor
{
class TaskCompletionSourceMethodMarkerAttribute : Attribute
{
}
private static readonly MethodInfo _taskCompletionSourceMethod = typeof(AsyncInterceptor)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.Single(x => x.GetCustomAttributes(typeof(TaskCompletionSourceMethodMarkerAttribute)).Any());
protected virtual Task<Object> InterceptAsync(Object target, MethodBase method, object[] arguments, Func<Task<Object>> proceed)
{
return proceed();
}
protected virtual void Intercept(Object target, MethodBase method, object[] arguments, Action proceed)
{
proceed();
}
[TaskCompletionSourceMethodMarker]
Task<TResult> TaskCompletionSource<TResult>(IInvocation invocation)
{
var tcs = new TaskCompletionSource<TResult>();
var task = InterceptAsync(invocation.InvocationTarget, invocation.Method, invocation.Arguments, () =>
{
var task2 = (Task)invocation.Method.Invoke(invocation.InvocationTarget, invocation.Arguments);
var tcs2 = new TaskCompletionSource<Object>();
task2.ContinueWith(x =>
{
if (x.IsFaulted)
{
tcs2.SetException(x.Exception);
return;
}
dynamic dynamicTask = task2;
Object result = dynamicTask.Result;
tcs2.SetResult(result);
});
return tcs2.Task;
});
task.ContinueWith(x =>
{
if (x.IsFaulted)
{
tcs.SetException(x.Exception);
return;
}
tcs.SetResult((TResult)x.Result);
});
return tcs.Task;
}
void IInterceptor.Intercept(IInvocation invocation)
{
if (!typeof(Task).IsAssignableFrom(invocation.Method.ReturnType))
{
Intercept(invocation.InvocationTarget, invocation.Method, invocation.Arguments, invocation.Proceed);
return;
}
var returnType = invocation.Method.ReturnType.IsGenericType ? invocation.Method.ReturnType.GetGenericArguments()[0] : typeof(object);
var method = _taskCompletionSourceMethod.MakeGenericMethod(returnType);
invocation.ReturnValue = method.Invoke(this, new object[] { invocation });
}
}
and sample usage:
class TestInterceptor : AsyncInterceptor
{
protected override async Task<Object> InterceptAsync(object target, MethodBase method, object[] arguments, Func<Task<object>> proceed)
{
await Task.Delay(5000);
var result = await proceed();
return DateTime.Now.Ticks % 2 == 0 ? 10000 :result;
}
}

My 2 cents:
It has been correctly established that for async methods the purpose of the interceptor would be to "enhance" the task returned by the invocation, via a continuation.
Now, it is precisely this task continuation the one that has to be returned to make the job of the interceptor complete.
So, based on the above discussions and examples, this would work perfectly well for regular methods as well as "raw" async Task methods.
public virtual void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
var task = invocation.ReturnValue as Task;
if (task != null)
{
invocation.ReturnValue = task.ContinueWith(t => {
if (t.IsFaulted)
OnException(invocation, t.Exception);
});
}
}
catch (Exception ex)
{
OnException(invocation, ex);
}
}
public virtual void OnException(IInvocation invocation, Exception exception)
{
...
}
But when dealing with async Task<T> methods, the above would incorrectly change the type of the task returned by the interception, from Task<T> to regular Task
Notice that we are calling Task.ContinueWith() and not Task<TResult>.ContinueWith(), which is the method we want to call.
This would be the resulting exception when ultimately awaiting the such an interception:
System.InvalidCastException: Unable to cast object of type 'System.Threading.Tasks.ContinuationTaskFromTask' to type 'System.Threading.Tasks.Task`1

Having a need to intercept methods returning Task<TResult>, I've created an extension to Castle.Core that simplifies the process.
Castle.Core.AsyncInterceptor
The package is available to download on NuGet.
The solution is largely based on this answer from #silas-reinagel, but simplifies it by providing a new interface to implement IAsyncInterceptor. There are also further abstractions to that make interception similar to implementing Interceptor.
See the readme of the project for further details.

void IInterceptor.Intercept(IInvocation invocation) {
try {
invocation.Proceed();
var task = invocation.ReturnValue as Task;
if (task != null && task.IsFaulted) throw task.Exception;
}
catch {
throw;
}
}

Instead of:
tcs2.SetException(x.Exception);
You should use:
x.Exception.Handle(ex => { tcs2.SetException(ex); return true; });
to bubble up the real exception...

Related

Possible to intercept an async method and overwrite ReturnValue?

I have a method:
public virtual async Task<IActionResult> GetEmployees([HttpTrigger(AuthorizationLevel.Admin, "get", Route = null)] HttpRequest req) {
return OkObjectResult(null);
}
I know I can intercept this synchronously using autofac:
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.Write("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
But how can I do this asynchronously while potentially overwriting the result the method I'm intercepting returns? It currently returns an OkObjectResult, I may want to return a 404 instead, for example.
Psuedo Code
public async Task Intercept(IInvocation invocation)
{
var myAsyncResult = await _myAsyncClass.MyAsyncMethod();
if (myAsyncResult == expected)
{
invocation.Proceed();
}
else
{
invocation.ReturnValue = // some overwrite of the value - and don't proceed with the invocation.
}
}
Note
I know there are clever approaches to async in autofac, but this doesn't allow me to prevent execution of the original method and overwrite the value, instead I need to 'proceed' the invocation and use it's return: https://stackoverflow.com/a/39784559/12683473
The main thing to keep in mind with Castle.Core is that you cannot call IInvocation.Proceed after an await because the IInvocation instance is reused after the interceptor (synchronously) returns.
However, modern versions of Castle.Core do support IInvocation.CaptureProceedInfo, which can be used as such:
public class CallLogger : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.ReturnValue = InterceptAsync<MyResult>(invocation.CaptureProceedInfo());
}
private async Task<TResult> InterceptAsync<TResult>(IInvocationProceedInfo proceed)
{
var myAsyncResult = await _myAsyncClass.MyAsyncMethod();
if (myAsyncResult == expected)
{
proceed.Invoke();
}
else
{
return ...;
}
}
}
This simple code assumes your code can specify a type MyResult that is the known result of the intercepted method. The more general case requires reflection and TaskCompletionSource<T>.

Async Await and ContinueWith not working as expected

I have the following code that runs on .NET Standard 2.0:
public static Task<JobResult> TryRunAsync(this IJob job,
CancellationToken cancellationToken = default(CancellationToken))
{
return job.RunAsync(cancellationToken)
.ContinueWith(t => {
if (t.IsFaulted)
return JobResult.FromException(t.Exception.InnerException);
if (t.IsCanceled)
return JobResult.Cancelled;
return t.Result;
});
}
And we noticed it wasn't running as expected. We thought that when you awaited the call to TryRun it would always call the continuation which could handle the exception/cancellation and return a job result. We were hoping to reduce the amount of async state machines created... However, this is not the case it just blows up. Here is a smaller sample (create a new .net core 2.0 console app and paste the following:
using System;
using System.Threading.Tasks;
namespace ConsoleApp4
{
public class Program
{
public static async Task Main()
{
// works
await DoStuff();
Console.ReadKey();
// blows up
await TryRun();
Console.ReadKey();
}
public static Task DoStuff()
{
return Method()
.ContinueWith(t => Throws())
.ContinueWith(t => {
if (t.IsFaulted)
Console.WriteLine("Faulted");
else if (t.IsCompletedSuccessfully)
Console.WriteLine("Success");
});
}
public static Task Method()
{
Console.WriteLine("Method");
return Task.CompletedTask;
}
public static Task TryRun()
{
return Throws()
.ContinueWith(t => {
if (t.IsFaulted)
Console.WriteLine("Faulted");
else if (t.IsCompletedSuccessfully)
Console.WriteLine("Success");
});
}
public static Task Throws()
{
Console.WriteLine("Throws");
throw new ApplicationException("Grr");
}
}
}
You may need <LangVersion>Latest</LangVersion> In your csproj.
UPDATE
We ended up going with the following code:
public static Task<JobResult> TryRunAsync(this IJob job,
CancellationToken cancellationToken = default(CancellationToken))
{
var tcs = new TaskCompletionSource<JobResult>(null);
try {
var task = job.RunAsync(cancellationToken);
task.ContinueWith((task2, state2) => {
var tcs2 = (TaskCompletionSource<object>)state2;
if (task2.IsCanceled) {
tcs2.SetResult(JobResult.Cancelled);
} else if (task2.IsFaulted) {
tcs2.SetResult(JobResult.FromException(task2.Exception));
} else {
tcs2.SetResult(JobResult.Success);
}
}, tcs, cancellationToken);
} catch (Exception ex) {
tcs.SetResult(JobResult.FromException(ex));
}
return tcs.Task;
}
The method throws is actually throwing an exception when called, not returning a faulted Task. There is no Task for you to add a continuation to; it's simply going up the call stack before even reaching the ContinueWith call.
Indeed there is no task created here. For instance if you did a loop you would stay on the same thread and same stack. You could see the correct behaviour by doing a Task.FromException in the Throws method rather than throwing. Also in core 2.1 at least you might find that an async method will be just as fast or even faster than the continuation version and less allocaty. Worth checking your trace numbers before trying to optimise away the state machine. Also if you are throwing exceptions your state machine is definitely the least of your perf concerns.

Intercept async method that returns generic Task<> via DynamicProxy

My questions is related to this post Intercept the call to an async method using DynamicProxy
I want to implement interceptor that works with async methods that returns Task or Task<T> result.
I use next code for return ContinueWith result (in order that caller method wait while interceptor finishes work)
var task = invocation.ReturnValue as Task;
invocation.ReturnValue = task.ContinueWith(c =>
{ code that should execute after method finish });
Above code works fine for Task result, but in case of Task<T> result ContinueWith will change return type from Task<T> to Task.
I need to call overloaded method ContinueWith that returns Task<T>, but for this I need to cast invocation.ReturnValue to Task<T>
I didn't find way to cast it dynamically in any way.
Does anyone know how to make it?
I also tried to call this method via reflection, but parameter is labmda function that can't be passed directly.
After extensive research, I was able to create a solution that works for intercepting Synchronous Methods as well as Async Task and Async Task< TResult >.
Here is my code for an Exception Handling interceptor that works on all those method types, using Castle Dynamic Proxy. This pattern is adaptable for doing any sort of intercept you wish. The syntax will be a little cleaner for standard BeforeInvoke/AfterInvoke actions, but the concept should be the same.
(Other note: the IExceptionHandler interface in the example is a custom type, and not a common object.)
private class AsyncExceptionHandlingInterceptor : IInterceptor
{
private static readonly MethodInfo handleAsyncMethodInfo = typeof(AsyncExceptionHandlingInterceptor).GetMethod("HandleAsyncWithResult", BindingFlags.Instance | BindingFlags.NonPublic);
private readonly IExceptionHandler _handler;
public AsyncExceptionHandlingInterceptor(IExceptionHandler handler)
{
_handler = handler;
}
public void Intercept(IInvocation invocation)
{
var delegateType = GetDelegateType(invocation);
if (delegateType == MethodType.Synchronous)
{
_handler.HandleExceptions(() => invocation.Proceed());
}
if (delegateType == MethodType.AsyncAction)
{
invocation.Proceed();
invocation.ReturnValue = HandleAsync((Task)invocation.ReturnValue);
}
if (delegateType == MethodType.AsyncFunction)
{
invocation.Proceed();
ExecuteHandleAsyncWithResultUsingReflection(invocation);
}
}
private void ExecuteHandleAsyncWithResultUsingReflection(IInvocation invocation)
{
var resultType = invocation.Method.ReturnType.GetGenericArguments()[0];
var mi = handleAsyncMethodInfo.MakeGenericMethod(resultType);
invocation.ReturnValue = mi.Invoke(this, new[] { invocation.ReturnValue });
}
private async Task HandleAsync(Task task)
{
await _handler.HandleExceptions(async () => await task);
}
private async Task<T> HandleAsyncWithResult<T>(Task<T> task)
{
return await _handler.HandleExceptions(async () => await task);
}
private MethodType GetDelegateType(IInvocation invocation)
{
var returnType = invocation.Method.ReturnType;
if (returnType == typeof(Task))
return MethodType.AsyncAction;
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
return MethodType.AsyncFunction;
return MethodType.Synchronous;
}
private enum MethodType
{
Synchronous,
AsyncAction,
AsyncFunction
}
}
A better solution would be to use the dynamic keyword to bypass the compiler type checking and resolve the operation at run time:
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var method = invocation.MethodInvocationTarget;
var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
{
invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue);
}
}
private static async Task InterceptAsync(Task task)
{
await task.ConfigureAwait(false);
// do the continuation work for Task...
}
private static async Task<T> InterceptAsync<T>(Task<T> task)
{
T result = await task.ConfigureAwait(false);
// do the continuation work for Task<T>...
return result;
}
Having a need to intercept methods returning Task<TResult>, I've created an extension to Castle.Core that simplifies the process.
Castle.Core.AsyncInterceptor
The package is available to download on NuGet.
The solution is largely based on the answer from #silas-reinagel, but simplifies it by providing a new interface to implement IAsyncInterceptor. There are also further abstractions to that make interception similar to implementing Interceptor.
See the readme of the project for further details.
The solutions from #Silas Reinagel and #thepirat000 didn't work for me, and I was not successful using Castle.Core.AsyncInterceptor solution from #James Skimming.
In my case, I'm intercepting an async method returning Task, and it should execute "after invocation.Proceed() code" only if there was no exception during the invocation.Proceed(). At the end I used #James Skimming's code sample, so this solution works only for intercepting async methods returning Task and not Task<TResult>:
public void Intercept(IInvocation invocation)
{
_stepPriorInvocation();
invocation.Proceed();
Func<Task> continuation = async () =>
{
await (Task)invocation.ReturnValue;
_stepAfterSuccessfulInvocation();
};
invocation.ReturnValue = continuation();
void _stepPriorInvocation()
{
}
void _stepAfterSuccessfulInvocation()
{
}
}
I've done this way:
invocation.Proceed();
object response;
Type type = invocation.ReturnValue?.GetType();
if (type != null && typeof(Task).IsAssignableFrom(type))
{
var resultProperty = type.GetProperty("Result");
response = resultProperty.GetValue(invocation.ReturnValue);
}

Different behavior of exception with async method

Supposed you have 2 async method define as bellow:
public async Task<TResult> SomeMethod1()
{
throw new Exception();
}
public async Task<TResult> SomeMethod2()
{
await Task.Delay(50);
throw new Exception();
}
Now if you await on those 2 methods the behavior will be pretty much the same. But if you are getting the task the behavior is different.
If I want to cache the result of such a computation but only when the task run to completion.
I have to take care of the 2 situation:
First Situation:
public Task<TResult> CachingThis1(Func<Task<TResult>> doSomthing1)
{
try
{
var futur = doSomthing1()
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
}
catch ()
{
// ... Remove from the pending cache
throw;
}
}
Second Situation
public Task<TResult> CachingThis2(Func<Task<TResult>> doSomthing)
{
var futur = SomeMethod2();
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
futur.ContinueWith(
t =>
{
// ... Remove from the pending cache
},
TaskContinuationOptions.OnlyOnFaulted);
}
Now I pass to my caching system the method that will execute the computation to cache.
cachingSystem.CachingThis1(SomeMethod1);
cachingSystem.CachingThis2(SomeMethod2);
Clearly I need to duplicate code in the "ConinueWith on faulted" and the catch block.
Do you know if there is a way to make the exception behave the same whether it is before or after an await?
There's no difference in the exception handling required for both SomeMethod1 and SomeMethod2. They run exactly the same way and the exception would be stored in the returned task.
This can easily be seen in this example;
static void Main(string[] args)
{
try
{
var task = SomeMethod1();
}
catch
{
// Unreachable code
}
}
public static async Task SomeMethod1()
{
throw new Exception();
}
No exception would be handled in this case since the returned task is not awaited.
There is however a distinction between a simple Task-returning method and an async method:
public static Task TaskReturning()
{
throw new Exception();
return Task.Delay(1000);
}
public static async Task Async()
{
throw new Exception();
await Task.Delay(1000);
}
You can avoid code duplication by simply having an async wrapper method that both invokes the method and awaits the returned task inside a single try-catch block:
public static async Task HandleAsync()
{
try
{
await TaskReturning();
// Add to cache.
}
catch
{
// handle exception from both the synchronous and asynchronous parts.
}
}
In addition to what I3arnon said in his answer, in case you ContinueWith on async method without the TaskContinuationOptions you specify, exception captured by the Task parameter you receive in the continuation handler can be handled in the following way:
SomeMethod1().ContinueWith(ProcessResult);
SomeMethod2().ContinueWith(ProcessResult);
With ProcessResult handler which looks like:
private void ProcessResult<TResult>(Task<TResult> task)
{
if (task.IsFaulted)
{
//remove from cahe
}
else if (task.IsCompleted)
{
//add to cache
}
}

Using async/await with Dispatcher.BeginInvoke()

I have a method with some code that does an await operation:
public async Task DoSomething()
{
var x = await ...;
}
I need that code to run on the Dispatcher thread. Now, Dispatcher.BeginInvoke() is awaitable, but I can't mark the lambda as async in order to run the await from inside it, like this:
public async Task DoSomething()
{
App.Current.Dispatcher.BeginInvoke(async () =>
{
var x = await ...;
}
);
}
On the inner async, I get the error:
Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type.
How can I work with async from within Dispatcher.BeginInvoke()?
The other answer may have introduced an obscure bug. This code:
public async Task DoSomething()
{
App.Current.Dispatcher.Invoke(async () =>
{
var x = await ...;
});
}
uses the Dispatcher.Invoke(Action callback) override form of Dispatcher.Invoke, which accepts an async void lambda in this particular case. This may lead to quite unexpected behavior, as it usually happens with async void methods.
You are probably looking for something like this:
public async Task<int> DoSomethingWithUIAsync()
{
await Task.Delay(100);
this.Title = "Hello!";
return 42;
}
public async Task DoSomething()
{
var x = await Application.Current.Dispatcher.Invoke<Task<int>>(
DoSomethingWithUIAsync);
Debug.Print(x.ToString()); // prints 42
}
In this case, Dispatch.Invoke<Task<int>> accepts a Func<Task<int>> argument and returns the corresponding Task<int> which is awaitable. If you don't need to return anything from DoSomethingWithUIAsync, simply use Task instead of Task<int>.
Alternatively, use one of Dispatcher.InvokeAsync methods.
I think you can use below code and then depends of place use it with async and await or without to fire and forget:
public static Task FromUiThreadAsync(Action action)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Dispatcher disp = GetUiDispatcher();
disp.Invoke(DispatcherPriority.Background, new Action(() =>
{
try
{
action();
tcs.SetResult(true);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}));
return tcs.Task;
}
Use Dispatcher.Invoke()
public async Task DoSomething()
{
App.Current.Dispatcher.Invoke(async () =>
{
var x = await ...;
});
}
(Edit: This answer is wrong, but I'll fix it soon)
Declare this
public async Task DoSomethingInUIThreadAsync(Func<Task> p)
{
await Application.Current.Dispatcher.Invoke(p);
}
Use like this
string someVar = "XXX";
DoSomethingInUIThreadAsync(()=>{
await Task.Run(()=> {
Thread.Sleep(10000);
Button1.Text = someVar;
});
});
DoSomethingInUIThreadAsync receives a delegate that returns a Task, Application.Current.Dispatcher.Invoke accepts a Func callback that can be awaited.

Categories

Resources