There are several techniques to help query and find entities in cache.
The EntityManager provides several means of finding entities in its entity cache. In DevForce-speak, "find" means to perform a search of cache only without attempting to access the server or datastore.
If you know the EntityKey of an entity, you can use the FindEntity method to find the entity in the EntityManager cache. This is an efficient way both to locate the entity or to prove that the entity is not in cache.
C# | Employee emp = (Employee) Manager.FindEntity(new EntityKey(typeof (Employee), 1)); |
VB | Dim emp As Employee = CType(Manager.FindEntity(New EntityKey(GetType(Employee), 1)), Employee) |
Note that the FindEntity call returns an object, so you must cast the returned object to the entity type expected.
You can also use FindEntity to find an entity with an EntityState of Deleted. This is the only means of locating an as-yet-unsaved deleted entity.
C# | var key = new EntityKey(typeof (Employee), 1); Employee emp = (Employee) Manager.FindEntity(key), includeDeleted: true ); |
VB | Dim key = New EntityKey(GetType(Employee), 1) Dim emp As Employee = CType(Manager.FindEntity(key), Employee), includeDeleted As Employee |
You can also look for entities in cache based on their EntityState. You can combine EntityStates too, for example to look for all entities which are either Added or Modified.
FindEntities comes in both generic and non-generic forms, and will return either an IEnumerable or IEnumerable<T>. If using the non-generic overload you can still cast the results to an IEnumerable<T> in order to build a composable LINQ query.
C# | var allAdded = manager.FindEntities(EntityState.Added); |
VB | Dim allAdded = manager.FindEntities(EntityState.Added) |
C# | var empList= manager.FindEntities<Employee>(EntityState.Unchanged); |
VB | Dim empList = manager.FindEntities(Of Employee)(EntityState.Unchanged) |
C# | var addedCustomersInUK = manager.FindEntities<Customer>(EntityState.Added).Where(c => c.Country == "UK"); // .. or .. var addedCustomersInUK = manager.FindEntities(EntityState.Added).OfType<Customer>().Where(c => c.Country == "UK"); |
VB | Dim addedCustomersInUK = manager.FindEntities(Of Customer)(EntityState.Added).Where(Function(c) c.Country = "UK") ' .. or .. Dim addedCustomersInUK = manager.FindEntities(EntityState.Added).OfType(Of Customer)().Where(Function(c) c.Country = "UK") |
The FindEntityGraph method allows you to search the entity cache for an object graph (aka entity graph) - a list of entities that are navigable from one or more root entities according to a specified graph. Working with an entity graph is particularly useful when performing a save or import and you wish to work with only the object and its dependent entities.
The FindEntityGraph method takes one or more "root" entities, effectively the starting point of the graph, and one or more EntitySpans , information about the entity relationships to be followed to build the graph. The FindEntityGraph method also allows you to specify the EntityState(s) wanted.
This sounds more complicated that it really is, so here's a simple example. Here we build an entity graph of all orders and order details for a specific employee:
C# | var emp = Manager.FindEntity(new EntityKey(typeof(Employee), 1)); var span = new EntitySpan(typeof(Employee), EntityRelations.FK_Order_Employee, EntityRelations.FK_OrderDetail_Order); var entityGraph = Manager.FindEntityGraph(new[] { emp }, new[] { span }, EntityState.AllButDetached); |
VB | Dim emp = Manager.FindEntity(New EntityKey(GetType(Employee), 1)) Dim span = New EntitySpan(GetType(Employee), EntityRelations.FK_Order_Employee, _ EntityRelations.FK_OrderDetail_Order) Dim entityGraph = Manager.FindEntityGraph( { emp }, { span }, EntityState.AllButDetached) |
Here's a more complex example. The EntityManager must be able to "walk" each EntitySpan, in the case above we navigated from Employee -> Order -> OrderDetail. If a relationship can't be walked, then you must supply another EntitySpan for it. Here we also want to retrieve all Customers for the selected Orders. To do so we need to include another EntitySpan, one which walks from Employee -> Order -> Customer.
C# | var emp = Manager.FindEntity(new EntityKey(typeof(Employee), 1)); var span1 = new EntitySpan(typeof(Employee), EntityRelations.FK_Order_Employee, EntityRelations.FK_OrderDetail_Order); var span2 = new EntitySpan(typeof(Employee), EntityRelations.FK_Order_Employee, EntityRelations.FK_Order_Customer); var entityGraph = Manager.FindEntityGraph(new[] { emp }, new[] { span1, span2 }, EntityState.AllButDetached); |
VB | Dim emp = Manager.FindEntity(New EntityKey(GetType(Employee), 1)) Dim span1 = New EntitySpan(GetType(Employee), EntityRelations.FK_Order_Employee, _ EntityRelations.FK_OrderDetail_Order) Dim span2 = New EntitySpan(GetType(Employee), EntityRelations.FK_Order_Employee, _ EntityRelations.FK_Order_Customer) Dim entityGraph = Manager.FindEntityGraph( { emp }, { span1, span2 }, EntityState.AllButDetached) |
A few more things to note.
We've already seen that any query can be given a QueryStrategy to control where it's executed. When you provide a CacheOnly query strategy, either explicitly on the query, by setting the DefaultQueryStrategy on the EntityManager, or by disconnecting, the EntityManager will run the query against the local entity cache only and not attempt to communicate with the EntityServer. Since no server communication is performed, you can execute CacheOnly queries synchronously in all environments, including Silverlight and Windows Store applications.
For example, the following will work in any environment:
C# | var customers = Manager.Customers.With(QueryStrategy.CacheOnly).Execute(); |
VB | Dim customers = Manager.Customers.With(QueryStrategy.CacheOnly).Execute() |
C# | var customers = Manager.Customers.With(QueryStrategy.CacheOnly).ToList(); |
VB | Dim customers = Manager.Customers.With(QueryStrategy.CacheOnly).ToList() |
You can also issue scalar "immediate" execution queries against cache only without using AsScalarAsync . Here's a First method executed synchronously against cache:
C# | var aCustomer = Manager.Customers.With(QueryStrategy.CacheOnly).First(); |
VB | Dim aCustomer = Manager.Customers.With(QueryStrategy.CacheOnly).First() |
In async environments you'll want to take care not to issue these synchronous queries without ensuring that the query will indeed be executed against cache, since an error will be thrown otherwise.