Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

WCF cyclic references support

WCF DataContract serializer isn't by default aware of cyclic object graphs. If you encounter the Object graph for type 'X.Y.Z' contains cycles and cannot be serialized if reference tracking is disabled error - read to the end.

There is a simple solution and it's well documented in the Preserving Object Reference in WCF article. However the code is very chaotic and contains mistakes. I've made several classes to support a simple and powerful control over the DataContract serialization parameters.

Support classes

  1. ApplyCyclicDataContractSerializerOperationBehavior

    An operation behavior and The DataContractSerializerOperationBehavior descendant. Used to inject custom configured DataContractSerializer instances into WCF pipeline.

    internal class ApplyCyclicDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior{
        private readonly Int32 _maxItemsInObjectGraph;
        private readonly bool _ignoreExtensionDataObject;
        private readonly bool _preserveObjectReferences;
    
        public ApplyCyclicDataContractSerializerOperationBehavior(OperationDescription operationDescription, Int32 maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences)
            : base(operationDescription) {
            _maxItemsInObjectGraph = maxItemsInObjectGraph;
            _ignoreExtensionDataObject = ignoreExtensionDataObject;
            _preserveObjectReferences = preserveObjectReferences;
        }
    
        public override XmlObjectSerializer CreateSerializer(Type type, String name, String ns, IList<Type> knownTypes) {
            return (new DataContractSerializer(type, name, ns, knownTypes, _maxItemsInObjectGraph, _ignoreExtensionDataObject, _preserveObjectReferences, null /*dataContractSurrogate*/));
        }
    
        public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes) {
            return (new DataContractSerializer(type, name, ns, knownTypes, _maxItemsInObjectGraph, _ignoreExtensionDataObject, _preserveObjectReferences, null /*dataContractSurrogate*/));
        }
    
    }
    
  2. CyclicReferencesAwareContractBehavior

    A contract behavior. Used to apply the DataContractSerializerOperationBehavior operation behavior to every operation within a contract.

    public class CyclicReferencesAwareContractBehavior : IContractBehavior{
        private const Int32 maxItemsInObjectGraph = 0xFFFF;
        private const bool ignoreExtensionDataObject = false;
    
        private bool _on;
    
        public CyclicReferencesAwareContractBehavior(bool on) {
            _on = on;
        }
    
        #region IContractBehavior Members
    
        public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) {
        }
    
        public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime) {
            ReplaceDataContractSerializerOperationBehaviors(contractDescription, _on);
        }
    
        public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime) {
            ReplaceDataContractSerializerOperationBehaviors(contractDescription, _on);
        }
    
        internal static void ReplaceDataContractSerializerOperationBehaviors(ContractDescription contractDescription, bool on) {
            foreach (var operation in contractDescription.Operations) {
                ReplaceDataContractSerializerOperationBehavior(operation, on);
            }
        }
    
        internal static void ReplaceDataContractSerializerOperationBehavior(OperationDescription operation, bool on) {
            if (operation.Behaviors.Remove(typeof(DataContractSerializerOperationBehavior)) || operation.Behaviors.Remove(typeof(ApplyCyclicDataContractSerializerOperationBehavior))) {
                operation.Behaviors.Add(new ApplyCyclicDataContractSerializerOperationBehavior(operation, maxItemsInObjectGraph, ignoreExtensionDataObject, on));
            }
        }
    
        public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) {
        }
    
        #endregion
    }
    
  3. CyclicReferencesAwareAttribute

    The most valuable member of the triade. Used to apply cycling support policy to the whole service interface or an individual interface operation.

    [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Method)]
    public class CyclicReferencesAwareAttribute : Attribute, IContractBehavior, IOperationBehavior{
        private readonly bool _on = true;
    
        public CyclicReferencesAwareAttribute(bool on) {
            _on = on;
        }
    
        public bool On {
            get { return (_on); }
        }
    
        #region IOperationBehavior Members
    
        void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
        }
    
        void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation) {
            CyclicReferencesAwareContractBehavior.ReplaceDataContractSerializerOperationBehavior(operationDescription, On);
        }
    
        void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) {
            CyclicReferencesAwareContractBehavior.ReplaceDataContractSerializerOperationBehavior(operationDescription, On);
        }
    
        void IOperationBehavior.Validate(OperationDescription operationDescription) {
        }
    
        #endregion
    
        #region IContractBehavior Members
    
        void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
        }
    
        void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) {
            CyclicReferencesAwareContractBehavior.ReplaceDataContractSerializerOperationBehaviors(contractDescription, On);
        }
    
        void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime) {
            CyclicReferencesAwareContractBehavior.ReplaceDataContractSerializerOperationBehaviors(contractDescription, On);
        }
    
        void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) {
        }
    
        #endregion
    }
    

Examples:

CyclicReferencesAware applied to the whole interface:

[ServiceContract]
[CyclicReferencesAware(true)]
public interface TestFacade
{
    [OperationContract]
    void Hello(SomeData data);
}

CyclicReferencesAware applied to individual operation(s):

[ServiceContract]
public interface TestFacade
{
    [OperationContract]
    [CyclicReferencesAware(true)]
    void Hello(SomeData data);
}

CyclicReferencesAware applied to the whole interface and individual operation(s):

[ServiceContract]
[CyclicReferencesAware(false)]
public interface TestFacade
{
    [OperationContract]
    [CyclicReferencesAware(true)]
    void Hello(SomeData data);
}

25 коммент.:

Анонимный комментирует...

Works nicely, but what to do when sending the objects from client back to Server?

Unknown комментирует...

You must apply this attribute to the client interfaces as well.

Анонимный комментирует...

Thanks for the nice article.

I assume that this attribute will need to be added to the service proxy either generated or coded manually? I'm using svcutil.exe to generate the proxies - there appears to be a mechanism to support the custom attribute:

http://msdn.microsoft.com/en-us/library/aa717040(VS.85).aspx

Анонимный комментирует...

Sweet, works like a charm!

Eric Kaufman комментирует...

Great Article! I am wondering how or where I can apply this attribute to the client interface. thanks!

John Haigh комментирует...

For anyone coming across this like I did. With the new .NET 3.5 SP1 you no longer need to pass an instance of DataContractSerializer to the WCF runtime and write the above. Simply specify the DataContract IsRefence to true.

[DataContract(IsReference = true)]
See an article here: http://www.zamd.net/2008/05/20/DataContractSerializerAndIsReferenceProperty.aspx
I.e.
[DataContract(IsReference = true)]

public class Employee

{

[DataMember]

public string Name { get; set; }

[DataMember]

public Employee Manager { get; set; }

}

alulema комментирует...

Hey chabster and all u guys.
Was anybody able to send object from Silverlight back to server? It seems the classes u r using here don't exist in Silverlight.

Any idea? Thanks in advance

Unknown комментирует...

Thanks!

Works perfectly with EF POCO objects serialized over WCF web services!

R

Unknown комментирует...

Hi, great code! Is there a way to do this breadth first? I noticed that if I return this in a SOAP response the first object has the information as expected. However, then when it drills down to the type that cyclically references the first type, it populates the objects there. So on the top level I end up with 1 full object and a bunch of references. Ideally, I would like to have all the main data at the top level and the references when you drill down.

Thanks,

Google комментирует...

Hi! Could we use [CyclicReferencesAware(true)] with WCF RIA Services in some way?
Thanks!!!

Анонимный комментирует...

Informative article, just what I needed.
Look at my homepage ; GFI Norte

Анонимный комментирует...

Very descriptive post, I liked that a lot. Will there be a part 2?


Take a look at my web blog :: online backup services
Here is my blog post ... online backup solution

Анонимный комментирует...

I seldom leave comments, however I read a ton of remarks
on "WCF cyclic references support". I do have a couple of questions for
you if you do not mind. Could it be simply me or do a few of these comments appear like
they are left by brain dead individuals? :-P And, if you are posting on
additional online social sites, I'd like to follow you. Would you list of every one of your shared sites like your linkedin profile, Facebook page or twitter feed?

My blog post :: online backup reviews
My homepage ... online backup server

Анонимный комментирует...

I'm impressed, I have to admit. Seldom do I come across a blog that's
both equally educative and amusing, and let me tell you, you have hit the nail on
the head. The problem is something too few men and women are speaking intelligently about.
I am very happy that I came across this during
my hunt for something concerning this.

Review my weblog online backup
my site - online backup service

Анонимный комментирует...

This info is invaluable. How can I find out more?

my blog ... insomnia lyrics 007
Here is my site : insomnia 5dpo

Анонимный комментирует...

Hi, I would like to subscribe for this webpage to obtain most recent updates, so where can i do it please assist.


Also visit my site - insomnia movie
Here is my site : insomnia uiuc

Анонимный комментирует...

We are a group of volunteers and opening a new scheme in our community.
Your website offered us with valuable info to work on.
You've done an impressive job and our entire community will be grateful to you.

Here is my weblog :: online backup free
Feel free to visit my web site online backup service

Анонимный комментирует...

I know this if off topic but I'm looking into starting my own weblog and was wondering what all is needed to get set up? I'm
assuming having a blog like yours would cost
a pretty penny? I'm not very web savvy so I'm not 100% positive. Any tips or advice would be greatly appreciated. Thank you

Feel free to surf to my site insomnia facts
Feel free to surf my site : insomnia quote fight club

Анонимный комментирует...

I'll right away grab your rss as I can not in finding your e-mail subscription hyperlink or e-newsletter service. Do you have any? Please let me realize in order that I could subscribe. Thanks.

My web blog ... diets that work for women

Анонимный комментирует...

Thank you, I have recently been looking for info about this subject for a while and yours is the greatest I have
came upon till now. However, what concerning the conclusion?

Are you certain concerning the source?

my blog ... psn code

Анонимный комментирует...

hello!,Ӏ love youг wrіting so
a lot! share we κeеp in tοuсh extгa
аbout уοur post on ΑОL?
I rеquіre a specialiѕt in thiѕ housе to гesοlvе mу problеm.

Maybe that's you! Looking ahead to see you.

Look at my webpage ... it support

Анонимный комментирует...

Hi! I could have sworn I've been to this site before but after browsing through some of the post I realized it's
new to me. Anyways, I'm definitely delighted I found it and I'll be bookmarking and checking back often!


my webpage - home cellulite treatment

Анонимный комментирует...

Wow that was strange. I just wrote an incredibly long comment but after I clicked submit my comment didn't show up. Grrrr... well I'm not writing all
that over again. Anyhow, just wanted to say great blog!


Here is my web page :: loggedonuser ()

Анонимный комментирует...

Link exchange is nothing else except it is simply placing the other person's blog link on your page at suitable place and other person will also do similar in support of you.

Also visit my web page ... Adf.ly Earnings Booster

Анонимный комментирует...

My relatives all the time say that I am wasting my
time here at web, however I know I am getting experience everyday
by reading such pleasant posts.

Also visit my weblog :: way to buy real cheap twitter followers free

Отправить комментарий

Copyright 2007-2011 Chabster