3

我在尝试使用循环引用序列化图形时遇到问题。这是我的场景:

        [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;

    }

当我尝试序列化 Parent 类型的 Object 时,它会产生 System.StackOverflowException,因为 Cyclic 引用。

如果我在 Child 类中将他的导航属性的 DataMember 删除到 Parent,它可以完美地工作,但是我会丢失序列化对象中的导航属性。我一直在研究解决方案,但没有运气。我只发现 IsReference 属性解决了这种类型的循环引用,但就我而言,它似乎无法正常工作。

一些帮助将受到欢迎!

提前致谢

家长班:

[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;
                }
            }
        }
    }
}

儿童班:

[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;
        }
    }
}
}

要序列化,我通过 WCF 使用标准 DataContractSerializer。我只是通过 Serilize Poco Proxies 的属性向我的 WCF 服务添加了一些行为。这是属性代码:

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 基类。具有一些基本验证功能的简单类

[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);
        }

    }

}
4

1 回答 1

1

序列化器查看Child并查看Parent,反之亦然。这就是为什么存在导致ThisSiteException.

解决方案是添加NonSerialized(仅限字段)属性或删除DataContract(这是不可取的)。

考虑重构您的代码以Parent从任何子节点中删除(我的观点)该属性,并将它们自动添加到您的客户端-服务器应用程序的接收端。

于 2012-10-23T11:11:56.583 回答