0

ODATA 용 WCF 데이터 서비스 클라이언트가있는 Windows 8 Store App에서 Enterprise Library Transient Fault Handling 응용 프로그램 블록을 사용하고 있습니다. ODATA 서비스를 호출 할 때 발생하는 일시적인 오류에 대한 재시도 논리를 사용하려고합니다. 나는 커스텀 일시적인 에러 검출 전략을 세웠다.EntLib TransientFaultHandling RetryPolicy.ExecuteAsync 동기화 컨텍스트

DataAsvice 메서드가 LoadCompleted 이벤트를 발생시키는 대신 DataServiceCollection에 대해 LoadAsync 메서드가 작업을 반환하지 않으므로 DataServiceCollection에 대한 LoadTaskAsync 확장 메서드를 구축했습니다. 다음과 같이

그래서 나는 DataServiceCollection에 데이터를로드 할 수

var query = this.DataContext.Products.Where(item => item.Modified >= anchor); 
var products = new DataServiceCollection<Product>(this.DataContext); 
await this.retryPolicy.ExecuteAsync(() => products.LoadTaskAsync(query)); 

이제 응용 프로그램 블록을 처리 엔터프라이즈 라이브러리 과도 오류에 대한 문서는

가 taskFunc 인수는 ExecuteAsync에 전달한다고 메서드는 반드시 원래 ExecuteAsync를 호출 할 때 사용한 것과 동일한 동기화 컨텍스트에서 호출 할 필요는 없습니다. 예를 들어 UI 스레드 내에서 작업을 시작해야하는 경우 델리게이트 내에서 명시 적으로 일정을 예약해야합니다.

로드 작업은 이미 데이터 컨텍스트에 의해 추적되고 UI에 바인딩 된 데이터를 업데이트 할 수 있으므로 UI ​​스레드에서 LoadTaskAsync 메서드를 호출해야합니다.

질문은 어떻게됩니까? LoadTaskAsync 확장 메서드를 수정하지 않고 (내 코드가 변경되지 않는 경우) 무엇을 선호합니다. taskFunc가 UI 스레드에서 호출되는지 확인하면서 ExecuteAsync 메서드를 호출하는 RetryPolicy의 확장 메서드를 만드는 방법을 생각했습니다.

가장 쉬운 방법은 아마도 TaskCreationOptions.AttachedToParent에 전달하는 LoadTaskAsync 확장 방법을 수정하는 것입니다, 그래서 다음과 같이 나는 RetryPolicy에 대한 확장 메서드를 만들 수 있습니다

public static Task<TResult> ExecuteCurrentSynchronizationContextAsync<TResult>(
     this RetryPolicy retryPolicy, 
     Func<TaskCreationOptions, Task<TResult>> taskFunc) 
    { 
     var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

     return 
      retryPolicy.ExecuteAsync(
       () => 
       Task.Factory.StartNew(
        () => taskFunc(TaskCreationOptions.AttachedToParent), 
        CancellationToken.None, 
        TaskCreationOptions.None, 
        scheduler).Unwrap()); 
    } 

참고 taskFunc 지금을해야한다는 함수 < TaskCreationOptions, 작업 <TResult> >. 다음과 같이

나는 다음이 부를 것이다 :

 await 
      this.retryPolicy.ExecuteCurrentSynchronizationContextAsync(
       creationOptions => products.LoadTaskAsync(query, creationOptions)); 

나는 LoadTaskAsync 확장 방법을 변경하지 않으려는, 나는이 RetryPolicy ExecuteCurrentSynchronizationContextAsync 확장 방법을 변경할 수있는 방법을 taskFunc는 Func을 < 작업 < TResult 할 수 있도록 > > UI 스레드에서 taskFunc가 호출되는지 다시 확인 하시겠습니까?

답변

1

비동기 작업에 AttachedToParent을 사용하지 않는 것이 좋습니다. 사실 대부분의 약속 스타일 작업에는 DenyChildAttach which prevents AttachedToParent from working이 지정됩니다.

대신 동기화 컨텍스트 자체를 캡처하여 사용해야합니다. 주름이 있습니다. Windows Store 앱 don't allow synchronous invocation on the synchronization context이므로 SynchronizationContext 대신 CoreDispatcher.RunAsync을 사용하거나 SynchronizationContext에 대해 직접 async- 인식 확장 방법을 구축해야합니다. 이 두 가지 중에서 나는 SynchronizationContext 접근 방법을 선호합니다. 이 경우에는 좀 더 많은 코드가 필요 하겠지만 이는 코드 (아마도 서비스 레이어 코드)를이 특정 UI 프레임 워크에 묶을 필요가 없다는 것을 의미합니다.

public static Task<TResult> RunAsync<TResult>(this SynchronizationContext context, Func<Task<TResult>> func) 
{ 
    var tcs = new TaskCompletionSource<TResult>(); 
    context.Post(async _ => 
    { 
     try 
     { 
      tcs.TrySetResult(await func()); 
     } 
     catch (OperationCanceledException) 
     { 
      tcs.TrySetCanceled(); 
     } 
     catch (Exception ex) 
     { 
      tcs.TrySetException(ex); 
     } 
    }, null); 
    return tcs.Task; 
} 

그런 다음 우리가 캡처하고 SynchronizationContext 사용할 수 있습니다 :

public static Task<TResult> ExecuteOnCurrentSynchronizationContextAsync<TResult>(
    this RetryPolicy retryPolicy, 
    Func<Task<TResult>> taskFunc) 
{ 
    var context = SynchronizationContext.Current ?? new SynchronizationContext(); 

    return retryPolicy.ExecuteAsync(() => context.RunAsync(taskFunc)); 
} 
+0

그래서 먼저 우리는 (비동기) 지정된 동기화 컨텍스트에서 비동기 코드를 실행합니다 SynchronizationContext,에 RunAsync 정의 뛰어난 솔루션. 그것은 나를 위해 일합니다. 고맙습니다. –