Подтвердить что ты не робот

Использование HttpContext в Async Task

У меня есть следующее действие mvc.

public async Task<JsonResult> DoSomeLongRunningOperation()
{
    return await Task.Run(() =>
    {
        //Do a lot of long running stuff
        //The underlying framework uses the HttpContext.Current.User.Identity.Name so the user is passed on the messagebus.
    }
}

В задаче HttpContext получает значение null. Мы много обманывали, но ничто не гарантирует нам, что HttpContext всегда доступен в нашем новом потоке.

Есть ли решение использовать HttpContext внутри асинхронных задач?

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

public class HttpContextUserIdentityName : ICredentials
{
    public string Name
    {
        get { return HttpContext.Current.User.Identity.Name; }
    }
}

Этот код вызывается во многих местах до того, как он останется в базе данных.

Нам нужен либо другой способ получить имя пользователя пользователя, инициировавшего веб-запрос, либо исправить проблему, когда HttpContext имеет значение null.

Поскольку сохранение в базе данных происходит в Задаче, я не могу получить доступ к HttpContext перед входом в задачу.

Я также не могу придумать безопасный способ временного сохранения имени пользователя, чтобы я мог реализовать другой объект службы ICredentials.

4b9b3361

Ответ 1

Вы почти никогда не хотите использовать Task.Run в методе ASP.NET.

Я думаю, что самым чистым решением (но самой работой) является реализация async -совместимых интерфейсов на других уровнях:

public async Task<JsonResult> DoSomeLongRunningOperation()
{
  //Do a lot of long running stuff
  var intermediateResult = await DoLongRunningStuff();
  return await DetermineFinalResult(intermediateResult);
}

Ответ 2

Я бы попытался передать ссылку на HttpContext как объект состояния, потому что это должно создать новый экземпляр этого объекта в стеке для потока, который выполняет эту работу. Вместо использования Task.Run используйте

return await Task.Factory.StartNew((ctx) => 
{
    var context = (HttpContext)ctx;
   //Do stuff
}, httpContextObject);

Task.Run и Task.Factory.StartNew возвращаются немедленно, поэтому asp.net продолжается в жизненном цикле события в рабочем потоке, который обрабатывает запрос, пока ваш поток работает с уже уложенным объектом.

Ответ 3

Перед тем, как начать новый поток, вы должны получить всю необходимую информацию из текущего контекста. В этом случае добавьте что-то вроде:

string username = HttpContext.Current.User.Username;

до Task.Run, а затем используйте это внутри другого потока.

На стороне примечания, как она есть, нет причин для await задачи. Вы можете просто вернуть задачу напрямую и не пометить метод как Async.

Если вам нужно получить доступ к объекту Response, который предположительно будет использовать результаты длительной операции и, следовательно, не может быть до Task.Run, вы должны сделать это после Task.Run (но убедитесь, что задача await ed). Если вы это сделаете, вы не сможете сделать то, что я предложил в предыдущем абзаце.