2013-02-27 3 views
6

Я работаю над веб-приложением ASP.NET MVC 4. Я использую .NET 4.5 и пытаюсь использовать новые асинхронные API.Возврат перед async Завершение задачи

У меня есть пара ситуаций, когда я хочу запланировать задачу async для запуска позже, когда я сразу же верну сразу сразу важное значение. Например, вот метод «Логин», который я хочу, чтобы вернуть новый SessionID как можно быстрее, но как только я вернулся SessionID Я хочу, чтобы очистить старый истекли SessionID'S:

public async Task<Guid> LogIn(string UserName, string Password) 
{ 
    //Asynchronously get ClientID from DB using UserName and Password 

    Session NewSession = new Session() 
    { 
     ClientID = ClientID, 
     TimeStamp = DateTime.Now 
    }; 
    DB.Sessions.Add(NewSession); 
    await DB.SaveChangesAsync(); //NewSession.ID is autopopulated by DB 

    CleanSessions(ClientID); //Async method which I want to execute later 

    return NewSession.ID; 
} 

private async void CleanSessions(int ClientID) 
{ 
    //Asynchronously get expired sessions from DB based on ClientID and mark them for removal 
    await DB.SaveChangesAsync(); 
} 

Я попробовал кучу разных вещей, включая комбинации Task.Run() и Parallel.Invoke(), но CleanSessions никогда не вызывается. Как достичь планирования фоновых задач?

+0

Исходя из этого, 'CleanSessions' должен начинаться * при завершении' SaveChangesAsync'. Возможно, это может быть связано с задачами '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' за то, что я вижу там –

+0

Примечание: вы не должны * использовать 'async void' для своих методов, кроме обработчиков событий верхнего уровня. –

+0

@MarcGravell: ASP.NET будет ждать завершения всех методов async void перед отправкой ответа. Op хочет пожара и забыть. –

ответ

12

Выполнение задач в ASP.NET без запроса не рекомендуется. It's dangerous.

Тем не менее, изменить CleanSessions вернуть Task и вы можете сделать это следующим образом:

Task.Run(() => CleanSessions()); 

В вашем случае, я думаю, было бы хорошо, потому что нет долгосрочной проблемы, если CleanSessions Безразлично» t выполняется или заканчивается в середине выполнения (что может произойти в ASP.NET из-за повторной переработки). Если вы хотите, чтобы уведомить ASP.NET, что у вас есть некоторые работы в прогресс, который не связан с запросом, вы можете использовать BackgroundTaskManager from my blog так:

BackgroundTaskManager.Run(() => CleanSessions()); 
+1

Это именно то, что я искал, спасибо. Я на самом деле уже пробовал это, но в моем методе CleanSessions() возникла ошибка, которая вызывала исключение, и поскольку метод выполняется без запроса, исключение не было обнаружено. Это создавало у меня впечатление, что метод вообще не вызвал! Итак, обратите внимание на любого, кто хочет использовать этот трюк, не забудьте прочитать ссылки в этом ответе и убедиться, что вы действительно понимаете, почему это «опасно». – user1084447

+0

Как насчет 4 лет спустя? Возможно, обновление? Ваш блог не обновляется с 2014 года, какие-либо новые идеи? – CularBytes

+0

@CularBytes: Нет; ASP.NET по-прежнему работает одинаково, и этот ответ по-прежнему остается в силе. 'BackgroundTaskManager' не будет работать на ядре ASP.NET, но вы можете сделать то же самое, используя' IHostedService' или используя 'IApplicationLifetime' вместо' IRegisteredObject'. –

0

Если, как уже было отмечено, вы пытаетесь сделать что-то «огонь и забыть», но внутри ASP.NET - тогда вы можете посмотреть на ConfigureAwait; вы можете использовать это как часть await, чтобы сообщить об этом, игнорируя синхронный контекст. Контекст синхронизации связывает его с конвейером выполнения ASP.NET, но если это не обязательно, вы можете его избежать. Например:

public async Task<Guid> LogIn(string UserName, string Password) 
{ 
    //Asynchronously get ClientID from DB using UserName and Password 

    Session NewSession = new Session() 
    { 
     ClientID = ClientID, 
     TimeStamp = DateTime.Now 
    }; 
    DB.Sessions.Add(NewSession); 
    await DB.SaveChangesAsync().ConfigureAwait(false); //NewSession.ID is autopopulated by DB 

    await CleanSessions(ClientID).ConfigureAwait(false); //Async method which I want to execute later 

    return NewSession.ID; 
} 
// simplified: no need for this to be full "async" 
private Task CleanSessions(int ClientID) 
{ 
    //Asynchronously get expired sessions from DB based on ClientID 
    return DB.SaveChangesAsync(); 
} 
+1

Это решение имеет лучшую обработку ошибок (используя 'await' на' CleanSessions'), но не возвращает ответ «раньше»; ответ будет отправлен после завершения «CleanSessions». –

+1

Ах, да - даже если * этот код * использовал ConfigureAwait, ответ явно не может быть отправлен, пока те, «ждут» не закончили. Поэтому, если вы представляете код * вызова *, ожидающий этой задачи, он будет находиться в контексте синхронизации ASP.NET. –

+0

Да, я попробовал, он все еще ждет CleanSessions() до его продолжения. Он не выполняет то, что я пытаюсь сделать. – user1084447

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