使用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
,那么我遇到了相反的问题。因为我有一个新的会话,我可以看到乔的变化。 Buuuuuuut ...然后我失去了乐观的并发性。在保存之前创建新会话时,此行会生成空GUID,因此会失败:
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
我错过了什么?如果不应在每个方法中创建会话,也不在应用级别创建会话,那么正确的范围是什么?如何在乐观并发和中获得在执行Session.Query()时看到其他人更改的能力?
答案 0 :(得分:2)
您将看不到更改,因为您使用相同的会话。有关更多详细信息,请参阅我的其他人回复
答案 1 :(得分:2)
免责声明:我知道这不是长期的方法,因此在这里不会是一个公认的答案。但是,我现在只需要一些工作,我可以稍后重构。我也知道有些人会对这种方法感到厌恶,哈哈,但也是如此。它似乎工作。我在每个查询(新会话)中都获得了新数据,并且我也获得了乐观的并发性。
底线是每个数据访问方法我回到一个会话。每当数据访问方法执行某种类型的get / load / query时,我都会将eTags存储在静态字典中:
public IEnumerable<CustomVariableGroup> GetAll()
{
using (IDocumentSession session = Database.OpenSession())
{
IEnumerable<CustomVariableGroup> groups = session.Query<CustomVariableGroup>();
CacheEtags(groups, session);
return groups;
}
}
然后,当我保存数据时,我从缓存中获取eTag。如果另一个实例修改了数据,这会导致并发异常,这就是我想要的。
public void Save(EntityBase objectToSave)
{
if (objectToSave == null) { throw new ArgumentNullException("objectToSave"); }
Guid eTag = Guid.Empty;
if (objectToSave.Id != null)
{
eTag = RetrieveEtagFromCache(objectToSave);
}
using (IDocumentSession session = Database.OpenSession())
{
session.Advanced.UseOptimisticConcurrency = true;
session.Store(objectToSave, eTag);
session.SaveChanges();
CacheEtag(objectToSave, session); // We have a new eTag after saving.
}
}
从长远来看,我绝对想以正确的方式做到这一点,但我不知道那是什么方式。
编辑:在我找到更好的方法之前,我会将此作为接受的答案。
答案 2 :(得分:1)
Bob,为什么不在每次要刷新数据时都打开一个新的会话?
为每个请求打开新会话有很多权衡,而你对乐观并发的解决方案(在你自己的单例字典中管理标签)表明它从未打算以这种方式使用。
你说你有一个WPF应用程序。好的,在启动时打开一个新的Session。加载并查询您想要的任何内容,但在您想要刷新数据之前不要关闭会话(例如订单列表,客户,我不知道......)。然后,当您想要刷新它(在用户单击按钮,触发计时器事件或其他事件之后)处置会话并打开一个新会话。这对你有用吗?