I don’t know how deep you’ve gone with entity framework and if you’ve given any thought on advance enterprise issues, like caching for example, yet. Diving in it though and trying to use it in an enterprise wise project gave me the opportunity to start thinking on these kind of issues. So how can you cache using EF? I won’t go into much detail about the type of Cache as this is not what this post is about so I’m just going to assume that Cache is some kind of memory based storage that I’m planning to use. You can see what a graphical representation of what I want in the following short video.
Generally what anyone would expect as caching support from EF is the ability to put EntityObjects in some kind of Cache and retrieve them next time you need them, instead of going back to the DB and get them every time. Well you can actually go ahead and do just that. Of course, you’ll first have to detach the EntityObject, from its current context to avoid an exception when you use it with a different one, and then put it on your Cache. Note that you’re actually caching a reference to the object and not the object it self.
Let’s assume now that you want to get back the object you cached and use it in another context that you’ve created. What you normally would do is take back the reference you’ve cached and attach the referenced object to the newly created context and work with it normally. Can you see the problem with this scenario? …
The keyword of this problem is “Reference”. Since you’re storing references to your cache, multiple threads will have access to the same EntityObject right? So attaching the referenced object to a thread’s Context may very well cause another to crash (Exception is thrown when you try to re-attach an EntityObject that’s already been attached to another context) as the object will be already attached.
You might argue that that’s not really a problem cause you could design a smart, thread safe Cache, that locked on the EntityObject reference before returning it, thus forbidding all other threads from accessing it for as long as it’s in use. Well, the problem with this solution is that if a thread gets an EntityObject reference from the Cache and doesn’t release it soon, then it will block all other threads that need the same object basically taking away all of the advantages of actually having a Cache.
You could resolve the locking problem by making copies of the referenced by the Cache objects and return those prior to attaching them to some context, then there’s no need for locking, as each context will have its own version of the EntityObject. But lets examine what copy actually means. Since there’s no member wise clone method available for EntityObjects, you’d have to make the copies yourself by using reflection or some other technique. You would of course, have to copy not only the primitive properties of the object but the Navigational (Relationship) ones as well by going into all the child objects and copying those too and so forth till you’ve exhausted all the object graph. Argghhh… this is a road I wouldn’t take…
There’s though a work around, that some have found convenient, in order to make copies without going into the trouble of doing a member wise clone. Since EntityObjects are serializable, they serialize the whole object graph, then copy the stream and de-serialize it back, to construct a copy of it. Although this is better than member wise cloning the objects, performance wise it’s very slow and surely unacceptable for a large Enterprise applications.
In my opinion the optimal solution is to store and return references of objects in Cache and avoid copies as much as possible. As for the the EntityObject status issues (Unchanged, detached,…etc.) that were discussed earlier you will just have to take two decisions and stick with those:
1. All objects in the Cache are read only. This means that you’ll never attach them again to a context (except for updating them in which case read the second decision) but instead always use them as detached.
2. When you need to modify a cached object you’ll have to lock on the reference so that other threads will wait till the modification is finished. This lock though is different from the one discussed earlier and something that you would do in any case since you wouldn’t want multiple threads modifying the same resource.
Although this seams to do the trick, it certainly breaks the programming model of EF. So you won’t be able to do Object1.Children and get the child related objects of the Object1 Entity object if they’re cached. You’ll instead have to fetch them from the cache and use them as detached.
Things are slightly better as far as windows forms applications are concerned, as the DataContext can be kept alive (don’t worry about the db connection, this is closed and opened as needed) for as long as the application running, playing the role of the Cache by keeping all instantiated objects alive. Unfortunately DataContext is not Tread Safe thus ruling this solution out from using it like that in a Web application.