I would like to cache some entities from database to local memory. Now i could use only fetching item by key, like this:

var state = new DisconnectedState();
...
///populate cache from database
...

using (var s = Session.Open(domain))
            {
                using (state.Attach(s))
                {
                    var item = Query.Single(key);
                }
            }

but when I try using Query.All instead of Query.Single - Exception {"Connection is required."} occurs. I think there should be simple way to get all items from cache without keys.


Updated at 25.03.2010 14:24:29

Another scenario for using disconnected state with Query.All<>: 1. Maybe change naming to avoid conflits for QueryFromCache 2. I would like to create a few simple objects that part of single business object in Transaction-like style. parts of creating: user click causes using (DisconnectedState.Attach(session)){ new SimpleObject(Guid){prop = value} }

after a few operations user may look at records he added in grid, with QueryFromCache.All<simpleobject>.Select()

after finishing creation and "commiting" transaction - should merge disconnected state into database.

This thread was imported from our support forum. The original discussion may contain more detailed answer.

asked Mar 25 '10 at 13:54

pil0t's gravatar image

pil0t
207575763


One Answer:

Alex (Xtensive) wrote:

DisconnectedState itself currently doesn't allow to enumerate all the entities there, and thus there is no way to do this (but we're seriously thinking about adding this feature).

On the other hand, the data it contains is usually "pushed" into it by a set of queries performed while it was connected. So actually the simplest way show the results of some query is to:

  • Perform it on server with DisconnectedState attached to the Session

  • Serialize both result and DisconnectedState // a bit tricky: you must use SerializationContext to ensure Entities are serialized as references

  • Deserialize DisconnectedState, attach it to some Session, and then deserialize the result on the client // a bit tricky again

  • And expose the result in UI "as is".

Obviously, all this template stuff must be hidden into a single helper type handling all the dirty job. And this precisely describes serialization behavior of a sample we're working on now. Please wait for a day or so - it is almost there (we can't show half-finished thing: we fear people will start to use it almost immediately). It's easier to show the approach then to talk about it (actually, I described it for may be 4 or 5 times during last few days in Skype/email talks...).


Alex (Xtensive) wrote:

One more hint (actually, a workaround you can use now):

var all = 
  from pair in disconnectedState.Versions
  select Query.Single(pair.Key);

var animals =
  from entity in all
  let animal = entity as Animal
  where animal!=null
  select animal;

var fourLeggedAnimals =
  from animal in animals
  where animal.LegCount==4
  select animal;

Obviously, all the queries there are regular LINQ to Object queries. But in case with DisconnectedState this must be fully acceptable: it is designed to host the state of ~ 10 ... 100K of entities, but not millions. If more entities must be cached by a client, likely, you need client-side storage + sync (we'll cover this case in future as well; currently you can implement this relying only on your own forces).


xumix wrote:

DisconnectedState itself currently doesn't allow to enumerate all the entities there, and thus there is no way to do this (but we're seriously thinking about adding this feature). On the other hand, the data it contains is usually "pushed" into it by a set of queries performed while it was connected. So actually the simplest way show the results of some query is to: - Perform it on server with DisconnectedState attached to the Session

public void DsTest()
        {
            var state = new DisconnectedState();
            List<FilterEntity> suppliers = null;
            List<SampleEntity> products = null;

            using (var session = Session.Open(domain))
            {
                using (state.Attach(session))
                {
                    using (var transactionScope = Transaction.Open())
                    {
                        using (state.Connect())
                        {
                            suppliers = Query.All<FilterEntity>().Prefetch(s => s.Sample).ToList();
                            products = Query.All<SampleEntity>().Prefetch(p => p.LinkedItem).ToList();
                        }

                        var ss = Query.All<FilterEntity>().Where(f => f.NullableGuid.HasValue).Single(); // Exception {"Connection is required."}

                        transactionScope.Complete();
                    }
                }
            }
        }

So, this IS the problem, it makes DS almost unusable in our case.


Alex (Xtensive) wrote:

DisconnectedState handles pretty isolated task: it serves as fully closed state container. It isn't intended to be enumerated and queried - i.e. the associations like "it is a part of the database similar to the database itself" are wrong. It is cache with closed structure, which, being attached to Session, makes it to expose its state.

I understand why you want to enumerate it, but it's nearly the same as trying to enumerate e.g. ASP.NET cache to find the data you need. To handle this case, you must use a type wrapping both DisconnectedState and all the stuff you'd like to enumerate there, and WCF sample shows how to implement this. There is DisconnectedResult<t> type combining two parts you need together:

  • DisconnectedState

  • A graph of objects, which may contain references to persistent objects. E.g. there can be new CustomerFormData { Customers = List<customer>(...), Orders = new List<order>(...), ... }.

When DisconnectedResult<t>.GetValue() method is called, the following happens:

  • DisconnectedState is deserialized and either attached to the current Session, or merged into already attached one there

  • Graph is deserialized; since DisconnectedState is already there, DO won't hit the database while materializing any entities there; moreover, you'll see these entities in the same state as when DisconnectedResult was created.

We use classes like CustomerListResult : DisconnectedResult<list<customer>> in this sample because default WCF serializer doesn't support generic types in contracts. It seems it can deserialize generic instances only as proxies, that won't work here, because proxy doesn't have necessary logic - i.e. GetValue() method.


Alex (Xtensive) wrote:

So to summarize this:

  • DisconnectedState solves a part of the problem related to the state of entities you'd like to deliver to remote party. Additionally, it gathers the changes made to remote entities.

  • Another part - the list(s) of entities or something else combining them to the logical structure you need @ remote party - must be handled by some other code. We use DisconnectedResult<t> for this purpose in WCF sample, but this isn't the only way you have (btw, there are just about 20 lines of code).

answered Mar 25 '10 at 15:47

Editor's gravatar image

Editor
46156156157

Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA