2013-06-06 35 views
5

Kể từ thời gian dài tôi đang viết lớp AsyncCodeActivity tùy chỉnh bằng cách sử dụng mẫu sau:AsyncCodeActivities Thực hiện (sử dụng C# async/chờ đợi)

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); 
    } 
} 

Tôi có một số câu hỏi về nó:

  1. nào là đúng cách để xử lý các ngoại lệ? Rethrowing? Đặt bối cảnh là bị hủy?
  2. Có cách nào thanh lịch để viết bằng cú pháp không đồng bộ/chờ đợi hiện có không?

Cảm ơn

Trả lời

13

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); 
} 
+2

Câu trả lời hay! Cảm ơn! – fra

+0

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

+0

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'. –

Các vấn đề liên quan