2

使用 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();
    }
}

如果另一个用户更改了该对象,则保存将正确失败。

但是,当在应用程序的整个生命周期中使用一个会话时,我不能做的是看到应用程序的其他实例(例如,五个隔间之外的乔)对文档所做的更改。当我这样做时,我看不到乔的变化:

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 不应该在每个方法中创建,也不应该在应用程序级别创建,那么正确的范围是什么?在执行 Session.Query() 时,如何获得乐观并发的好处以及查看他人更改的能力?

4

3 回答 3

2

您不会看到更改,因为您使用的是同一个会话。查看我其他人的回复了解更多详情

于 2011-12-30T09:22:06.627 回答
2

免责声明:我知道这不是长期的方法,因此在这里不会被接受。但是,我现在只需要让一些东西工作,我可以稍后重构。我也知道有些人会对这种方法感到厌恶,哈哈,但就这样吧。它似乎正在工作。我每次查询(新会话)都会获得新数据,并且我也可以使用乐观并发。

底线是我回到每个数据访问方法的一个会话。每当数据访问方法执行某种类型的获取/加载/查询时,我都会将 eTag 存储在静态字典中:

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.
    }
}

从长远来看,我绝对希望以正确的方式做到这一点,但我还不知道那是什么。

编辑:在找到更好的方法之前,我将把它作为公认的答案。

于 2011-12-30T18:53:27.907 回答
1

Bob,为什么不每次要刷新数据时都打开一个新会话?

为每个请求打开新会话需要权衡取舍,而您对乐观并发的解决方案(在您自己的单例字典中管理标签)表明它从未打算以这种方式使用。

你说你有一个 WPF 应用程序。好的,在启动时打开一个新的 Session。加载和查询您想要的任何内容,但在您想要刷新数据之前不要关闭会话(例如订单列表、客户列表,我不知道......)。然后,当您想要刷新它时(在用户单击按钮后,触发计时器事件或其他)释放会话并打开一个新会话。那对你有用吗?

于 2012-01-04T01:29:24.077 回答