RavenDB Catch 22 - 乐观并发并查看其他客户端的变化
使用RavenDB,在应用程序启动时创建一个IDocumentSession
(并且在应用程序关闭之前永远不会关闭它),允许我通过执行以下操作来使用乐观并发:
public class GenericData : DataAccessLayerBase, IGenericData
{
public void Save<T>(T objectToSave)
{
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
Session.Store(objectToSave, eTag);
Session.SaveChanges();
}
}
如果另一个用户更改了该对象,则保存将正确失败。
但是,当在应用程序的整个生命周期中使用一个会话时,我无法看到应用程序的其他实例(例如,距离五个隔间的乔)对文档所做的更改。当我这样做时,我看不到 Joe 的更改:
public class CustomVariableGroupData : DataAccessLayerBase, ICustomVariableGroupData
{
public IEnumerable<CustomVariableGroup> GetAll()
{
return Session.Query<CustomVariableGroup>();
}
}
注意:我也尝试过此操作,但它也没有显示 Joe 的更改:
return Session.Query<CustomVariableGroup>().Customize(x => x.WaitForNonStaleResults());
现在,如果我采取其他方式,并创建一个 IDocumentSession 在访问数据库的每个方法中,那么我就会遇到相反的问题。因为我有一个新会话,所以我可以看到 Joe 的变化。 Buuuuuuut...然后我就失去了乐观并发性。当我在保存之前创建新会话时,此行会生成一个空 GUID,因此会失败:
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
我缺少什么?如果不应在每个方法中创建会话,也不应在应用程序级别创建会话,那么正确的范围是什么?在执行 Session.Query() 时,如何才能获得乐观并发的好处以及查看其他人更改的能力?
With RavenDB, creating an IDocumentSession
upon app start-up (and never closing it until the app is closed), allows me to use optimistic concurrency by doing this:
public class GenericData : DataAccessLayerBase, IGenericData
{
public void Save<T>(T objectToSave)
{
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
Session.Store(objectToSave, eTag);
Session.SaveChanges();
}
}
If another user has changed that object, then the save will correctly fail.
But what I can't do, when using one session for the lifetime of an app, is seeing changes, made by other instances of the app (say, Joe, five cubicles away), to documents. When I do this, I don't see Joe's changes:
public class CustomVariableGroupData : DataAccessLayerBase, ICustomVariableGroupData
{
public IEnumerable<CustomVariableGroup> GetAll()
{
return Session.Query<CustomVariableGroup>();
}
}
Note: I've also tried this, but it didn't display Joe's changes either:
return Session.Query<CustomVariableGroup>().Customize(x => x.WaitForNonStaleResults());
Now, if I go the other way, and create an IDocumentSession
within every method that accesses the database, then I have the opposite problem. Because I have a new session, I can see Joe's changes. Buuuuuuut... then I lose optimistic concurrency. When I create a new session before saving, this line produces an empty GUID, and therefore fails:
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
What am I missing? If a Session shouldn't be created within each method, nor at the app level, then what is the correct scope? How can I get the benefits of optimistic concurrency and the ability to see others' changes when doing a Session.Query()?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不会看到更改,因为您使用相同的会话。更多详情请看我其他人的回复
You won't see the changes, because you use the same session. See my others replies for more details
免责声明:我知道这不是长期方法,因此不会成为此处可接受的答案。然而,我现在只需要让一些东西工作起来,然后我就可以重构。我也知道有些人会对这种方法感到厌恶,哈哈,但就这样吧。似乎正在发挥作用。我每次查询(新会话)都会获得新数据,并且乐观并发也能正常工作。
最重要的是,我回到了每种数据访问方法一个会话。每当数据访问方法执行某种类型的获取/加载/查询时,我都会将 eTag 存储在静态字典中:
然后,当我保存数据时,我会从缓存中获取 eTag。如果另一个实例修改了数据,这会导致并发异常,这正是我想要的。
从长远来看,我绝对想以正确的方式做到这一点,但我还不知道那是什么。
编辑:我将把它作为公认的答案,直到找到更好的方法。
Disclaimer: I know this can't be the long-term approach, and therefore won't be an accepted answer here. However, I simply need to get something working now, and I can refactor later. I also know some folks will be disgusted with this approach, lol, but so be it. It seems to be working. I get new data with every query (new session), and I get optimistic concurrency working as well.
The bottom line is that I went back to one session per data access method. And whenever a data access method does some type of get/load/query, I store the eTags in a static dictionary:
Then, when I'm saving data, I grab the eTag from the cache. This causes a concurrency exception if another instance has modified the data, which is what I want.
I absolutely want to do this the right way in the long run, but I don't know what that way is yet.
Edit: I'm going to make this the accepted answer until I find a better way.
Bob,为什么不在每次想要刷新数据时打开一个新会话呢?
为每个请求打开新会话需要进行许多权衡,并且您的乐观并发解决方案(在您自己的单例字典中管理标签)表明它从未打算以这种方式使用。
你说你有一个 WPF 应用程序。好的,启动时打开一个新会话。加载和查询您想要的任何内容,但在您想要刷新数据之前不要关闭会话(例如订单列表、客户,我不知道......)。然后,当您想要刷新它时(在用户单击按钮后,触发计时器事件或其他事件)处理会话并打开一个新会话。这对你有用吗?
Bob, why don't you just open up a new Session every time you want to refresh your data?
It has many trade-offs to open new sessions for every request, and your solution to optimistic concurrency (managing tags within your own singleton dictionary) shows that it was never intended to be used that way.
You said you have a WPF application. Alright, open a new Session on startup. Load and query whatever you want but don't close the Session until you want to refresh your data (e.g. a list of order, customers, i don't know...). Then, when you want to refresh it (after a user clicks on a button, a timer event is fired or whatever) dispose the session and open a new one. Does that work for you?