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

Показаны сообщения с ярлыком WCF. Показать все сообщения
Показаны сообщения с ярлыком WCF. Показать все сообщения

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);
}
Copyright 2007-2011 Chabster