When serializing object to a client through WCF you often have reference objects that are needing to be serialized. This becomes a problem if the
reference parent and reference child are both of the same type. This causes
WCF to barf over cyclic references. There are two fixes. If you are using
.NET 3.5 or previous you can do this (Thanks to Chabster):
namespace MetroServer.Infrastructure
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Method)]
public class CyclicReferencesAwareAttribute : Attribute, IContractBehavior, IOperationBehavior
{
private readonly bool _on;
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
}
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
}
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*/));
}
}
Usage:
[OperationContract]
[CyclicReferenceAware(true)]
Object MyMethod(int number);
Or if you use .NET 3.5 SP1 you can do this:
[DataContract(IsReference = true)]
public class MyClass
{
[DataMember]
public string MyProperty{ get; set;}
}
I think you will agree .NET 3.5 SP1 is better.
feee9b02-f1e7-4bec-ae4f-2c7d125201bc|0|.0
.NET, C#