2013-06-07 6 views
5

저는 일주일 동안 async/await로 어려움을 겪어 왔습니다. 일부 배경 : 아래 코드는 MVC4 웹 사이트 프로젝트의 일부입니다. 웹 사이트에는 많은 수의 API 호출이 있습니다. 목표는 동기식 대신에 이러한 API 호출을 병렬로 처리하여 사이트 응답 속도를 향상시키는 것입니다. 현재 모든 API 호출은 서로를 차단합니다. 따라서 한 페이지에 4 개의 통화가 필요한 경우 ...로드 시간이 길어집니다.MVC4에서 비동기/응답하지 않음

모든 API 호출의 동기식 버전과 비동기식 버전에 대한 개별 메소드를 구축했습니다. 내가 가진 문제는 결코 응답하지 않을 것이라는 것입니다. 나는 그것이 this question와 관련이 있다고 생각합니다. 그러나, 나는 그것을 해결하는 방법을 정말로 모른다. 나는 ConfigureAwait (false)를 시도했는데, 그것이 나를 도왔다. 여기

코드입니다 :

그렇게 보이는 컨트롤러의 초기 호출 :

내가 사용 싶네요
BaseData bdata = API.GetBaseData().Result; 

여기 기다리고 있지만은 AsyncController없는 옵션이 아니다 그것은 우리 요청/응답 액세스가 필요하기 때문에 사용할 수 없습니다. 다른 방법은 API 클래스에 이러한 메소드가 호출 될 때

internal static async Task<BaseData> GetBaseData() { 
    var catTask = GetParentCategoriesAsync(); 
    var yearTask = GetYearsAsync(); 

    await Task.WhenAll(new Task[] { catTask, yearTask }); 

    var bdata = new BaseData { 
     years = await yearTask, 
     cats = await catTask 
    }; 
    return bdata; 
} 

internal static async Task<List<APICategory>> GetParentCategoriesAsync() { 
    try { 
     WebClient wc = new WebClient(); 
     wc.Proxy = null; 

     string url = getAPIPath(); 
     url += "GetFullParentCategories"; 
     url += "?dataType=JSON"; 

     Uri targeturi = new Uri(url); 
     List<APICategory> cats = new List<APICategory>(); 

     var cat_json = await wc.DownloadStringTaskAsync(targeturi); 
     cats = JsonConvert.DeserializeObject<List<APICategory>>(cat_json); 

     return cats; 
    } catch (Exception) { 
     return new List<APICategory>(); 
    } 
} 

internal static async Task<List<double>> GetYearsAsync() { 
    WebClient wc = new WebClient(); 
    wc.Proxy = null; 
    Uri targeturi = new Uri(getAPIPath() + "getyear?dataType=JSON"); 
    var year_json = await wc.DownloadStringTaskAsync(targeturi); 
    List<double> years = JsonConvert.DeserializeObject<List<double>>(year_json); 
    return years; 
} 

, 나는 GetYearsAsync()와 GetParentCategoriesAsync에 중단 점을 넣을 수 있습니다(). wc.DownloadStringTaskAsync (targeturi) 명령을 기다릴 때까지 모든 작업이 시작됩니다. 그것이 멈추는 곳입니다.

모든 작업에 ConfigureAwait (continueOnCapturedContext : false)를 추가했지만 도움이되지 않았습니다. 문제는 스레드가 동일한 컨텍스트에 있지 않다는 것입니다. 그러나, 나는 확실하지 않다. 그러나 나는 틀린 일을하고 있다고 확신합니다. 나는 무엇이 확실하지 않다. 그것은 그 중 하나입니다. 또는 .NET MVC4로 수행 할 수없는 작업을하려고하고 있습니다. 어떤 생각이라도 대단히 감사하겠습니다.

+5

"모든 방법을 비동기"할 수 없다면 비동기 메서드를 전혀 사용하지 않아도됩니다. 최상위 수준의 작업을 차단하려는 경우 작업의 전체 기간 동안 스레드 풀 스레드를 계속 사용 중입니다. 처음부터 끝까지 비동기 메서드를 사용할 수도 있습니다. – Servy

+0

백그라운드에서 장기 실행 작업을 수행하려면 HTTP 요청을 초과하여 실행되는 서비스가 필요합니다. –

+3

"요청/응답 액세스가 필요하기 때문에 사용할 수없는"-이 의미가 무엇인지 확신 할 수 없습니까? 'async' 코드는 요청과 응답에 잘 접근 할 수 있습니다. –

답변

0

나는 Servy 's와 Stephen Cleary의 조언을 따라 갔다. Stephen의 응답에 따라 컨트롤러에서 비동기 호출을하기 전에 요청/응답에 액세스 할 수 있다는 것을 알게되었습니다.

변수에 HttpContext를 저장 한 경우 해당 컨텍스트를 액세스 할 필요가있는 모든 모델/서비스 메서드에 전달할 수 있습니다. 이것은 Servy가 제안한 것처럼 비동기로 끝까지 진행할 수있었습니다. 그 후, 나는 비동기 패턴으로 원했던 어떤 것에도 문제가 없었다.

+2

변수에'HttpContext'를 건네 줄 수없는 다른 쓰레드에 전달하는 것은 권장하지 않습니다. 그냥'async'와'await'을 사용하면 ('ConfigureAwait'없이)'* await' 전 *과 * 후에 * HttpContext.Current'에 액세스 할 수 있습니다. –

+0

처음에는 비공식적 인 오래된 async/await로 처음에 실행 한 큰 문제는 HttpContext.Current가 쿠키 데이터를 가져오고 설정해야하는 도우미 메서드에서 비어 있다는 것이 었습니다. async와 await 만 있으면 HttpContext.Current에 액세스 할 때 null 참조 오류가 발생합니다. 변수에 HttpContext.Current를 저장하면 다른 언어로 모델링 한 것처럼 많이 전달할 수 있습니다. 변수로 저장하지 않으면 쿠키 논리가 전혀 작동하지 않습니다. 나는 아직도 내가 놓친 뭔가가 있어야만한다고 생각한다. – janiukjf

+0

그 때 다른 문제가 있습니다. 왜'HttpContext.Current'가'null'인지에 대한 질문으로 최소한의 repro를 게시하는 것이 좋습니다. –

2

문제가 WebClient 인해 실제로있는 것 항상 백 (인해 Result 통화 차단) 요청 컨텍스트 싱크. ConfigureAwait(false)와 함께 당신은 대신 HttpClient을 사용할 수 있습니다

: 그러나

BaseData bdata = API.GetBaseDataAsync().Result; 

내가 당신이로 전화하는 것이 좋습니다 강하게 :

수 있도록해야
internal static async Task<BaseData> GetBaseDataAsync() { 
    var catTask = GetParentCategoriesAsync(); 
    var yearTask = GetYearsAsync(); 

    await Task.WhenAll(catTask, yearTask).ConfigureAwait(false); 

    var bdata = new BaseData { 
    years = await yearTask, 
    cats = await catTask 
    }; 
    return bdata; 
} 

internal static async Task<List<APICategory>> GetParentCategoriesAsync() { 
    try { 
    var client = new HttpClient(); 

    string url = getAPIPath(); 
    url += "GetFullParentCategories"; 
    url += "?dataType=JSON"; 

    Uri targeturi = new Uri(url); 
    List<APICategory> cats = new List<APICategory>(); 

    var cat_json = await client.GetStringAsync(targeturi).ConfigureAwait(false); 
    cats = JsonConvert.DeserializeObject<List<APICategory>>(cat_json); 

    return cats; 
    } catch (Exception) { 
    return new List<APICategory>(); 
    } 
} 

internal static async Task<List<double>> GetYearsAsync() { 
    var client = new HttpClient(); 
    Uri targeturi = new Uri(getAPIPath() + "getyear?dataType=JSON"); 
    var year_json = await client.GetStringAsync(targeturi).ConfigureAwait(false); 
    List<double> years = JsonConvert.DeserializeObject<List<double>>(year_json); 
    return years; 
} 

같은 호출하기 그러한 :

BaseData bdata = await API.GetBaseDataAsync(); 

await 전후의 코드는 요청 및 응답 컨텍스트에 잘 액세스 할 수 있습니다.