2014-04-03 167 views
1

我有Web API方法調用另一個標有異步的方法來更新數據庫(使用EF 6)。我不需要等待數據庫方法來完成(它的火和遺忘),因此我調用這個異步方法時不使用await。如果我不打電話給await,則會拋出一個永遠不會傳遞給我的代碼的NullReferenceException,並且只是在VS2013的輸出窗口中顯示爲第一次機會異常。Web API異步異步方法

什麼是正確的方法來處理調用異步方法沒有await -ing?

下面是一個例子我在做什麼:

數據回購方法:

public async Task UpdateSessionCheckAsync(int sessionId, DateTime time) 
    { 

     using (MyEntities context = new MyEntities()) 
     { 
      var session = await context.Sessions.FindAsync(sessionId); 

      if (session != null) 
       session.LastCheck = time; 

      await context.SaveChangesAsync(); 
     } 

    } 

網絡API庫方法:

public async Task<ISession> GetSessionInfoAsync(int accountId, int siteId, int visitorId, int sessionId) 
    { 
     //some type of validation 

     var session =await GetValidSession(accountId, siteId, visitorId, sessionId); 

     DataAdapter.UpdateSessionCheckAsync(sessionId, DateTime.UtcNow); // this is the line causing the exception if not awaited 

     return new Session 
     { 
      Id = sessionId, 
      VisitorId = session.VisitorId 
     }; 

    } 

的Web API方法:

[ResponseType(typeof(Session))] 
    public async Task<IHttpActionResult> GetSessionInfo(HttpRequestMessage request, int accountId, int siteId, int visitorId, int sessionId) 
    { 
      var info = await _repository.GetSessionInfoAsync(accountId, siteId, visitorId, sessionId); 

      return Ok(info); 
    } 

最後堆棧跟蹤我得到:

System.Web.dll!System.Web.ThreadContext.AssociateWithCurrentThread(bool setImpersonationContext) 
System.Web.dll!System.Web.HttpApplication.OnThreadEnterPrivate(bool setImpersonationContext) 
System.Web.dll!System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter() 
System.Web.dll!System.Web.Util.SynchronizationHelper.SafeWrapCallback(System.Action action) 
System.Web.dll!System.Web.Util.SynchronizationHelper.QueueAsynchronous.AnonymousMethod__7(System.Threading.Tasks.Task _) 
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke() 
mscorlib.dll!System.Threading.Tasks.Task.Execute() 
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) 
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) 
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) 
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) 
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) 
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() 
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 

編輯: 香港專業教育學院改變了我的分貝方法返回的任務,而不是無效,然後用Task.Factory啓動一個新的任務調用DB方法和異常消失了。

public async Task<ISession> GetSessionInfoAsync(int accountId, int siteId, int visitorId, int sessionId) 
    { 
     //some type of validation 

     var session =await GetValidSession(accountId, siteId, visitorId, sessionId); 

     Task.Factory.StartNew(() => DataAdapter.UpdateSessionCheckAsync(sessionId, DateTime.UtcNow)); 

     return new Session 
     { 
      Id = sessionId, 
      VisitorId = session.VisitorId 
     }; 

    } 
+0

請顯示一些代碼,包括例外的詳細信息。 –

+0

@JonSkeet我已更新原始帖子。 –

+2

[Fire and forget async method in asp.net mvc]可能的重複(http://stackoverflow.com/questions/18502745/fire-and-forget-async-method-in-asp-net-mvc) –

回答

5

我並不需要等待的DB方法來完成(其發射後不管)

我一直做的第一件事,當我看到這是問:你是絕對的把握你想這樣做?

請記住,ASP.NET上的「火與遺忘」實際上與「我不在乎這些代碼是否被實際執行過」相同。由於你只是更新了「最後一次檢查」時間,所以你可能會想到「即發即棄」請注意,這意味着您的上次檢查時間可能確實或可能不正確。

這就是說,你可以用ASP.NET運行時as I describe on my blog工作登記:

public async Task<ISession> GetSessionInfoAsync(int accountId, int siteId, int visitorId, int sessionId) 
{ 
    //some type of validation 
    var session = await GetValidSession(accountId, siteId, visitorId, sessionId); 
    BackgroundTaskManager.Run(() => DataAdapter.UpdateSessionCheckAsync(sessionId, DateTime.UtcNow)); 

    return new Session 
    { 
    Id = sessionId, 
    VisitorId = session.VisitorId 
    }; 
} 

請注意,這是假設UpdateSessionCheckAsync不會返回一個Task

特別是:

  • async void方法有很尷尬的錯誤處理語義。如果寫入數據庫時​​出現問題,默認情況下,async void方法會使您的應用程序崩潰。
  • Task.Factory.StartNew is rather dangerous,正如我在博客上解釋的那樣。
+0

謝謝斯蒂芬。那些博客文章正是我所需要的。 –

+0

您的博客無法正常工作 –

+0

@Vlado:鏈接對我來說工作得很好。你的意思是我的博客上的代碼不起作用? –