2015-10-19 79 views
0

我正在構建一組API。其中之一是一個認證API,它返回JWT令牌。我正在嘗試實施「每次操作的會話」方法,其中包含ActionFiltersAttribute。我的控制器裝飾有此屬性:帶有任務/線程的RESTful API中的NHibernate會話管理

public class NHibernateSessionAttribute : ActionFilterAttribute 
    { 
     public override void OnActionExecuting(HttpActionContext actionContext) 
     { 
      var session = NHibernateSessionManager.SessionFactory.OpenSession(); 
      session.BeginTransaction();    
      CurrentSessionContext.Bind(session); 
     } 

     public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 
     { 
      var session = CurrentSessionContext.Unbind(NHibernateSessionManager.SessionFactory)    
      if (session != null) 
      { 
       if (session.Transaction.IsActive) 
       { 
        try 
        { 
         session.Transaction.Commit(); 
        } 
        catch 
        { 
         session.Transaction.Rollback(); 
        } 
       } 

       session.Close(); 
      } 
     } 
    } 

問題在哪裏?使用NHibernate來管理用戶而不是實體框架我已經實現了所有需要的ASP.NET Identity接口,並且它們都返回Task<T>。例如以下動作:

AccountController.cs 

public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model) 
     { 
      if (!ModelState.IsValid) 
      { 
       return BadRequest(ModelState); 
      } 

      IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, 
       model.NewPassword); 

      if (!result.Succeeded) 
      { 
       return GetErrorResult(result); 
      } 

      return Ok(); 
     } 

ChangePasswordAsync內部調用多種方法至極裏面有新的任務,其中SessionFactory.GetCurrentSession()導致NullException代碼。據我所知,因爲那是另一個線程和上下文。 在代碼中,第一次保存嘗試沒有失敗,第二次沒有失敗。重複的代碼僅用於說明情況。

UserStore.cs 

public System.Threading.Tasks.Task UpdateAsync(UserModel user) 
    { 
     if (user == null) 
     { 
      throw new ArgumentNullException("user"); 
     } 
     //Here the Session is found 
     DataProviderI<UserModel, int> prov = new DataProviderImplGeneric<UserModel, int>(); 
     prov.Save(user); 


     return Task.Factory.StartNew(() => 
     { 
      //Here the Session is NOT found 
      DataProviderI<UserModel, int> prov2 = new DataProviderImplGeneric<UserModel, int>(); 
      prov.Save(user); 
     }); 
    } 

什麼對付這一點,所有的Action時得到同樣ISession的最佳方式?

據我所知,NHibernate不支持異步調用,我可以在返回類型爲Task.FromResult(0)的虛方案或Task.FromResult<T>(T)上重構方法,其中T是一個對象,但我想知道是否有另一種解決方案利用並行性

+0

看看使用DI庫,[simpleinjector](https://simpleinjector.org/index.html)是一樣的好。使用DI容器,您可以將實例的生命週期與作用域綁定在一起,在這種情況下,您希望將「ISession」綁定到Web請求。 SimpleInjector非常適合這一點,並且適用於任務和異步。 –

回答

1

看起來好像你正在處理的問題是由於HttpContext在任務內部爲空,因此無法訪問存儲在上下文變量中的NHibernate會話。

您可以通過在調用任務之前獲取ISession來解決此問題。

添加在構造函數中您DataProviderImplGeneric所以你手動傳遞一個在你從任務中調用此之前

DataProviderImplGeneric(ISession session) { 
     this.session = session; 
} 

只是檢索會話。

var session = GetCurrentNHibernateSession(); 

return Task.Factory.StartNew(() => { 
    var dataProvider = DataProviderImplGeneric<UserModel, int>(session); 
    return dataProvider.Save(user); 
}