3

I have a problem trying to serialize a graph with cyclic references. This is my scenario:

        [DataContract(IsReference = true)]
        public class Child
        {
            [DataMember]
            public string name { get; set; }
            [DataMember]
            public Parent parent { get; set; }
        }


        [DataContract(IsReference = true)]
        public class Parent
        {

            [DataMember]
            public string name { get; set; }
            [DataMember]
            public List<Child> children { get; set; }

            public Parent()
            {
                children = new List<Child>();
            }

        }

   //Testing method
   //Invoke it through WCF Service (basocHttpBinding)
   public Parent Test()
    {
        Parent p = new Parent();
        p.name = "Pepe";
        p.children.Add(new Child { name = "Juan", parent = p });
        return p;

    }

When I try to serialize a Object of type Parent, it produces an System.StackOverflowException, because the Cyclic reference.

If I delete in the Child class the DataMember of his navigation property to the Parent, It works perfectly, but I lose the navigation property in the serializated object. I have been researching for a solution, but no Luck. I only found that IsReference Property solves this type of Cyclic reference, but in my case it seems not to work correctly.

Some help will be welcomed!!

Thanks in advance

PARENT CLASS:

[DataContract(IsReference = true)]  
[KnownType(typeof(Expediente))]    
public partial class Expediente: POCO, IEntidad
{   

    public Expediente() : base(typeof(Expediente))            
    {
    }

    public virtual int Sid
    {
        get;
        set;
    }
    [DataMember] 
    public virtual int NumExpediente
    {
        get;
        set;
    }

    [DataMember]
    public virtual ICollection<Policia> Policias
    {
        get
        {
            if (_policias == null)
            {
                var newCollection = new FixupCollection<Policia>();
                newCollection.CollectionChanged += FixupPolicias;
                _policias = newCollection;
            }
            return _policias;
        }
        set
        {
            if (!ReferenceEquals(_policias, value))
            {
                var previousValue = _policias as FixupCollection<Policia>;
                if (previousValue != null)
                {
                    previousValue.CollectionChanged -= FixupPolicias;
                }
                _policias = value;
                var newValue = value as FixupCollection<Policia>;
                if (newValue != null)
                {
                    newValue.CollectionChanged += FixupPolicias;
                }
            }
        }
    }
    private ICollection<Policia> _policias;


    private void FixupPolicias(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Policia item in e.NewItems)
            {
                item.Expediente = this;
            }
        }

        if (e.OldItems != null)
        {
            foreach (Policia item in e.OldItems)
            {
                if (ReferenceEquals(item.Expediente, this))
                {
                    item.Expediente = null;
                }
            }
        }
    }
}

CHILD CLASS:

[DataContract(IsReference = true)]
[KnownType(typeof(Policia))]    
public partial class Policia: POCO, IEntidad
{

//Constructor por defecto
public Policia() : base(typeof(Policia))            
{
}


[DataMember] 
public virtual int Sid
{
    get { return _sid; }
    set
    {
        if (_sid != value)
        {
            if (Expediente != null && Expediente.Sid != value)
            {
                Expediente = null;
            }
            _sid = value;
        }
    }
}
private int _sid;

[DataMember] 
public virtual int NumPlaca
{
    get;
    set;
}

[DataMember]
public virtual Expediente Expediente
{
    get { return _expediente; }
    set
    {
        if (!ReferenceEquals(_expediente, value))
        {
            var previousValue = _expediente;
            _expediente = value;
            FixupExpediente(previousValue);
        }
    }
}
private Expediente _expediente;


private void FixupExpediente(Expediente previousValue)
{
    if (previousValue != null && previousValue.Policias.Contains(this))
    {
        previousValue.Policias.Remove(this);
    }

    if (Expediente != null)
    {
        if (!Expediente.Policias.Contains(this))
        {
            Expediente.Policias.Add(this);
        }
        if (Sid != Expediente.Sid)
        {
            Sid = Expediente.Sid;
        }
    }
}
}

To serialize I am using standar DataContractSerializer throught WCF. I only added some behaviour to my WCF services through an attribute to Serilize Poco Proxies. This is the attribute code:

public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior
{
    public ApplyDataContractResolverAttribute()
    {
    }

    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
    {
        DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior =
            description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver =
            new ProxyDataContractResolver();
    }

    public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
    {
        DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior =
            description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver =
            new ProxyDataContractResolver();
    }

    public void Validate(OperationDescription description)
    {            
    }

}

POCO base Class. A simple Class with some base Validation functionallity

[DataContract(IsReference = true)]
public class POCO
{
    public POCO(Type Tipo)
    {
        RegistrarMetadatos(Tipo);
    }

    /// <summary>
    /// Este metodo valida la clase y devuelve todos los mensajes de validacion.
    /// </summary>        
    /// <returns>Devuelve una Lista de tipo ValidationResults con todos los resultados de la validación</returns>
    public List<ValidationResult> Validar()
    {
        List<ValidationResult> Resultados = new List<ValidationResult>();
        ValidationContext ctx = new ValidationContext(this, null, null);
        Validator.TryValidateObject(this, ctx, Resultados, true);
        return Resultados;
    }

    /// <summary>
    /// Este metodo valida la clase y en caso de no superar la validación levante una excepción del tipo ValidationEx
    /// </summary>        
    public void TryValidar()
    {
        List<ValidationResult> Resultados = new List<ValidationResult>();
        ValidationContext ctx = new ValidationContext(this, null, null);
        Validator.TryValidateObject(this, ctx, Resultados, true);
        if (Resultados.Count > 0)
        {
            throw new ValEx(Resultados);
        };
    }


    /// <summary>
    /// Este metodo busca la clase interna que contiene los metadatos con las 
    /// Validaciones y las adhiere a la clase principal. Parae ello usa la convención "MD"
    /// <param name="Tipo">El tipo de la clase que sobre la que se quiere registar los metadatos</param>
    /// </summary>
    private void RegistrarMetadatos(Type Tipo)
    {
        string aux = Tipo.AssemblyQualifiedName;

        aux = aux.Replace(Tipo.FullName, Tipo.FullName + "+" + Tipo.Name +"MD");

        Type Meta = Type.GetType(aux);

        if (Meta != null)
        {
            var DescProv =
                new AssociatedMetadataTypeTypeDescriptionProvider(
                    Tipo, Meta
                );
            TypeDescriptor.AddProviderTransparent(DescProv, Tipo);
        }

    }

}
10
  • 3
    How do you serialize the objects? What Serializer are you using? Commented Oct 23, 2012 at 10:32
  • What did you mean when said "attribute IsReference not work correctly?" Commented Oct 23, 2012 at 10:43
  • I am using DataContractSerializer, I am trying to serialize POCO With Proxies enabled, to a non .Net Client. Attribute IsReference seems not to Work, because It is supposed that enabling it, this problem will not ocur, because the xml will be composed with ids to the objects, instead of the entire object. Watch: zamd.net/2008/05/20/… Commented Oct 23, 2012 at 10:52
  • 2
    Show us real data contracts and real serialization code. Commented Oct 23, 2012 at 10:54
  • @Fernando Note: Remove the KnownTypeAttributes on your Poco. Normally are these attributes declared on base types (in your case POCO) Commented Oct 23, 2012 at 11:43

1 Answer 1

1

The serializator looks into the Child and sees Parent and vice versa. That's why there is a recursion which leads to the ThisSiteException.

The solution is to add the NonSerialized (fields only) attribute or to remove the DataContract (which is not desirable).

Consider refactoring your code to remove (my opinion) the Parent property from any child and auto-add them on the receiving side of your client-server application.

Sign up to request clarification or add additional context in comments.

8 Comments

Please, read about DataContractAttribute.IsReference: msdn.microsoft.com/en-us/library/… This property is intended for avoiding circular references during serialization.
I am using actually this property but don't seems to work, because I fanilly catch a StackOverflow.Exception
@Fernando: again, show the real code. The code you provided in the question even won't compile. Believe me, DataContractAttribute.IsReference works well.
After some minutes of fighting with editor, Real Code added!! :)
@Dennis now the sample code provided (In the first code block) raises the exception so you can reproduce the problem easily. Also uploaded code here: 4shared.com/zip/VTn6oSkC/Serializacion.html
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.