2012-11-12 8 views
2

MonoTouch 앱에서 DropBox API에 비동기 적으로 액세스하려고합니다.
DropNet 자체가 RestSharp에 의존하는 것이 편리하다고 생각했습니다.DropNet에서 CancellationToken 지원을 구현하는 방법은 무엇입니까?

두 라이브러리 모두 제대로 작동하지만 DropNet 오버로드가 발생하면 Task이 취소 토큰과 요청을 연결하는 방법을 제공하지 않습니다.

public Task<IRestResponse> GetThumbnailTask(string path, ThumbnailSize size) 
{ 
    if (!path.StartsWith("/")) path = "/" + path; 
    var request = _requestHelper.CreateThumbnailRequest(path, size, Root); 
    return ExecuteTask(ApiType.Content, request, cancel); 
} 

ExecuteTask 구현 TaskCompletionSource를 기반으로하고 was 원래 written by Laurent Kempé :

이 자신의 구현 모습입니다

public static class RestClientExtensions 
{ 
    public static Task<TResult> ExecuteTask<TResult>(
     this IRestClient client, IRestRequest request 
     ) where TResult : new() 
    { 
     var tcs = new TaskCompletionSource<TResult>(); 

     WaitCallback asyncWork = _ => { 
      try { 
       client.ExecuteAsync<TResult>(request, 
        (response, asynchandle) => { 
         if (response.StatusCode != HttpStatusCode.OK) { 
          tcs.SetException(new DropboxException(response)); 
         } else { 
          tcs.SetResult(response.Data); 
         } 
        } 
       ); 
      } catch (Exception exc) { 
        tcs.SetException(exc); 
      } 
     }; 

     return ExecuteTask(asyncWork, tcs); 
    } 


    public static Task<IRestResponse> ExecuteTask(
     this IRestClient client, IRestRequest request 
     ) 
    { 
     var tcs = new TaskCompletionSource<IRestResponse>(); 

     WaitCallback asyncWork = _ => { 
      try { 
       client.ExecuteAsync(request, 
        (response, asynchandle) => { 
         if (response.StatusCode != HttpStatusCode.OK) { 
          tcs.SetException(new DropboxException(response)); 
         } else { 
          tcs.SetResult(response); 
         } 
        } 
       ); 
      } catch (Exception exc) { 
        tcs.SetException(exc); 
      } 
     }; 

     return ExecuteTask(asyncWork, tcs); 
    } 

    private static Task<TResult> ExecuteTask<TResult>(
     WaitCallback asyncWork, TaskCompletionSource<TResult> tcs 
     ) 
    { 
     ThreadPool.QueueUserWorkItem(asyncWork); 
     return tcs.Task; 
    } 
} 

가 어떻게 변경하거나 CancellationToken와 취소를 지원하기 위해이 코드를 확장 할 수 있습니까?
나는 다음과 같이 호출 싶습니다 값 유형이

var task = dropbox.GetThumbnailTask(
    "/test.jpg", ThumbnailSize.ExtraLarge2, _token 
); 

답변

1

CancellationToken 때문에, 우리는 기본 값으로 API에 대한 선택적 매개 변수로 추가하고 달콤한 널 (null) 검사를 피할 수 있습니다.

public Task<IRestResponse> GetThumbnailTask(
    string path, ThumbnailSize size, CancellationToken cancel = default(CancellationToken) 
) { 
    if (!path.StartsWith("/")) path = "/" + path; 
    var request = _requestHelper.CreateThumbnailRequest(path, size, Root); 
    return ExecuteTask(ApiType.Content, request, cancel); 
} 

이제 RestSharp ExecuteAsync 방법은 Abort 방법과 함께, HttpWebRequest을 기본 캡슐화 RestRequestAsyncHandle를 반환합니다. 이것은 우리가 사물을 취소하는 방법입니다.

public static Task<TResult> ExecuteTask<TResult>(
    this IRestClient client, IRestRequest request, CancellationToken cancel = default(CancellationToken) 
    ) where TResult : new() 
{ 
    var tcs = new TaskCompletionSource<TResult>(); 
    try { 
     var async = client.ExecuteAsync<TResult>(request, (response, _) => { 
      if (cancel.IsCancellationRequested || response == null) 
       return; 

      if (response.StatusCode != HttpStatusCode.OK) { 
       tcs.TrySetException(new DropboxException(response)); 
      } else { 
       tcs.TrySetResult(response.Data); 
      } 
     }); 

     cancel.Register(() => { 
      async.Abort(); 
      tcs.TrySetCanceled(); 
     }); 
    } catch (Exception ex) { 
     tcs.TrySetException(ex); 
    } 

    return tcs.Task; 
} 

public static Task<IRestResponse> ExecuteTask(this IRestClient client, IRestRequest request, CancellationToken cancel = default(CancellationToken)) 
{ 
    var tcs = new TaskCompletionSource<IRestResponse>(); 
    try { 
     var async = client.ExecuteAsync<IRestResponse>(request, (response, _) => { 
      if (cancel.IsCancellationRequested || response == null) 
       return; 

      if (response.StatusCode != HttpStatusCode.OK) { 
       tcs.TrySetException(new DropboxException(response)); 
      } else { 
       tcs.TrySetResult(response); 
      } 
     }); 

     cancel.Register(() => { 
      async.Abort(); 
      tcs.TrySetCanceled(); 
     }); 
    } catch (Exception ex) { 
     tcs.TrySetException(ex); 
    } 

    return tcs.Task; 
} 

마지막으로, Lauren's implementation는 스레드 풀에서 요청을두고 있지만 ExecuteAsync 자체 비동기 소위 할 수있는 이유가 표시되지 않습니다. 그래서 나는 그렇게하지 않는다.

DropNet 작업을 취소하기위한 것입니다.

나는 또한 당신에게 유용 할 수있는 약간 비틀기를 만들었다. 내가 Task의 다른 Task들에 ExecuteTask 전화를 포장에 의존하지 않고 일정 보관 용의 방법이 없었 때문에

, 나는 나를 위해 4 밝혀졌다 요청을위한 최적의 동시성 레벨을 발견하고 명시 적으로 설정하기로 결정했다 :

TaskScheduler.UnobservedTaskException += (sender, e) => { 
    e.SetObserved(); 
}; 

마지막으로 관찰이 요입니다 : 그게 내가 무슨 짓을했는지, 그래서

static readonly Uri DropboxContentHost = new Uri("https://api-content.dropbox.com"); 

static DropboxService() 
{ 
    var point = ServicePointManager.FindServicePoint(DropboxContentHost); 
    point.ConnectionLimit = 4; 
} 

나는 또한 작업 예외가 지옥에서 썩어 처리되지 않은시키기에 만족했다 u 은 작업이 바로 시작되기 때문에 DropNet에 의해 반환 된 작업에서 Start()을 호출하면 안됩니다. 당신이 그것을 좋아하지 않으면, TaskCompletionSource에 의해 뒷받침되지 않은 또 다른 "진짜"작업에서 ExecuteTask을 감쌀 필요가 있습니다.