基于任务/线程的RESTful API的NHibernate会话管理

本文关键字:RESTful API NHibernate 管理 会话 线程 于任务 任务 | 更新日期: 2023-09-27 18:18:17

我正在构建一组api。其中之一是身份验证API,它返回JWT令牌。我试图实现会话每行动的方法,与ActionFiltersAttribute。我的控制器是用这个属性装饰的:

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)的void情况下或Task.FromResult<T>(T),其中T是一个对象,但我想知道是否有另一个解决方案来利用并行性

基于任务/线程的RESTful API的NHibernate会话管理

似乎你正在处理的问题是源于HttpContext是空的,而在一个任务内,因此不能访问存储在上下文变量内的NHibernate会话。

您可以通过在调用任务之前获取session来解决这个问题。

在你的DataProviderImplGeneric中添加一个构造函数,这样你就可以手动传入一个。

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

在任务内部调用this之前,只需要检索会话。

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