4

we use Azure Caching directly (and not through one of the available Entity Framework wrappers). Apparently, for distributed caching, we need to serialize the objects. Unfortunately, this causes issues with lazy-loaded DbContext-based proxies used for navigation properties.

I see we can use a custom serializer in order to map proxies to empty collections (if not loaded) or to normal objects (if loaded), but I am not sure about the implementation. One possible implementation can be based on the one used by WCF, but I am not sure Azure works the same way.

The ideal solution (and that's why I point to ProxyDataContractResolver) would be one where, when serialization happens:

  • IF the navigation property has been already loaded the data would be serialized as if it were a normal Collection,
  • and if they are not loaded, they won't be serialized (I would like lazy loading to work back after deserialization for the latter case, but it's acceptable if it doesn't).

Has anyone manually fixed that problem in an elegant way?

Thanks in advance!

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
tec-goblin
  • 196
  • 1
  • 11

2 Answers2

2

I will presume that if you are wanting to cache EF objects, you don't require lazy loading or change tracking on those entities.

I believe that both of those are enabled through object proxies that will cause serialization issues (since you don't want to serialize the proxy).

If you disable the property DbContext.Configuration.ProxyCreationEnabled then serialization of the actual object, not the proxy, should work fine. This is typically required when returning POCO objects over WCF but is likley the same for other serializations scenarios such as this.

Andy Milligan
  • 400
  • 4
  • 10
  • Actually that's the solution I want to avoid, because 1) it means I need to modify all queries to be cached to not use ProxyCreation 2) I have to do extra code to include some aforementioned relationships (that's a problem with our *** architecture, unfortunately we cannot use the Include method) 3) Having Lazy Loading active once we get back the object from cache would be a nice to have I update my original question to show this. – tec-goblin Jan 13 '13 at 11:40
  • Sorry for not having awarded the bounty - I didn't know I would lose the points completely - I thought I could award those points afterwards. Now I don't have enough ;). – tec-goblin Jan 18 '13 at 10:39
1

If you detach the EF entity from the DbContext before serializing it, that disables lazy loading, so your custom serializer won't try to serialize anything that isn't already part of the entity's graph.

Then when you get it back from the cache, if you attach it to a new (identical) DbContext, that should reenable lazy loading.

(Caveat: once you detach the entity from the context, any new queries that include that same object will create a new, attached, copy, so you will need to code with some care to avoid running into trouble with multiple potentially-different versions of the same object running around. But that said, this should let you do what you want.)

Cerebrate
  • 1,391
  • 11
  • 26
  • I don't yet mark this as THE answer in case someone knows a way we can do that just by configuring the general cache serializer. – tec-goblin Jan 14 '13 at 15:45
  • I believe that you have to detach the objects found through navigation properties individually - Detach() doesn't propagate. The easiest way to do this if you don't have a record elsewhere of which ones were queried is probably to use DbContext.ObjectStateManager.GetObjectStateEntries() to pull a list of entities of appropriate type accessed through the context, and Detach()/cache the relevant ones from there. By going in that way, you'll only get the already-enumerated ones, rather than enumerating the navigation properties by accessing them directly. – Cerebrate Jan 15 '13 at 00:12
  • This is extremely difficult. My Load method has practically zero knowledge of the queryOnCacheMiss. It does know what Type is the object returned, but not the associated entries. An object in the ObjectStateManager might be there because of another query performed during that request, so I will need to fall back to Andy's suggestion. – tec-goblin Jan 15 '13 at 14:46