Now if you are like me, you’ve probably had some interest in POCO (plain old CLR objects) objects for at least some time. They are an invaluable tool in the distributed systems and service oriented architecture areas, but up until now they’ve been inaccessible for those designs.
In a nutshell, both LINQ to SQL and Entity Framework (v1) class entities did not support serialization for the purpose of stateless transport(such as web service communication). This stems from the embedded context tracking attributes, and the design which stipulates a fairly poor experience for those daring enough to detach entities and “pass them around”.
Enter the ADO.net Entity Framework v2.. ahem, version 4 which shipped in the early part of this year. Whilst the EFv4 doesn’t support POCO objects out of the box (you have to use an online template), it’s easy enough to accomplish with minimal effort. Plus, they can used (almost) as seamlessly as non-POCO objects.
Before we get into the nitty gritty of this particularly long post, I will direct your attention to the following MSDN article which covers most of the steps for harmonious life with POCO objects and WCF services. What the article does not cover is handling somewhat more complex object graphs. In other words, the MSDN scenario is fine with fairly basic (and bland) objects, but it’s pretty nasty when you have objects containing, well, joins (collections, relationships, yada yada).
Now what follows, is based on a number of other articles floating around the Internet. I’m not trying to take any credit for (the majority), I’m just collating the information into one handy to reach place. I’m also going to supply sample code in case you have any trouble getting it all configured. The parts which are my implementation alone, I’ll highlight.
The Data Model
First, let us take a quick look at the sample data model. Nothing fancy, I’ll admit, but enough for our purposes:
Which we will use with a WCF Service or two. You can use the attached T-SQL script to create and populate a SQL Server database (and later generate your EDMX model from that schema). Next, create a solution containing WCF services, and add a ADO.net Entity Framework (v4) model. You can see from my sample below, the model is admittedly not very complex. Notice the “self join” on the Category table. This is not an uncommon scenario in designing parent/child relationships at the DB level. It also has the (awesome) advantage of generating Parent/Child navigation properties (you may need to do some renaming if you generated the model from my sample schema).
The Object Model
Solutions and Settings
Once you have generated the model, right click anywhere on the blank model surface and select “Add Code Generation Item”. This prompts you with a bulky dialog window – select “Online Templates” from the left hand side tree view.
Select ADO.NET C# POCO Entity Generator and click OK a few times as needed. The template builds up the POCO entities and removes the EDMX/Designer based implementation which the EF designer would have originally generated. This leaves you with a number of new files in your solution, which should look a lot like the following:
Now that I’ve got your attention, lets have a think about how we’re going to expose these via WCF. I’ve created two WCF Services, SystemLogService.svc and ProductService.svc.
The interface definition of each is per below:
Don’t worry about those attributes just yet! I’ll explain a little about why they are necessary shortly. If you have reviewed the original MSDN article you’ll recall:
“The POCO proxy type cannot be directly serialized or deserialized by the Windows Communication Foundation (WCF), because the DataContractSerializer serialization engine can only serialize and deserialize known types. The proxy type is not a known type. For more information, see the Serializing POCO Proxies section in the Working with POCO Entities topic. To serialize POCO proxies as POCO entities, use the ProxyDataContractResolver class to map proxy types to POCO types during serialization.”
Which means that the default (runtime) classes generated by LINQ/EF are incompatible with WCF because WCF requires classes defined at compile time.
As such, you need to both disable the use of Proxies, and also label your web service methods with the [ApplyDataContractResolver] attribute as seen above. You can obtain the details about this attribute from the MSDN article or from my sample solution. You only need to use it on the service side. This is as simple as creating a new class and pasting the implementation from either source. Then add the attribute to decorate your web service definition (on the interfaces).
Now, for the part not previously covered – we generally encounter a problem with passing entities which are a little more complicated than the example POCO objects encountered in the MSDN article. Take our sample application. The System Log entities define a basic relationship, and the products include a (fairly standard) self join, allowing product categories to have a hierarchy.
If we then create a standard console application, and add a web service reference, we can observe the class definition from the generated WSDL (below).
If you’re unsure about how to view the WSDL code within Visual Studio, simply follow these steps:
- Right Click on the Service Reference
- Select “View in Object Browser”
- From here, expand the namespace of the reference, then right click on one of the interfaces
- Select “Go To Definition”
Now assuming you have done everything correctly, you should be able to consume the web services and the POCO objects in your console application:
If we execute the code, the first web service call returns fine, with no errors. The second call however, which returns a collection is not as fortunate.
When we step over the following line of code, we receive an exception with the following message:
SystemLog logs = logClient.GetLogEntryByCategoryId(1);
“The underlying connection was closed: The connection was closed unexpectedly.”
Looking deeper into the service side of affairs (debugging), we may discover that the exception being thrown is, in fact, the following:
There was an error while trying to serialize parameter http://tempuri.org/:GetLogEntryByCategoryIdResult. The InnerException message was ‘Object graph for type ‘Products.WcfServices.SystemLogCategory’ contains cycles and cannot be serialized if reference tracking is disabled.’. Please see InnerException for more details.
After a fair amount of searching, I found a way to work around this little problem. Implementing the suggested attribute [CyclicReferencesAware(true)] to methods involving collections appears to fix the problem. After applying the attribute and updating the service reference (just to be sure!) you will find the call succeeds, as per below:
But Wait.. There’s More..
Just when you thought it was safe to go back into the ocean.. What happens when we want to send things the other direction?
Let’s look ahead to a web service method which takes one of our POCO objects, and tries to apply an update.
The logic I’ve used here detects a new entity, and also when an existing entity can not be located in the data store.
So nothing terribly complicated, correct? If we implement something on the client side – something very simple, like the following:
When we try to execute this rather simple update scenario, we get the same kind of exception we’ve seen before:
I love it when a plan comes together..
So what is the solution? Well, rather simple, if somewhat complex in the implementation.
The outcome I found which works quite well is to emit the same attribute into the generated WSDL on the client side, when the reference is created.
This turned out to be a pretty straightforward idea, but a terribly intriguing problem to try to solve.
Without delving too much into details (please download and examine the sample solution) the basic premise was two fold:
- Define the required files in a common or shared assembly that both the service and the client project can consume.
- Build a class which implements several WSDL extensions: IWsdlImportExtension, IServiceContractGenerationExtension,IOperationContractGenerationExtension and IOperationBehavior
Basically, the class is triggered when the WSDL is being imported, and it adds the appropriate [CyclicReferencesAware(true)] attribute above the appropriate methods.
To do this, you must modify the client’s App.Config to include the following configuration:
When the WSDL import is called, the referenced extension finds operations decorated with the CyclicReferencesAware attribute (the export decorates them with a documentation text).
When an operation decorated with the attribute is found, the importer adds (writes) a reference to itself to the operations’s behavours collection.
As the WSDL is being generated, it’s a relatively easy step to output the required attribute.
Now, when if you update the service reference the appropriate attribute is applied to the generated WSDL code, as you can see from the screenshot below:
The only thing I didn’t figure out was how to add the required using directive to the generated code, however it is very easy to add the reference yourself – just compile the client project and you’ll get the appropriate errors.
Double click on one, right click on the reference and you can easily add it to the code. I realise it’s a bad practice to modify generated code, but I ran out of patience and figured this wasn’t a terrible oversight. If you find a nice way to fix this, please get in touch.
Running the solution after updating the configuration (and referencing the shared assembly) and now the previous code runs just fine. You can check the database to ensure the update occurred.
Summary and Disclaimer
Thus far, I haven’t had much time to test this any further. I’ve implemented it on a number of web service clients without any problems.
I’ve not tried any further complicated scenarios, but I’d really appreciate any feedback if people find further problems.
To wrap up, I’ve included the sample project and T-SQL to create a database. This is not production code, so please use it as a demo.
There’s no encryption, compression or other types of scenarios we might encounter in a complete system.
It is supplied “As-IS” and no warranty is implied
As always, if you have any feedback please leave it here or get in touch.
Seriously though, I sincerely hope this might help out some folks who are as intrigued and equally baffled with WCF and the Entity Framework.
Bon Appétit. /R
[ Download Sample Project and Schema ]
MSDN Walkthrough on POCO Entities