Session NHibernate ArgumentOutOfRangeException
我最近遇到了一个实例,我想从web应用程序中定期运行的任务中访问数据库。我重构了代码以使用ThreadStaticSessionContext,这样我就可以获得一个没有HttpContext的会话。这对于读取很好,但是当我尝试刷新任务的更新时,我得到“索引超出范围。必须是非负的并且小于集合的大小。”错误。通常,我看到的这个错误与在映射中两次使用列名有关,但这似乎不是问题所在,因为如果会话与请求关联,我可以更新该表(我查看了,没有看到任何重复项)。只有当任务尝试刷新时,我才会得到异常 有人知道为什么它可以通过请求而不是任务调用正常工作吗 可能是因为任务是异步的吗 调用堆栈: 会话生成:Session NHibernate ArgumentOutOfRangeException,session,nhibernate,task,Session,Nhibernate,Task,我最近遇到了一个实例,我想从web应用程序中定期运行的任务中访问数据库。我重构了代码以使用ThreadStaticSessionContext,这样我就可以获得一个没有HttpContext的会话。这对于读取很好,但是当我尝试刷新任务的更新时,我得到“索引超出范围。必须是非负的并且小于集合的大小。”错误。通常,我看到的这个错误与在映射中两次使用列名有关,但这似乎不是问题所在,因为如果会话与请求关联,我可以更新该表(我查看了,没有看到任何重复项)。只有当任务尝试刷新时,我才会得到异常 有人知道为什
internal static ISession CurrentSession {
get {
if(HasSession) return Initializer.SessionFactory.GetCurrentSession();
ISession session = Initializer.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
return session;
}
}
private static bool HasSession {
get { return CurrentSessionContext.HasBind(Initializer.SessionFactory); }
}
要从中访问数据库的任务:
_maid = Task.Factory.StartNew(async () => {
while(true) {
if(CleaningSession != null) CleaningSession(Instance, new CleaningSessionEventArgs { Session = UnitOfWorkProvider.CurrentSession });
UnitOfWorkProvider.TransactionManager.Commit();
await Task.Delay(AppSettings.TempPollingInterval, _paycheck.Token);
}
//I know this function never returns, I'm using the cancellation token for that
// ReSharper disable once FunctionNeverReturns
}, _paycheck.Token);
_maid.GetAwaiter().OnCompleted(() => _maid.Dispose());
编辑:快速澄清上述某些类型。CleaningSession是一个触发事件,用于运行需要执行的各种操作,而_paycheck是该任务的CancellationTokenSource
编辑2:哦,是的,这是使用NHibernate版本4.0.0.4000
编辑3:此后,我尝试使用计时器执行此操作,结果相同
编辑4:从源代码中可以看到,它正在ILST上执行foreach循环。与foreach循环中的IndexOutOfRangeException相关的问题往往表明存在并发问题。除非我误解了ThreadStaticSessionContext的目的,否则我仍然不认为这是一个什么问题
编辑5:我认为这可能是因为请求在线程之间来回跳转,所以我尝试创建一个新的SessionContext,它结合了WebSessionContext和ThreadStaticSessionContext的逻辑。但还是有问题
编辑6:这似乎与我设置的监听器有关,该监听器用于在实体上的某些审核字段被保存之前更新它们。如果我不运行它,提交就会正常进行。通过事件而不是OnPreInsert来实现这一点会更好吗?还是改用拦截器呢?在得过且过之后,我准确地找到了问题所在。基本上,在我的监听器中,有一个用于加载从PreUpdate事件内部调用的当前用户记录的查询
我发现了两种解决方案。我可以将用户缓存在内存中,避免查询,但可能会有过时的数据(这里除了id之外,其他什么都不重要)。或者,我可以打开一个临时的无状态会话,并使用它来查找有问题的用户 我的问题非常相似,但帮助我认识到我正在查询数据库以获取当前用户,但我应该这样做。谢谢大家!
_maid = Task.Factory.StartNew(async () => {
while(true) {
if(CleaningSession != null) CleaningSession(Instance, new CleaningSessionEventArgs { Session = UnitOfWorkProvider.CurrentSession });
UnitOfWorkProvider.TransactionManager.Commit();
await Task.Delay(AppSettings.TempPollingInterval, _paycheck.Token);
}
//I know this function never returns, I'm using the cancellation token for that
// ReSharper disable once FunctionNeverReturns
}, _paycheck.Token);
_maid.GetAwaiter().OnCompleted(() => _maid.Dispose());