We cache a lot of data in our Silverlight client. One of the main ways we accomplish this is by having an Entity Manager that has a bunch of commonly used entities. Then when somebody asks for data, we check that Entity Manager and use those entities if possible. However, in our case it is not safe to just return the same copy of the cached entities to all the callers - we need to give them copies. We do this by importing the cached entities into the EntityManager of the code asking for the data (we have one Entity Manager for each window and sometimes even for sections of the window that need isolation). Also, most of our code assumes that entities will be owned by an entity manager so it's not really safe to give back free-floating entities.
We've been doing some performance testing of our app and noticed that the ImportEntities logic is actually starting to be a bottleneck. I did a bunch of timings and it seems like a simple ImportEntities from a nearly empty entity manager into an empty entity manager will always take a certain, non-trivial amount of time. On my machine, I can import from one to twenty entities at a time and it will always take about 60 milliseconds. It's not until I start importing like 40 entities at the same time that the time starts to increase (40 takes like 66ms). If you're doing big imports, that extra 60ms is probably not a big deal....but in our case, we are sometimes doing dozens of tiny imports of just one to four entities at a time. And those 60ms start to add up.
I looked at how ImportEntities works and it seems like it makes a copy of the EntityCacheState using the DataContractSerializer to basically do a deep clone. In all my profiling, that is the logic that seems to be taking the longest time in various operations in our app. I'm not sure if there is anything that can be done. Perhaps many small imports is not really the intended use of that method? But if there could be some way to speed that up, I would very much appreciate it....
In trying to find alternate ways for quickly copying entities from one Entity Manager to another, I've come across the following solution and wanted to get your opinion. Basically, I check whether any of the entities I need to copy have changes or exist in the destination Entity Manager. In that case, it seems complicated so I just fall back to Import Entities. Luckily, in our code that rarely happens. Most of the time, we are moving unchanged entities into an empty (or mostly empty) Entity Manager. In that case, I just clone the entities and attach them. My code looks like this:
private static void QuickImport(IList<Entity> toImport, EntityManager destinationManager, MergeStrategy mergeStrategy){ //If any of the passed in entities have pending changes or exist in the destination entity manager, use the // standard (slow) Import Entities logic. Otherwise, we can just clone each entity and attach it to the destination // for better performance.
if (toImport.Any(e => e.EntityAspect.EntityState != EntityState.Unchanged || destinationManager.FindEntity(e.EntityAspect.EntityKey, includeDeleted: true) != null)) { destinationManager.ImportEntities(toImport, mergeStrategy); } else { destinationManager.AttachEntities(toImport.Select(e => ((ICloneable)e).Clone())); } } |
Does that seem like a good and safe solution? I tried to restrict it to the cases that I assume don't require any complicated import/merging logic.
For reference, here are some numbers on my timings comparing how long imports take for various numbers of entities and also the average time per entity for doing the merge. You'll notice that the ImportEntities logic takes a long time even with just a handful of entities.
Thanks!