I'm having problems with Futures in Nhibernate 3 and can't realize what's wrong.
我在Nhibernate 3中遇到了Futures的问题,并且无法意识到什么是错的。
The following code (without Futures), works as expected:
以下代码(没有期货)按预期工作:
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 just stores a Session for this context, and DoInTransaction is a convenience method:
SessionHandler只为这个上下文存储一个Session,而DoInTransaction是一个方便的方法:
public void DoInTransaction(Action<ITransaction> action)
{
using (var transaction = Session.BeginTransaction())
{
action(transaction);
transaction.Commit();
}
}
Now, the following code causes GenericAdoException:
现在,以下代码导致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();
});
I'm using PostgreSQL 9.2, Npgsql 2.0.11.0 and NHibernate 3.3.1.4000. If that matters, I use Fluent NHibernate for my mappings Thank you for any advice.
我正在使用PostgreSQL 9.2,Npgsql 2.0.11.0和NHibernate 3.3.1.4000。如果这很重要,我使用Fluent NHibernate作为我的映射谢谢你的任何建议。
EDIT: After more research, I found that this error only occurrs after I add an item. At starting, I'm loading data in my form, and it works just fine. I get the exception when I reload the data in my form after adding an item. But it is pretty strange. The item is added correctly. The code for adding or updating items looks like this:
编辑:经过更多的研究,我发现这个错误只发生在我添加项目后。在开始时,我正在以我的形式加载数据,它工作得很好。我在添加项目后重新加载表单中的数据时收到异常。但这很奇怪。该项目已正确添加。添加或更新项目的代码如下所示:
if (IsEditing)
{
SessionHandler.DoInTransaction(tx => SessionHandler.Session.Update(CurrentItem));
}
else
{
SessionHandler.DoInTransaction(tx => SessionHandler.Session.Save(CurrentItem));
}
What is strange is that I (sometimes, I think) get this exception when I'm raising the PropertyChanged event. I noticed that sometimes the InnerException is different. Sounds like a threading problem, but it is strange that it works without futures. But I'm not using threads for loading the data, just for adding items (hmm, but maybe, because I notify when my items are added, and I load the items in answer to "that message", I think that load would be executed in another thread)
奇怪的是,当我提升PropertyChanged事件时,我(有时候,我认为)会得到这个例外。我注意到有时InnerException是不同的。听起来像是一个线程问题,但奇怪的是它没有未来的作用。但是我没有使用线程来加载数据,只是为了添加项目(嗯,但也许,因为我在添加项目时通知我,并且我加载项目以回答“那条消息”,我认为负载将是在另一个线程中执行)
EDIT 2: The error seems pretty random. Sometimes I get it, sometimes not :S
编辑2:错误似乎很随机。有时候我会得到它,有时候不会:S
1 个解决方案
#1
0
I think I've found the issue.
我想我已经找到了这个问题。
This may sound stupid, but I think it makes sense. I had to swap these lines:
这可能听起来很愚蠢,但我认为这是有道理的。我不得不交换这些线:
ItemList = result;
TotalResults = count.Value;
So they resulted
所以他们结果了
TotalResults = count.Value;
ItemList = result;
The problem was, basically, multithreading (I think I didn't mentioned it pretty much in my question, but the randomness of the errors were a bit suspicious). So first, I'll tell you some background so the solution is clearer:
问题基本上是多线程(我想我在问题中并没有提到它,但是错误的随机性有点可疑)。首先,我将告诉你一些背景,以便解决方案更清晰:
When a new element is added to the database, a message is (globally) sent, so everyone 'interested' can update its elements to reflect the changes. As I'm using MVVM Light, I do it like this:
将新元素添加到数据库时,会(全局)发送一条消息,因此每个“感兴趣的”都可以更新其元素以反映更改。当我使用MVVM Light时,我这样做:
Messenger.Default.Send(new DataReloadSuggested<MyEntityType>(theUpdatedId));
I was using Tasks to add the elements, so when I clicked on the 'Add' button, something like this was executed:
我正在使用Tasks来添加元素,所以当我点击“添加”按钮时,执行了类似的操作:
Task.Factory.StartNew(CommitCurrentItem);
And, inside CommitCurrentItem, I added the item to the database and notified the program that the list was updated (sending the message as mentioned above).
并且,在CommitCurrentItem内部,我将项目添加到数据库并通知程序列表已更新(如上所述发送消息)。
My main form registered to that messages, like this:
我的主要表单已注册到该消息,如下所示:
Messenger.Default.Register<DataReloadSuggested<T>>(this, true, unused => LoadPage(CurrentPage));
Even though the LoadPage function was not creating a different thread manually (at that moment), it was executed in the same thread as the CommitCurrentItem. A Task is not guaranteed to run in a different thread, but in this case it was. LoadPage just called the code that was in the question. My code is guaranteed to raise the PropertyChanged event (from the INotifyPropertyChanged interface) in the UI thread, so when I set the ItemList property (of type IEnumerable), the UI was notified so it shows the new list. So, the UI retrieved the new value of ItemList while (in the other thread), the line TotalResults = count.Value;
was executing. In this case, I guess the query is not executed against the database until I retrieve the first value (the first item in the list or the RowCount). Remember that ISession is not thread safe, so this situation was unreliable: the UI thread and the other thread was using the same session at the same time. In my code I'm not sharing the sessions between the ViewModels, and each ViewModel uses the Session with only one thread at the same time, in order to prevent this kind of situations.
即使LoadPage函数没有手动创建不同的线程(此时),它也在与CommitCurrentItem相同的线程中执行。不保证任务在不同的线程中运行,但在这种情况下它是。 LoadPage刚刚调用了问题中的代码。我的代码保证在UI线程中引发PropertyChanged事件(来自INotifyPropertyChanged接口),所以当我设置ItemList属性(IEnumerable类型)时,UI被通知所以它显示新列表。因此,UI检索ItemList的新值,而在另一个线程中,行TotalResults = count.Value;正在执行。在这种情况下,我想在检索第一个值(列表中的第一个项目或RowCount)之前,不会对数据库执行查询。请记住,ISession不是线程安全的,因此这种情况不可靠:UI线程和另一个线程同时使用相同的会话。在我的代码中,我不是在ViewModel之间共享会话,并且每个ViewModel同时使用Session只有一个线程,以防止这种情况。
So, the final solution was to force the execution of the query in the same thread I was working, so I simply called count.Value
before setting ItemList
to result
.
因此,最终的解决方案是在我正在工作的同一个线程中强制执行查询,所以在将ItemList设置为result之前,我只需调用count.Value。
#1
0
I think I've found the issue.
我想我已经找到了这个问题。
This may sound stupid, but I think it makes sense. I had to swap these lines:
这可能听起来很愚蠢,但我认为这是有道理的。我不得不交换这些线:
ItemList = result;
TotalResults = count.Value;
So they resulted
所以他们结果了
TotalResults = count.Value;
ItemList = result;
The problem was, basically, multithreading (I think I didn't mentioned it pretty much in my question, but the randomness of the errors were a bit suspicious). So first, I'll tell you some background so the solution is clearer:
问题基本上是多线程(我想我在问题中并没有提到它,但是错误的随机性有点可疑)。首先,我将告诉你一些背景,以便解决方案更清晰:
When a new element is added to the database, a message is (globally) sent, so everyone 'interested' can update its elements to reflect the changes. As I'm using MVVM Light, I do it like this:
将新元素添加到数据库时,会(全局)发送一条消息,因此每个“感兴趣的”都可以更新其元素以反映更改。当我使用MVVM Light时,我这样做:
Messenger.Default.Send(new DataReloadSuggested<MyEntityType>(theUpdatedId));
I was using Tasks to add the elements, so when I clicked on the 'Add' button, something like this was executed:
我正在使用Tasks来添加元素,所以当我点击“添加”按钮时,执行了类似的操作:
Task.Factory.StartNew(CommitCurrentItem);
And, inside CommitCurrentItem, I added the item to the database and notified the program that the list was updated (sending the message as mentioned above).
并且,在CommitCurrentItem内部,我将项目添加到数据库并通知程序列表已更新(如上所述发送消息)。
My main form registered to that messages, like this:
我的主要表单已注册到该消息,如下所示:
Messenger.Default.Register<DataReloadSuggested<T>>(this, true, unused => LoadPage(CurrentPage));
Even though the LoadPage function was not creating a different thread manually (at that moment), it was executed in the same thread as the CommitCurrentItem. A Task is not guaranteed to run in a different thread, but in this case it was. LoadPage just called the code that was in the question. My code is guaranteed to raise the PropertyChanged event (from the INotifyPropertyChanged interface) in the UI thread, so when I set the ItemList property (of type IEnumerable), the UI was notified so it shows the new list. So, the UI retrieved the new value of ItemList while (in the other thread), the line TotalResults = count.Value;
was executing. In this case, I guess the query is not executed against the database until I retrieve the first value (the first item in the list or the RowCount). Remember that ISession is not thread safe, so this situation was unreliable: the UI thread and the other thread was using the same session at the same time. In my code I'm not sharing the sessions between the ViewModels, and each ViewModel uses the Session with only one thread at the same time, in order to prevent this kind of situations.
即使LoadPage函数没有手动创建不同的线程(此时),它也在与CommitCurrentItem相同的线程中执行。不保证任务在不同的线程中运行,但在这种情况下它是。 LoadPage刚刚调用了问题中的代码。我的代码保证在UI线程中引发PropertyChanged事件(来自INotifyPropertyChanged接口),所以当我设置ItemList属性(IEnumerable类型)时,UI被通知所以它显示新列表。因此,UI检索ItemList的新值,而在另一个线程中,行TotalResults = count.Value;正在执行。在这种情况下,我想在检索第一个值(列表中的第一个项目或RowCount)之前,不会对数据库执行查询。请记住,ISession不是线程安全的,因此这种情况不可靠:UI线程和另一个线程同时使用相同的会话。在我的代码中,我不是在ViewModel之间共享会话,并且每个ViewModel同时使用Session只有一个线程,以防止这种情况。
So, the final solution was to force the execution of the query in the same thread I was working, so I simply called count.Value
before setting ItemList
to result
.
因此,最终的解决方案是在我正在工作的同一个线程中强制执行查询,所以在将ItemList设置为result之前,我只需调用count.Value。