1

我在 Nhibernate 3 中遇到了 Futures 的问题,无法意识到出了什么问题。

以下代码(没有期货)按预期工作:

SessionHandler.DoInTransaction(transaction =>
            {
                var criteria = SessionHandler.Session.CreateCriteria<T>();
                var clonedCriteria = (ICriteria)criteria.Clone();

                var count = criteria
                    .SetProjection(Projections.RowCount())
                    .UniqueResult<Int32>();


                var result = clonedCriteria
                    .SetMaxResults(PageSize)
                    .SetFirstResult(page * PageSize)
                    .List<T>();

                ItemList = result;
                TotalResults = count;
                RecalculatePageCount();
            });

SessionHandler 只是为这个上下文存储了一个 Session ,而 DoInTransaction 是一个方便的方法:

public void DoInTransaction(Action<ITransaction> action)
    {
        using (var transaction = Session.BeginTransaction())
        {

            action(transaction);
            transaction.Commit();
        }
    }

现在,以下代码会导致 GenericAdoException:

    SessionHandler.DoInTransaction(transaction =>
            {
                var criteria = SessionHandler.Session.CreateCriteria<T>();
                var clonedCriteria = (ICriteria)criteria.Clone();

                var count = criteria
                    .SetProjection(Projections.RowCount())
                    .FutureValue<Int32>();


                var result = clonedCriteria
                    .SetMaxResults(PageSize)
                    .SetFirstResult(page * PageSize)
                    .Future<T>();

                ItemList = result;
                TotalResults = count.Value;
                RecalculatePageCount();
            });

我正在使用 PostgreSQL 9.2、Npgsql 2.0.11.0 和 NHibernate 3.3.1.4000。如果这很重要,我使用 Fluent NHibernate 进行映射谢谢您的任何建议。

编辑:经过更多研究,我发现此错误仅在添加项目后发生。在开始时,我在我的表单中加载数据,它工作得很好。添加项目后在表单中重新加载数据时出现异常。但这很奇怪。该项目已正确添加。添加或更新项目的代码如下所示:

if (IsEditing)
                {
                    SessionHandler.DoInTransaction(tx => SessionHandler.Session.Update(CurrentItem));
                }
                else
                {
                    SessionHandler.DoInTransaction(tx => SessionHandler.Session.Save(CurrentItem));
                }

奇怪的是,当我引发 PropertyChanged 事件时,我(有时,我认为)得到了这个异常。我注意到有时 InnerException 是不同的。听起来像一个线程问题,但奇怪的是它没有期货。但我没有使用线程来加载数据,只是为了添加项目(嗯,但也许,因为我在添加项目时通知,并且我加载项目以响应“该消息”,我认为加载将是在另一个线程中执行)

编辑2:错误似乎很随机。有时我明白,有时没有:S

4

1 回答 1

0

我想我找到了问题所在。

这听起来可能很愚蠢,但我认为这是有道理的。我不得不交换这些行:

ItemList = result;
TotalResults = count.Value;

所以他们结果

TotalResults = count.Value;
ItemList = result;

基本上,问题是多线程(我想我在我的问题中没有提到它,但错误的随机性有点可疑)。所以首先,我会告诉你一些背景,这样解决方案就更清楚了:

当一个新元素添加到数据库中时,会(全局)发送一条消息,因此每个“感兴趣”的人都可以更新其元素以反映更改。当我使用 MVVM Light 时,我会这样做:

Messenger.Default.Send(new DataReloadSuggested<MyEntityType>(theUpdatedId));

我正在使用 Tasks添加元素,因此当我单击“添加”按钮时,执行了以下操作:

Task.Factory.StartNew(CommitCurrentItem);

并且,在 CommitCurrentItem 中,我将项目添加到数据库并通知程序列表已更新(如上所述发送消息)。

我的主要表单注册到该消息,如下所示:

Messenger.Default.Register<DataReloadSuggested<T>>(this, true, unused => LoadPage(CurrentPage));

即使 LoadPage 函数(当时)没有手动创建不同的线程,它也是在与 CommitCurrentItem 相同的线程中执行的。任务不能保证在不同的线程中运行,但在这种情况下它是。LoadPage 只是调用了问题中的代码。我的代码保证会在 UI 线程中引发 PropertyChanged 事件(来自 INotifyPropertyChanged 接口),因此当我设置 ItemList 属性(类型为 IEnumerable)时,UI 会收到通知,因此它会显示新列表。因此,UI 检索了 ItemList 的新值,而(在另一个线程中),该行TotalResults = count.Value;正在执行。在这种情况下,我猜在我检索到第一个值(列表中的第一项或 RowCount)之前,不会对数据库执行查询。请记住,ISession 不是线程安全的,因此这种情况是不可靠的:UI 线程和另一个线程同时使用同一个会话。在我的代码中,我没有在 ViewModel 之间共享会话,并且每个 ViewModel 同时使用只有一个线程的 Session,以防止这种情况。

所以,最终的解决方案是在我正在工作的同一个线程中强制执行查询,所以我只是count.Value在设置ItemListresult.

于 2012-12-23T23:17:39.893 回答