2

Хорошо, так что медведь со мной может потребовать объяснения, у меня есть простой контроллер учетной записи;Почему метод async/await в атрибуте никогда не возвращается

[RoutePrefix("api/account")] 
[Authorize] 
[HmacAuthentication] 
public class AccountController : ApiController 
{ 
    public async Task<IHttpActionResult> Register(UserModel userModel) 
    { 
     if (!this.ModelState.IsValid) 
     { 
      return this.BadRequest(this.ModelState); 
     } 

     IdentityResult result = await this._userService.RegisterUser(userModel); 

     var errorResult = this.GetErrorResult(result); 
     if (errorResult != null) 
     { 
      return errorResult; 
     } 

     return this.Ok(); 
    } 
} 

Атрибут HmacAuthentication здесь:

public class HmacAuthenticationAttribute : Attribute, IAuthenticationFilter 
{ 
    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) 
    { 
     ... 

     var isValid = this.IsValidRequest(req, appId, incomingBase64Signature, nonce, requestTimeStamp); 

     if (isValid.Result) 
     { 
      ... 
     } 
     ... 
    } 

    private async Task<bool> IsValidRequest(
     HttpRequestMessage req, 
     string appId, 
     string incomingBase64Signature, 
     string nonce, 
     string requestTimeStamp) 
    { 
     ... 
     var user = await this.UserService.FindUser(userId); // this never gets a return value 
     ... 
    } 
} 

метод вызывается в UserService это:

public async Task<ApplicationUserModel> FindUser(int id) 
{ 
    var user = await this._userBusiness.FindAsync(id); 
    return this.MapToModel(user); 
} 

и в бизнес-классе это:

public async Task<ApplicationUser> FindAsync(int id) 
{ 
    var result = await this._userManager.FindByIdAsync(id); 
    return result; 
} 

проблема, с которой я сталкиваюсь, заключается в том, что при вызове метода Register активируется атрибут HmacAuthentication и выполняется метод фильтра AuthenticateAsync. Вызов в IsValidRequest для поиска пользователя никогда не получает возвращаемого значения, если я пробую запрос через почтальон, он никогда не завершается.

Может ли кто-нибудь помочь в этом, пожалуйста?

+0

Что вы делаете с 'isValid'? Вы когда-нибудь ждали? – sstan

+1

Можете ли вы определить, застрял ли код в строке 'isValid.Result'? Если это так, вы, вероятно, сталкиваетесь с тупиком. Прочитайте [здесь] (http://stackoverflow.com/questions/17248680/await-works-but-calling-task-result-hangs-deadlocks), почему вы обычно не хотите использовать 'Task.Result', чтобы ждать задача. – sstan

+0

иногда вар - это плохо, это заставляет вас забыть ожидание ... –

ответ

5
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) 
    { 
     ... 

     var isValid = await this.IsValidRequest(req, appId, incomingBase64Signature, nonce, requestTimeStamp); 

     if (isValid) 
     { 
      ... 
     } 
     ... 
    } 

Как компилятор предложил, вы можете использовать только await ключевое слово внутри метода, помеченный как async. Поэтому я обновил подпись для AuthenticateAsync соответственно. Также обратите внимание, что теперь вы можете просто проверить isValid вместо того, чтобы делать isValid.Result, так как значение булева будет доступно за ожидаемой строкой.

Это может быть немного запутанным, поскольку интерфейс для IAuthenticationFilter не указывает async (интерфейсы не могут, они могут только указывать, что метод вернет задание). Разработчик должен определить, будет ли метод просто возвращать задачу или будет поставлять асинхронный запрос, чтобы значения можно было ожидать внутри тела метода.

+0

Я пробовал это, но я получаю ошибку времени компиляции 'Оператор 'ожидание' может использоваться только в метод или лямбда, отмеченный «модификатором async», метод, который я вызываю, находится в интерфейсе фреймворка 'public Task AuthenticateAsync (HttpAuthenticationContext context, CancellationToken cancelationToken)' –

+0

Я не знал, что вы можете украсить метод с помощью async, который не был вашим методом я все еще пытаюсь поднять голову на ожидание/асинк, но это и трюк, также спасибо за комментарий выше от @sstan, он действительно заблокирован на 'isValid.Result', предположив, что это была предыдущая строка, так как я никогда не получал что далеко при переходе через –

+0

Вы можете сделать это только по методам интерфейса, которые возвращают задачи. async/await определенно берет немного, чтобы обернуть вашу голову, но она безумно мощная, и как только вы займетесь ею, вы будете удивлены, как вы когда-либо закодировали без нее :) –

4

Принятый ответ правильный, как исправить вашу проблему, но не объясняет , почему это происходит.

Это:

var isValid = this.IsValidRequest(
        req, 
        appId, 
        incomingBase64Signature, 
        nonce, 
        requestTimeStamp); 

if (isValid.Result) 

вызывает ваш код тупиковой. Вы синхронно блокируете метод async. Это происходит потому, что когда компилятор видит метод async, он генерирует машину состояний, которая берет все после первого await и завершает ее как продолжение. Компилятор также видит, есть ли какие-либо контексты синхронизации, и если есть, он пытается перевести продолжение на AspNetSynchronizationContext, который заблокирован вашим вызовом .Result, что фактически вызывает тупик.

Вот почему you shouldn't block on async code

Смежные вопросы