Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to cast async Func or Action to Delegate and invoke it

I'm trying to make this code working:

protected async Task RunIsolated<TServ1, TServ2>(Action<TServ1, TServ2> action)
{
    await RunInScope(action, typeof(TServ1), typeof(TServ2));
}

protected async Task<TResult> RunIsolatedForResult<TService, TResult>(Func<TService, TResult> func)
{
    return (TResult) await RunInScope(func, typeof(TService));
}

private Task<object> RunInScope(Delegate d, params object[] args)
{
     using (var scope = _serviceProvider.CreateScope())
     {
         object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
         return Task.FromResult(d.DynamicInvoke(parameters));
     }
}

this work for sync version of code, like this:

await RunIsolated<Service>(serv => serv.SaveAsync(item).Wait());

but don't work (db operation throw an exception) for async version of the same code

await RunIsolated<Service>(async serv => await serv.SaveAsync(item));

Is it somehow possible to convert async Action or Func to Delegate and invoke it without loosing async state?

like image 808
Ph0en1x Avatar asked Dec 28 '17 11:12

Ph0en1x


People also ask

Is delegate Async in C#?

Delegates enable you to call a synchronous method in an asynchronous manner. When you call a delegate synchronously, the Invoke method calls the target method directly on the current thread.

How do you call a Func delegate method in C#?

Func<int, int, int> Add = Sum; This Func delegate takes two parameters and returns a single value. In the following example, we use a delegate with three input parameters. int Sum(int x, int y, int z) { return x + y + z; } Func<int, int, int, int> add = Sum; int res = add(150, 20, 30); Console.

Can an action be async?

For example, action=getstatus is a synchronous action. Actions that can take a significant time to complete are usually asynchronous. These actions are asynchronous because otherwise requests might time out before Media Server is able to process them.

What is func task in C#?

The Func accepts a string parameter (the id of the record to fetch) and returns a Task of type RecordType (a generic type parameter). In the following example, Employee will be specified for the generic type. This means when the Func is awaited, it'll return an Employee object.


1 Answers

You need to create new overload which accepts Func<Task>. Right now, anonymous async function you pass here

await RunIsolated<Service>(async serv => await serv.SaveAsync(item));

Is treated as Action, which means that is async void method basically, with all corresponding drawbacks. Instead you have to do something like this (simplified to use basic Action and Func, adjust as needed to your situation):

protected Task RunIsolated(Action action) {
    return RunInScope(action);
}

protected Task RunIsolated(Func<Task> action) {
    return RunInScope(action);
}

protected Task<TResult> RunIsolatedForResult<TResult>(Func<Task<TResult>> action) {
    return RunInScopeWithResult<TResult>(action);
}

protected Task<TResult> RunIsolatedForResult<TResult>(Func<TResult> action) {
    return RunInScopeWithResult<TResult>(action);
}

private async Task RunInScope(Delegate d, params object[] args) {
    // do some stuff
    using (var scope = _serviceProvider.CreateScope()) {
        object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
        var result = d.DynamicInvoke(parameters);
        var resultTask = result as Task;
        if (resultTask != null) {
            await resultTask;
        }
    }
}

private async Task<TResult> RunInScopeWithResult<TResult>(Delegate d, params object[] args) {
    // do some stuff
    using (var scope = _serviceProvider.CreateScope()) {
        object[] parameters = args.Cast<Type>().Select(t => scope.ServiceProvider.GetService(t)).ToArray();
        var result = d.DynamicInvoke(parameters);
        var resultTask = result as Task<TResult>;
        if (resultTask != null) {
            return await resultTask;
        }
        return (TResult) result;
    }
}
like image 145
Evk Avatar answered Oct 10 '22 16:10

Evk