WCF and the Cyclical Object Referencing Problem

by Moridin8 28. April 2008 20:10

As usual, when you need something done right, you sometimes just can not rely on MSDN or its forums, google or any form of current literature - or even a number of Microsoft employee's on my contact lists. 

Thats just the way of it sometimes.

What am I talking about? DataContractSerialization in WCF - in reference to the sometimes essential need to send cyclical object graphs across the wire.  WCF just does not support it.  At all - Something to do with an Xml Serialization standard or some such.  Which is fine in the general run of the mill situations involved with third party SOA services publically published - well... no, not in my oppinion... it's just not good enough.

But I digress.

Ok, some of you may be asking, "What do you mean by cyclical object graphs?",

Here is crude example:

[DataContract]
public class Example
{
    [DataMember]
    public Example MeMyselfAndI { get; set; }

    public Example()
    {
        this.MeMyselfAndI = this;
    }
}

Notice the (bad example) of the MeMyselfAndI property referencing itself?  When you serialize the object any serializer object will usually spit out an exception.

But wait...  Doesn't WCF use the DataContractSerializer class?  And doesn't that support cyclical object graphs?  Yes it does, which is great.  It's just that WCF doesn't generate the DataContractSerializer with the PreserveReferences feature enabled, which can be a major issue on services hosted in IIS that use the .svc files, and auto-generated client proxies and/or in situations where you have absolutely no choice but to use WCF configuration files - the M$ recommended route - and rightly so.

There are plenty of examples on the web demonstrating how to use the DataContractSerializer/PreserveReferences purely on the client (and only the client so far as I can find) when building your WCF client manually sans config.  This however only solves half of the problem because you just can not get the server to play the same way with these examples if you need cyclical object graphs returned, and the lack of a configuration support flies in the face of all reason.

MSDN is simply no help, neither are the forums, neither are a couple of highly skilled Microsoft employed architects I know.  *grin* they will choke when I send them the link to this.  I tend to enjoy these moments...

So, here is the solution... here is how to get WCF to support PreserveReferences... and still maintain the configurational aspect of WCF.

First, the server

The mechanics of WCF hosted in IIS involves the use of the well known '.svc' file.  Behind the scenes within WCF each page is generated within a factory that generates a ServiceHost instance.  The service host handles the requested calls to your service as well as all the appropriate discriptive information required during the lifetime of the service request. 

Usually, the ServiceHost is completely handled by the activation classes within the the ASP.NET model (Microsoft say it's handled by IIS... not true... but makes things easy to explain I suppose).  The ServiceHost contains all the information about the service contracts etc.  However, the ServiceHost isn't generally available under normal circumstances - and we need the ServiceHost.

So, to get access to the ServiceHost, we need to create our own ServiceHost factory and tell the '.svc' to it instead of the default factory supplied by WCF (code follows shortly).  To tell the '.svc' file to use the custom factory we do the following:

Remove the codebehind attribute and replace it with the Factory attribute thus...

<%@ ServiceHost Language="C#" Debug="true" Service="TestWCF.ManagerService" Factory="TestWCF.ManagerServiceHostFactory"  %>

Don't worry about what visual studio says about the Factory attribute not being valid, it is supported by WCF in the internal System.ServiceModel.Activation.ServiceParser class, which is more important.

The custom ServiceHostFactory, ServiceHost and a minor DataContactSerializer replacement are as follows:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;
using System.Xml;

public class ManagerServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, 
Uri[] baseAddresses) { ManagerServiceCustomHost customServiceHost
= new ManagerServiceCustomHost(serviceType, baseAddresses);


return customServiceHost; } } public class ManagerServiceCustomHost : ServiceHost { public ManagerServiceCustomHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { } protected override void ApplyConfiguration() { base.ApplyConfiguration(); RefPreserveDataContractSerializerOperationBehavior.Reconfigure
(base.ImplementedContracts["TestWCF.IManagerService"].Operations); } } public class RefPreserveDataContractSerializerOperationBehavior
: DataContractSerializerOperationBehavior { public static void Reconfigure(OperationDescriptionCollection odc) { foreach (OperationDescription oper in odc) { for (int i = 0; i < oper.Behaviors.Count; i++) { if (oper.Behaviors[i] is DataContractSerializerOperationBehavior) { DataContractSerializerOperationBehavior tmp
= oper.Behaviors[i]
as DataContractSerializerOperationBehavior; // replace out the DataContractSerializerOperationBehavior
// with our own...
RefPreserveDataContractSerializerOperationBehavior tmp2
= new RefPreserveDataContractSerializerOperationBehavior
(oper, tmp.DataContractFormatAttribute); tmp2.DataContractSurrogate = tmp.DataContractSurrogate; tmp2.IgnoreExtensionDataObject = tmp.IgnoreExtensionDataObject; tmp2.MaxItemsInObjectGraph = tmp.MaxItemsInObjectGraph; oper.Behaviors[i] = tmp2; break; } } } } public RefPreserveDataContractSerializerOperationBehavior
(OperationDescription operationDescription) : this(operationDescription, null) { } public RefPreserveDataContractSerializerOperationBehavior
(OperationDescription operation,
DataContractFormatAttribute
dataContractFormatAttribute
) : base(operation, dataContractFormatAttribute) { } public override XmlObjectSerializer CreateSerializer
(Type type, string name, string ns, IList<Type> knownTypes) { return CreateDataContractSerializer(type, name, ns, knownTypes); } private static XmlObjectSerializer CreateDataContractSerializer
(Type type, string name, string ns, IList<Type> knownTypes) { return CreateDataContractSerializer(type, name, ns, knownTypes); } public override XmlObjectSerializer CreateSerializer
(Type type, XmlDictionaryString name, XmlDictionaryString ns,
IList<Type> knownTypes) { return new DataContractSerializer
(type, name, ns, knownTypes, int.MaxValue,
false,
true /* PreserveReferences*/,
null); } }

You should notice in the DataContractSerializer the 'static void Reconfigure(OperationDescriptionCollection odc)' method.  This is what does the work to ensure that WCF gives us that nice cyclical reference feature we are after.  Also please notice the contructor parameters used in the CreateSerializer method (please see MSDN for more info).

[client work pending... need to eat... ;)]

(didn't get chance to complete.... sorry)

Tags: , , , , , , ,

Articles

The Four .NET Web Development Steps to Enlightenment

by Moridin8 22. June 2007 20:05

In my humble opinion Microsoft made a slight mistake with the way they implemented ASP.NET in 2005. They should have split their documentation into at least three areas and defined a path through them that over time introduced newer and lower-level concepts as projects become more complex – and that this path should be kept independent from the whole certification framework and made generally available for everyone.

These three general areas should have been split into something like as follows:

  1. Small sites that require only the highest level knowledge of ASP.NET – essentially this would be what come’s out of the box as most people new to ASP.NET understand it.
     
  2. Medium complexity/size sites that require some control customisation and a greater understanding of the underlying technology. You could say that this is the point you start to wonder if ‘.ascx’ is going to do everything you want and maybe you might be considering a Web Service or two. You also may start to wonder if the 2005 version of web projects is cutting the mustard. You also find that class libraries are starting to look attractive.
     
  3. Larger sites that maybe could be called ‘Enterprise’ or ‘distributed’ solutions. Here you would require a varying knowledge of the platforms and technologies from intermediate to exceptional. You instantly discover that the nice fuzzy world of ASP.NET isn’t that fuzzy after all and in fact can become a hindrance. You have to work more and more outside of the nice cosy ASP.NET world and ASP.NET simply becomes a thin layer façade over the real work underneath. You may also start praising Microsoft for caving in to the real world and releasing proper Web Application projects as part of VS2k5SP1.

It is not that any of the above isn’t possible to construct with everything that Microsoft makes available, merely that there does not seem to be an easy path through the possibilities for people and many developers new to the ASP.NET platform, while finding the initial transition easier than with the previous versions of .NET, find that there can be a very steep learning curve involved if anything more complex than the ASP.NET based controls is required.

One of the largest issues I encounter is related specifically to this learning curve, especially with individuals migrating from ASP, PHP or Coldfusion (although admittedly this is becoming less of an issue with PHP as many PHP developers are happily embracing the OOP paradigm as the language grows). Even though Microsoft’s marketing is proving successful with ASP.NET’s take up being faster that at any other time, Microsoft is making technical transitions potentially difficult DUE to with their marketing and high level fluffy features available in ASP.NET.

That’s the thing though, it *is* easy to create basic dynamic web sites with ASP.NET using many of the advanced drag-and-drop features of the IDE, so initially ASP.NET comes over in the most part as a breath of fresh air and in fact, Microsoft has excelled here. However, if you do need to create something a little more complex than the statistical majority represented by the smaller-site implementations, you have to start to scratch and look beneath that shiny veneer; this where many find that under the shiny exterior ASP.NET can actually be a complicated beast.

ASP.NET reeks of one aspect of the KISS (Keep It Simple Stupid) principle. In order to make ASP.NET easy to use for the masses they have had to stretch and contort everything. Most of it is exceptionally clever and ASP.NET is a wonderfully powerful reflection and JIT compiler engine. It’s because of this end-user KISS based design that Microsoft have inadvertently made the ASP.NET internals a bit complicated. Also because of this ‘ease of use’ angle Microsoft has completely ignored in many instances (except for the Singleton for example) the entire OOP design paradigm from MSDN and more specifically ASP.NET.

This is a huge travesty that is giving me the most cause for concern at the present moment.

Many well intentioned and possibly very skilled Web Developers are missing the point to .NET because of this. .NET is Object Oriented! Why the hell is Microsoft not telling people how to leverage this paradigm? Why are so many developers being left out in the cold in this manner with .NET?

It is because of this issue that I and no doubt many, many other developers/architects ‘in the know’ are finding that they are being employed to work on projects or teams that are suffering badly directly or indirectly because of a lack of knowledge in OOP, .NET and how computers really tick.

Because I don’t have time to do something about the situation described above, all I can do is give some general pointers…

So those four steps to ‘.NET Web Development’ enlightenment are in my opinion as follows:

  1. OOP
    • Learn the basic pillars of OOP, ‘Encapsulation’, ‘Inheritance’ and ‘Polymorphism’ using the .NET language of your choice.
    • Learn how to use the Gang of Four design OOP patterns with the .NET language of your choice
    • Learn to think in these patterns so that when you are presented with a problem, you can state to other developers and business analysts the names of these patterns and why you think they are useful.
       
  2. ASP.NET
    • Don’t use ASP.NET’s inbuilt tools simply for the sake of using them, they are sometimes too generalised and trying to be too many things for too many reasons.
    • Never use the newer Website projects in VS2k5 – Install SP1 and use real Web Application projects. Convert them if needed. Get away from the VS2k5 website!
    • Keep UI logic and Business logic separate. Don’t be afraid to use class library projects.
    • Use Web Control library projects instead of ASCX’s if appropriate, especially if the controls are to be re-useable for other projects.
       
  3. Learn how your computer works; learn how .NET makes your computer work.
    • If you don’t know:
      • Why a string comparison is slower than an integer comparison
      • That ASP.NET isn’t just a request/response based environment
      • Why the Dispose/Finalize methods are so important and how the GC works
      • How your computers memory and general IO works

        And you are not interested in finding out, then to be quite blunt - stick to small websites!
         
    • Learn why you should split Presentation from business logic and business logic from data logic. 
    • Get Lutz Roeder’s Reflector and don’t be scared to use it to view the internals of .NET. You may be surprised what you find and how it will improve your ability to leverage .NET.
       
  4. Be your own apprentice.
    1. Buy books and learn.
    2. Use gained knowledge and help others
    3. Goto point a - Repeat forever.

 

Tags: , , , , , , , , , , , , , ,

Articles

Powered by BlogEngine.NET 1.5.0.7

About Matt R.Warren

MeMy name is Matt and I am the current tenant of this small corner of the internet. I mostly architect, design and prototype applications that use .NET with C# and a little C++/CLI for Enterprise although I am aware of and enjoy fully embracing Java based solutions and alternatives such as Mono/Linux.  

I have worked on projects ranging from small tools to large distributed real-time Enterprise systems ranging from EPOS and real-time/JIT stock management systems, to distributed applications for National/International Utility, Healthcare, Insurance and Finance  in the private sector in both the USA and the EU.

My LinkedIn Profile (Opens new window/tab)

“Matt is one of the brightest people I've worked with. His in-depth knowledge of the .NET frameworks has been a tremendous benefit to nVISIA and our clients. His knowledge of software architecture in general allows him to architect systems for the best fit to his client's needs.” 
Dan Christopherson , Technical Director , nVISIA

“I had the distinct pleasure of working with Matt at nVisia. Matt's understanding of the Microsoft Technical space is outstanding. He is constantly working on improving his technical skills and rapidly masters any new technology that he encounters. He is an excellent teacher and a wonderful asset for any size team.” 
Jim Harnden , Senior Technical Architect , nVISIA

“Matt Warren is a very talented developer with great capacity for self study, investigation and adapts to new languages and frameworks with ease. He has an excellent grasp of software architecture and modern development principles. He has proven himself time and time again to be a hard worker and someone who can get the job done when you're in a tight spot.” 
Andrew Jump , Partner, C# Developer , Contegra

This website represents some of my spare time.  My small presence on the web between my family and my career.  I hope over time you find many useful things here.