实现AsyncCodeActivities(使用C#async/await)

fra*_*fra 6 c# workflow-foundation-4 async-await

很长一段时间我使用以下模板编写自定义AsyncCodeActivity类:

public sealed class MyActivity : AsyncCodeActivity<T>
{
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        var task = new Task<T>(this.Execute, state, CancellationToken.None, TaskCreationOptions.AttachedToParent);
        task.ContinueWith(s => callback(s));
        task.Start();
        return task;
    }

    protected override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        var task = result as Task<T>;
        if (task.Exception != null)
        {
            // Error handling. Rethrow? Cancel?
        }

        return task.Result;
    }

    private T Execute(object state)
    {
        // Logic here
        return default(T);
    }
}
Run Code Online (Sandbox Code Playgroud)

我有一些问题:

  1. 哪种方法可以处理异常?重新抛出?将上下文设置为已取消?
  2. 是否有一种优雅的方式来编写现在可用的async/await语法?

谢谢

Ste*_*ary 14

1)您应该从您的EndExecute方法中重新抛出异常.

2)我建议你创建自己的基本类型.我在AsyncTaskCodeActivity<T>下面写了一个:

public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>
{
    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        var task = ExecuteAsync(context);
        var tcs = new TaskCompletionSource<T>(state);
        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;
    }

    protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        var task = (Task<T>)result;
        try
        {
            return task.Result;
        }
        catch (AggregateException ex)
        {
            ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
            throw;
        }
    }

    protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context);
}
Run Code Online (Sandbox Code Playgroud)

如果您使用我的AsyncEx库,这个包装器变得更加简单:

public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>
{
    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        var task = ExecuteAsync(context);
        return AsyncFactory<T>.ToBegin(task, callback, state);
    }

    protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        return AsyncFactory<T>.ToEnd(result);
    }

    protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context);
}
Run Code Online (Sandbox Code Playgroud)

获得基本类型后,您可以定义自己的派生类型.这是一个使用async/ await:

public sealed class MyActivity : AsyncTaskCodeActivity<int>
{
    protected override async Task<int> ExecuteAsync(AsyncCodeActivityContext context)
    {
        await Task.Delay(100);
        return 13;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个将CPU绑定工作安排到线程池(类似于您当前的模板):

public sealed class MyCpuActivity : AsyncTaskCodeActivity<int>
{
    protected override Task<int> ExecuteAsync(AsyncCodeActivityContext context)
    {
        return Task.Run(() => 13);
    }
}
Run Code Online (Sandbox Code Playgroud)

来自评论的更新:这是使用取消的.我不是100%肯定它是正确的,因为取消本身是异步的,并且语义AsyncCodeActivity<T>.Cancel是未被记录的(即,Cancel应该等待活动在取消状态下完成?活动是否可以接受成功完成后Cancel是叫什么名字?).

public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>
{
    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        var cts = new CancellationTokenSource();
        context.UserState = cts;
        var task = ExecuteAsync(context, cts.Token);
        return AsyncFactory<T>.ToBegin(task, callback, state);
    }

    protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        try
        {
            return AsyncFactory<T>.ToEnd(result);
        }
        catch (OperationCanceledException)
        {
            if (context.IsCancellationRequested)
                context.MarkCanceled();
            else
                throw;
            return default(T); // or throw?
        }
    }

    protected override void Cancel(AsyncCodeActivityContext context)
    {
        var cts = (CancellationTokenSource)context.UserState;
        cts.Cancel();
    }

    protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken);
}
Run Code Online (Sandbox Code Playgroud)

  • 很棒的答案!谢谢! (2认同)