RavenDB Catch 22 - Optimistic Concurrency AND Seeing Changes from Other Clients

自古美人都是妖i 提交于 2019-12-11 03:39:30

问题


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()?


回答1:


You won't see the changes, because you use the same session. See my others replies for more details




回答2:


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:

public IEnumerable<CustomVariableGroup> GetAll()
{
    using (IDocumentSession session = Database.OpenSession())
    {
        IEnumerable<CustomVariableGroup> groups = session.Query<CustomVariableGroup>();
        CacheEtags(groups, session);
        return groups;
    }
}

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.

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

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.




回答3:


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?



来源:https://stackoverflow.com/questions/8675958/ravendb-catch-22-optimistic-concurrency-and-seeing-changes-from-other-clients

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!