我在尝试使用循环引用序列化图形时遇到问题。这是我的场景:
[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);
}
}
}