【问题标题】:System.StackOverflowException when trying to Serialize in WCF尝试在 WCF 中序列化时出现 System.StackOverflowException
【发布时间】:2012-10-13 06:16:23
【问题描述】:

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

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

    }

}

【问题讨论】:

  • 如何序列化对象?你用的是什么序列化器?
  • “属性 IsReference 无法正常工作”是什么意思?
  • 我正在使用 DataContractSerializer,我正在尝试将启用代理的 POCO 序列化到非 .Net 客户端。属性 IsReference 似乎不起作用,因为假设启用它,这个问题不会发生,因为 xml 将由对象的 id 组成,而不是整个对象。观看:zamd.net/2008/05/20/…
  • 向我们展示real数据合约和real序列化代码。
  • @Fernando 注意:删除 Poco 上的 KnownTypeAttributes。通常这些属性是在基本类型上声明的(在你的情况下是 POCO)

标签: c# wcf serialization poco datacontract


【解决方案1】:

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

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

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

【讨论】:

  • 请阅读DataContractAttribute.IsReference: msdn.microsoft.com/en-us/library/… 此属性旨在避免序列化期间的循环引用。
  • 我实际上正在使用这个属性,但似乎不起作用,因为我很容易捕捉到 StackOverflow.Exception
  • @Fernando:再次展示真实代码。您在问题中提供的代码甚至无法编译。相信我,DataContractAttribute.IsReference 效果很好。
  • 在与编辑斗争了几分钟后,添加了真实代码! :)
  • @Dennis 现在提供的示例代码(在第一个代码块中)引发了异常,因此您可以轻松地重现问题。还在这里上传了代码:4shared.com/zip/VTn6oSkC/Serializacion.html
猜你喜欢
  • 1970-01-01
  • 2013-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多