6

Будет ли использовать Thread.CurrentPrincipal в ссылочной библиотеке, которая использует ConfigureAwait (false), создавать какие-либо проблемы или будет ли поток логического контекста вызова ExecutionContext заботиться обо мне? (мое чтение и тестирование пока показывают, что это произойдет). ДействиеБезопасность, Thread.CurrentPrincipal и ConfigureAwait (false)

Пример WebAPI Контроллер:

[CustomAuthorizeThatSetsCurrentUsersClaimsToThreadCurrentContextAndHttpContextCurrentUser] 
public async Task<Order> Get(int orderId) 
{ 
    return await _orderBusinessLogicLibrary.LoadAsync(orderId); // defaults to .ConfigureAwait(true) 
} 

Пример загрузки функции из внешней, ссылочного библиотеки:

[ClaimsPrincipalPermission(
    SecurityAction.Demand, 
    Operation="Read", 
    Resource="Orders")] 
[ClaimsPrincipalPermission(
    SecurityAction.Demand, 
    Operation="Read", 
    Resource="OrderItems")] 
public async Task<Order> Load(int orderId) 
{ 
    var order = await _repository.LoadOrderAsync(orderId).ConfigureAwait(false); 

    // here's the key line.. assuming this lower-level function is also imposing 
    // security constraints in the same way this method does, would 
    // Thread.CurrentPrincipal still be correct inside the function below? 
    order.Items = await _repository.LoadOrderItemsAsync(orderId).ConfigureAwait(false); 
    return order; 
} 

Кроме того, ответ не может быть «хорошо не использовать ConfigureAwait (ложь) тогда!". Это может вызвать другие проблемы, такие как взаимоблокировка (Don't Block on Async Code).

ответ

12

Из моих тестов выяснилось, что Thread.CurrentPrincipal будет работать правильно, даже если вы используете ConfigureAwait(false). Следующий код WebAPI устанавливает основной, а затем блокирует вызов async, заставляя другой поток возобновить метод async. Этот другой поток наследует правильный принцип.

private async Task<string> Async() 
{ 
    await Task.Delay(1000).ConfigureAwait(false); 
    return "Thread " + Thread.CurrentThread.ManagedThreadId + ": " + Thread.CurrentPrincipal.Identity.Name + "\n"; 
} 

public string Get(int id) 
{ 
    var user = new ClaimsPrincipal(new ClaimsIdentity(
     new[] 
     { 
      new Claim(ClaimTypes.Name, "Bob"), 
     } 
    )); 
    HttpContext.Current.User = user; 
    Thread.CurrentPrincipal = user; 

    var ret = "Thread " + Thread.CurrentThread.ManagedThreadId + ": " + Thread.CurrentPrincipal.Identity.Name + "\n"; 

    ret += Async().Result; 

    return ret; 
} 

Когда я запускаю этот код на новый экземпляр IISExpress, я получаю:

"Thread 7: Bob\nThread 6: Bob\n" 

Однако следует отметить, что использование ConfigureAwait(false), чтобы избежать тупиковой ситуации не рекомендуется. Это особенно актуально для ASP.NET. Если это вообще возможно, используйте ConfigureAwait(false), а также использовать async полностью. Обратите внимание, что WebAPI - это полный стек async, и вы должны должны быть в состоянии сделать это.

+0

Отлично, я видел те же результаты. Один вопрос для пояснения - ваш последний комментарий о том, что вы не используете 'ConfigureAwait (false)', чтобы избежать тупиковой ситуации, является специфическим для использования .Result для блокировки метода async, правильно? Поэтому вы не должны переключаться с «ConfigureAwait (true)» на «ConfigureAwait (false)», чтобы исправить тупик, вы должны сделать весь стек полностью асинхронным и все равно хотите, чтобы «ConfigureAwait (true)» на ASP.NET ожидал, чтобы полностью восстановить HttpContext и так далее. –

+3

@MichaelTrotter: Значение по умолчанию для 'ConfigureAwait' равно' true'. Поэтому, если ваш метод 'async' нуждается в контексте, он должен просто не использовать' ConfigureAwait' вообще. Если контекст не нужен, он должен использовать 'ConfigureAwait (false)'. Я рекомендую сделать весь стек 'async', а также использовать' ConfigureAwait (false) 'для любого метода, который не нуждается в контексте. –

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