RavenDB Catch 22 - 乐观并发和看到来自其他客户端的变化

时间:2011-12-30 03:48:57

标签: ravendb optimistic-concurrency

使用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()时看到其他人更改的能力?

3 个答案:

答案 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。加载并查询您想要的任何内容,但在您想要刷新数据之前不要关闭会话(例如订单列表,客户,我不知道......)。然后,当您想要刷新它(在用户单击按钮,触发计时器事件或其他事件之后)处置会话并打开一个新会话。这对你有用吗?