1) Bạn nên rethrow ngoại trừ từ phương pháp EndExecute
của bạn.
2) Tôi khuyên bạn nên tạo loại cơ sở của riêng bạn. Tôi viết lên gọi AsyncTaskCodeActivity<T>
dưới đây:
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);
}
Nếu bạn sử dụng thư viện AsyncEx tôi, wrapper này trở nên đơn giản hơn nhiều:
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);
}
Khi bạn có các loại cơ sở, bạn có thể xác định loại hình xuất phát của riêng bạn. Đây là một loại sử dụng async
/await
:
public sealed class MyActivity : AsyncTaskCodeActivity<int>
{
protected override async Task<int> ExecuteAsync(AsyncCodeActivityContext context)
{
await Task.Delay(100);
return 13;
}
}
Và đây cũng là loại lịch trình CPU-bound việc để các hồ bơi thread (tương tự như mẫu hiện tại của bạn):
public sealed class MyCpuActivity : AsyncTaskCodeActivity<int>
{
protected override Task<int> ExecuteAsync(AsyncCodeActivityContext context)
{
return Task.Run(() => 13);
}
}
Update từ bình luận : Đây là một trong số đó sử dụng hủy. Tôi không chắc chắn 100% là chính xác, vì hủy chính nó là không đồng bộ, và ngữ nghĩa cho AsyncCodeActivity<T>.Cancel
là không có giấy tờ (tức là, Cancel
phải đợi cho hoạt động hoàn thành ở trạng thái bị hủy không? sau khi Cancel
được gọi?).
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);
}
Câu trả lời hay! Cảm ơn! – fra
Một yêu cầu bổ sung nhỏ: hoạt động không đồng bộ tôi cần là kích hoạt có thể trong hoạt động chọn, do đó tôi cũng cần hủy bỏ (duyên dáng) ... làm thế nào để mở rộng hỗ trợ kịch bản như vậy? cảm ơn – fra
Tôi không quen với việc hủy WF, nhưng tôi hy vọng bạn có thể tạo một 'CancellationTokenSource' trong' BeginExecute' (và lưu nó trong ngữ cảnh), chuyển mã thông báo tới 'ExecuteAsync'. Sau đó ghi đè lên 'Cancel' để lấy' CancellationTokenSource' từ ngữ cảnh, hủy bỏ nó và gọi 'MarkCanceled'. –