我最近遇到了一个实例,我想从我在Web应用程序中定期运行的任务中查找数据库。我重构了代码以使用ThreadStaticSessionContext,这样我就可以在没有HttpContext的情况下获得会话。这适用于读取,但是当我尝试从任务刷新更新时,我得到“索引超出范围。必须是非负数且小于集合的大小。”错误。通常我看到的这个错误与在映射中使用两次列名有关,但这似乎不是问题,因为如果会话与请求相关联,我可以更新该表(我看了,我没有看到任何重复)。只有在Task尝试刷新时才会出现异常。
有谁知道为什么它会在请求中正常工作,而不是来自任务的调用?
可能是因为Task是异步的吗?
调用堆栈:
at System.ThrowHelper.ThrowArgumentOutOfRangeException() at System.Collections.Generic.List`1.System.Collections.IList.get_Item(Int32 index) at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) at NHibernate.Engine.ActionQueue.ExecuteActions() at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) at NHibernate.Impl.SessionImpl.Flush()
会话生成:
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 :从我所看到的来源,它正在对IList进行foreach循环。有关foreach循环中的IndexOutOfRangeException的问题倾向于提出并发问题。除非我误解了ThreadStaticSessionContext的用途,否则我仍然看不出这是一个什么问题。
编辑5 :我认为这可能是因为请求在线程之间徘徊,所以我尝试创建一个新的SessionContext,它结合了WebSessionContext和ThreadStaticSessionContext的逻辑。仍然有问题,但是......
编辑6 :这似乎与我设置的侦听器有关,这些侦听器在保存之前更新实体上的某些审核字段。如果我不运行它,则提交正确。通过一个事件而不是OnPreInsert,或者使用拦截器代替它会更好吗?
答案 0 :(得分:0)
我遇到了两个解决方案。我可以将用户缓存在内存中,避免查询,但可能有过时的数据(除此之外的任何内容都不重要)。或者,我可以打开一个临时的无状态会话,并使用它来查找有问题的用户。