2016-08-15 8 views
3

Как мне позвонить async из Session_Start в Global.asax?Метод вызова Async из Session_Start

Global.asax: метод

protected async Task Session_Start(object sender, EventArgs e) 
    {    
     Session.Timeout = 10; 

     // Do some asynch work 
     await repository.SetStatsInfo(System.DateTime.Now);    
    } 

асинхронный:

public async Task SetStatsInfo(DateTime time) 
    { 
     using (ApplicationDBContext db = new ApplicationDBContext()) 
     { 
      // Do stuff (update visitors counter in db) .. 

      await db.SaveChangesAsync(); 

     } 
    } 

я могу запустить все это синхронно (определить void Session_Start и т.д.), который работает, но предпочел бы асинхронный путь так, что удар db не блокирует.

Выполняется как «async Task» для Session_Start, код не выполнен, контрольные точки внутри session_start не попадают.

+1

Крис сказал об этом правильно. Нет оснований для этого быть асинхронным, поскольку это просто не имеет смысла. http://stackoverflow.com/a/38956850/2410379 –

+0

@DavidPine Но что мы должны делать, когда нам нужно использовать API, который доступен только как «async Task »? Стивен Четко избил нас, что мы никогда не должны называть «GetAwaiter()» или «.Result» из-за риска взаимоблокировок - так как ASP может быть.NET Application 'Global' 'Application_Start' безопасно вызывает метод async? – Dai

ответ

3

Эти методы, как Session_Start в Global.asax являются специальными. Вы не можете просто произвольно определить новые. Рамка запускает ту, которую она запрограммирована для запуска, и не предоставляются асинхронные версии. В результате версия async никогда не будет запущена.

Однако в любом случае не имеет смысла быть асинхронным. Методы Global.asax вызываются при запуске и завершении пула приложений. В результате нет смысла отказываться от рабочего потока в любой момент, потому что ничего не может произойти до тех пор, пока он не завершит свою работу в любом случае.

Я не совсем уверен, что вы делаете, но на основе комментария в вашем коде это звучит не так, как будто это подходящее место для этого. Опять же, этот код работает только один раз, а не за запрос. Если вы хотите, чтобы что-то произошло по запросу, посмотрите на что-то вроде фильтра действий.

+0

Как вы отметили, цель состоит в том, чтобы обновлять посетителей сайта в db каждый раз, когда запускается новый сеанс (выполняется синхронизация, как я теперь понимаю). Но почему это не Session_Start для этого места? – Danielle

+0

Поскольку Global.asax запускается только при запуске или завершении пула приложений. Тем временем любое количество запросов для любого числа разных пользователей происходит. Если вы добавили какую-то логику отслеживания посетителей в Session_Start, это произойдет только один раз, когда пул приложений начнется, и пул приложений, возможно, не будет возобновлен в течение нескольких часов, дней или даже недель, в зависимости от того, как он настроен и состояние сервера. Очевидно, это не даст вам очень актуальной информации. –

+0

Вы ссылаетесь на 'Application_Start'? Событие 'Session_Start' запускается каждый раз, когда создается новый сеанс, как указано в: [http://forums.asp.net/t/1230163.aspx?What+s+the+difference+between+Application_Start+and+Session_Start + in + Global + aspx +] (http://forums.asp.net/t/1230163.aspx?What+s+the+difference+between+Application_Start+and+Session_Start+in+Global+aspx+) – Danielle

4

Из того, что я понимаю, ASP имеет обозначенную нить, которая является единственной нитью, которая имеет доступ к HttpContext.Current объекта, и, в свою очередь, доступ к сессии (HttpContext.Current.Session) так же, как в потоке пользовательского интерфейса в окнах приложений. Поэтому выполнение .Wait() или .Result в обратном вызове Session_Start даст вам неизвестные результаты и/или затормозит процесс.

Там, кажется, много способов, чтобы управлять поток Task по исполнению, основным из которых является указанием вашей задачи для работы с определенным контекстом синхронизации через класс TaskScheduler https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx#Sync

Однако, поскольку класс задач был разработан для реализуйте IAsyncResult, который исходит из шаблона APM (асинхронной модели программирования), что делает задачу обратной совместимость со старым кодом шаблона APM (из чего я понял, ASP первоначально был построен). Требуется немного интеграции, хотя https://blogs.msdn.microsoft.com/mazhou/2011/10/04/the-asynchronous-programming-models/ (стандартный APM).

.Net 4.5 был создан для обеспечения асинхронных действий с использованием APM-стиля HttpApplication. Он удовлетворяет всем требованиям для доступа к объекту Session и правильно выполняется в HttpApplication:

public override void Init() 
{ 
    base.Init(); 
    //EventHandlerTaskAsyncHelper Wraps the task call in an APM-style BeginEventHandler, EndEventHandler 
    var wrapper = new EventHandlerTaskAsyncHelper(AsyncSessionStart); 
    this.AddOnAcquireRequestStateAsync(wrapper.BeginEventHandler, wrapper.EndEventHandler); 
} 

private async Task AsyncSessionStart(Object sender, EventArgs evtArgs) 
{  
    //The only caveat is we have to check IsNewSession to see if it was created in this call 
    //This doesn't need to be applied for other AddOn*Async wire-ups 
    if (!Session.IsNewSession) 
     return; 

    await doSomethingAsync(); 
} 

//I recall seeing something that for session state to be active, this callback has to be declared, even if empty 
protected void Session_Start(object sender, EventArgs e) 
{ 
    //Synchronous session 
} 
+1

FYI: пустой метод Session_OnStart необходим для страниц с сеансами ReadOnly, потому что SessionStateModule проверяет, установлен ли обработчик синхронизации Session_OnStart, чтобы принудительно сохранить состояние сеанса даже в сеансах только для чтения: http://referencesource.microsoft.com/#System.Web/State/ SessionStateModule.cs, 1292 –