My Battle with…
| August 11, 2011 | in
This summer we are hosting 23 interns at the Don’t Panic Labs office. These interns are placed into four separate teams, with each team tasked to develop a product based around a specific need. Alec Johnston, a member of the Planhandler team, wrote this internal blog post based on his experiences in developing a product that allows users to find and collaboratively create events that fit their interests.
While creating our backend I ran into some issues regarding Object-Relational Mapping (ORM), especially in relation to WCF serialization. Our project uses the fairly new Entity Framework (EF) Code First approach. We originally created an object with a fairly straight-forward Parent-Child relationship.
Everything ran fine and the Nodes table was created successfully (sorry if you don’t like the pluralization of database tables). Then I went to make sure everything was working properly, so I ran a quick query. This query uses some functions exposed by Entity Framework which simplify LINQ queries even further. For the record, DatabaseContext has the base class of DbContext, which is a wrapper of the old class ObjectContext that was added in EF4.1 to simplify a lot of the code surrounding it.
Much to my surprise, ID and Name loaded perfectly, but both Parent and Children were null. I double checked the database and the Node with an ID of “0” did indeed have children. I did some delving (i.e., I Googled it), and it turns out that by default, EF does not load related objects. My first reaction was that I needed to get all these objects to load, so I looked into it, and it seemed that the best way (and one of the only ways) to get these objects was with lazy loading.
I did some research and lazy loading is actually enabled by default in DbContext. But my related objects were still null.
Just because I didn’t know what else to do, I tried explicitly setting LazyLoadingEnabled to both true and false, and there was no change to my related object loading. This is because when using Plain Old CLR Objects (POCOs), lazy loading is achieved by creating instances of derived dynamic proxy types and then overriding virtual properties to add the loading hook. This means that if I don’t allow Entity Framework to override my POCOs (via the virtual keyword), dynamic proxies will not be able to be created. So I went back to my Node class and added the virtual keyword to my navigation properties, Parent and Children.
Re-running my old test resulted in this result:
Now that all of my related objects were loading, I was pretty happy. I could move on to passing this object up yet another layer (since thus far I had only moved it from the Database to the Resource Access layer). To go up another layer, though, meant using WCF and passing data between services, something that I had never dealt with before. I suppose it might be important to add here that we were using the IDesign architecture pattern, so everything is a service. So, of course, my first instinct was to pump out some quick code and see what happened.
I wrote the following function in my NodeAccessor class:
And then wrote some more code to test it. ServiceFactory here comes from the DontPanic.ServiceHelpers.dll that we used.
I ran the test and my hopes were very quickly shattered when an exception “was unhandled by user code.”
Not only was there an exception but it was possibly the most useless exception ever given to me. The inner exceptions simply alternated between this message and “The read operation failed, see inner exception.” Very helpful. I asked around the office and the first response I got was that it was probably a serialization issue. Turns out that was precisely the problem.
The first thing I did was turn proxies off using dbContext.Configuration.ProxyCreationEnabled, but that just disabled my lazy loading as well as the proxy creation, so I quickly undid that and kept searching.
Serializing Dynamic Proxies
I soon ran across some information thanks to MSDN: http://msdn.microsoft.com/en-us/library/dd456853.aspx
It turns out, for those too lazy to read the whole thing, that WCF cannot directly serialize or deserialize proxies because the DataContractSerializer (which is the default serializer for data contracts, which node was) can only serialize and deserialize known types. Since dynamic proxies are just that, dynamic, they aren’t known types. I found two ways that I could potentially solve it.
The first way I saw involved adding an attribute that forced it to look through the list of known types again before serialization, while simultaneously adding the dynamic proxies to a list of known types as they got created, and merging it with the current list of known types. This looked interesting, but slightly more complex than it should be, especially since I also soon found that there is already a class called ProxyDataContractResolver in existence. All I had to do was get the DataContractSerializer to use this ProxyDataContractResolver instead of the DataContractResolver.
Note: If you want to learn more about that first method, you can find out all about it here: http://daniel.wertheim.se/2009/12/01/wcf-datacontractserializer-problems-with-dynamic-proxies-in-entity-framework-4.
Therefore, I found the following Attribute class that I used on all Operation Contracts that would need to pass a dynamic proxy. This ProxyDataContractResolver would map proxy types to pure POCO types, allowing serialization.
Unfortunately, this method caused the client to receive and deserialize the actual POCO entities. It was at this point that I realized the lazy loading and WCF serialization don’t really make a whole lot of sense together, because in order to serialize, all of your navigation properties are going to be used and queried, making the entire purpose of lazy loading void. I had gotten to this point simply wanting to be able to access all of my properties, and I didn’t really care about the lazy loading aspect it all. Now that I succeeded, however, I realized that if you load entire objects with their related objects, you could very easily end up passing entire tables of your database (or at least large portions) simply from the extensive network of object relationships.
This was definitely a huge setback, but at the same time was (in my opinions) a great use of time and a wonderful learning experience. While the things I tried and posted here is by no means trivial, it is also only a fraction of the information and reading I found. I know have a much better understanding of how WCF services work.
But, as you may have noticed, I still had not managed to actually pass related objects through WCF. That may be for another post.