首页 文章

尝试在WCF中进行序列化时出现System.StackOverflowException

提问于
浏览
2

尝试使用循环引用序列化图表时遇到问题 . 这是我的情景:

[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 Property解决了这种类型的Cyclic引用,但在我看来它似乎无法正常工作 .

一些帮助将受到欢迎!

提前致谢

家长级:

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

    }

}

1 回答

  • 1

    序列化器查看 Child 并查看 Parent ,反之亦然 . 这就是为什么有一个递归导致 ThisSiteException .

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

    考虑重构您的代码以从任何子项中删除(我的意见) Parent 属性,并在客户端 - 服务器应用程序的接收端自动添加它们 .

相关问题