[WCF] DataContractSerializer y la propiedad IsReference. .NET 3.5 SP1

 

Hola a todos, siguiendo al hilo de mi anterior post y haciendo caso a un señor que de esto sabe un rato, se puede pasar por referencia los objetillos sin necesidad de generar un behavior especial, simplemente con una propiedad del atributo DataContract.

 

Sigo pensando exactamente lo mismo que antes, que esto, aunque solucione muchos problemas es posible que enmascare un desconocimiento de fondo por parte de los desarrolladores del proyecto, y que las cosas dejen de funcionar correctamente porque esto está o no aplicado.

 

Pero bueno al lío, que esto es rápido y sencillo.

 

Clase que NO se pasa por referencia

[DataContract]
public class Persona
{
[DataMember]
public string Nombre {get;set;}

[DataMember]
public string Apellidos {get;set;}
}

Esto es un DataContract normal y corriente.

 

Clase que SI se pasa por referencia

[DataContract(IsReference = true)]
public class Persona
{
[DataMember]
public string Nombre {get;set;}

[DataMember]
public string Apellidos {get;set;}
}

 

Como podéis observar es mucho más sencillo ahora. Lo único es que es necesario tener instalado el SP1 del framework 3.5.

 

Muchas gracias a Unai por indicarnos el camino….

 

Hasta la próxima.

 

Mario Ropero.

[WCF] Vuelta a la realidad,… y jugando con el DataContractSerializer

Hola a todos!!!!, han pasado muchas semanas sin que haya actualizado el blog, de hecho ni me he acercado a él. Necesitaba desconectar y vaya que si lo he hecho… :). Pero todo se acaba, así que aquí vuelvo a soltar rollazos a troche y moche (tampoco muchos, que esto de escribir cansa…).

 

Uno de mis mayores problemas que he encontrado cuando se trabaja con un patrón proxy (como Remoting, WCF,…) es que mucha gente no tiene claro dónde van sus objetos y cómo se pasan los valores por referencia, por valor, etc… Y se escucha mucho eso de…”He pasado mi clase loquesea y cuando actualizo en el servidor su valor, no me lo devuelve cambiado…”

 

Debo deciros que a mí también me costó lo suyo (uno que es lento…), pero bueno es bastante normal que esto ocurra, y también es normal que haya formas de evitar esto, por ejemplo en Remoting había que poner explícitamente el “ref” en la variable y el se encargaba de mantener el cambio y devolverlo.

 

Pues estuve buscando algo similar para utilizar en WCF, y encontré que si usamos el DataContractSerializer (el serializador que se usa por defecto en WCF), tenemos una opción parecida aunque un poco más compleja de elaborar, se hace a través de OperationBehavior y decoradores de métodos.

 

Generamos un DataContractSerializerOperationBehavior

Creamos una clase que herede de “DataContractSerializarOperationBehavior”, este es el behavior que luego aplicaremos a través del decorador.

public class SerializadorConReferencias : DataContractSerializerOperationBehavior
{
public SerializadorConReferencias(OperationDescription operationDescription)
: base(operationDescription)
{ }

public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return CreateDataContractSerializer(type, name, ns, knownTypes);
}

private static XmlObjectSerializer CreateDataContractSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return CreateDataContractSerializer(type, name, ns, knownTypes);
}

public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractSerializer(type, name, ns, knownTypes,
2147483647 /* En plan bruto... */,
this.IgnoreExtensionDataObject,
true /*Aquí esta el cambio*/,
this.DataContractSurrogate);
}
}

 

El caso está en el último CreateSerializer al que ponemos a true la propiedad de “PreserveReferences”. Usando este serializador en vez del de por defecto ya lo tendríamos, pero queda más elegante si usamos un decorador de métodos y así decidimos los métodos que queremos que lo usen y los que no.

 

Generamos un decorador que extienda IOperationBehavior

Para crear un decorador debemos heredar de Attribute y además en este caso debemos implementar el interfaz IOperationBehavior para poder aplicar este behavior en la operación (Método) que deseemos.

public class ConReferenciasDec : Attribute, IOperationBehavior
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}

public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
IOperationBehavior innerBehavior = new SerializadorConReferencias(description);
innerBehavior.ApplyClientBehavior(description, proxy);
}

public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
IOperationBehavior innerBehavior = new SerializadorConReferencias(description);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}

public void Validate(OperationDescription description)
{
}

#endregion

}

Aquí lo único a destacar es que tenemos que aplicar el behavior en el cliente y en el servidor. Ahora sólo nos quedaría utilizarlo.

public interface IService
{

[OperationContract]
[ConReferenciaDec]
void DoSomething(ComplexType algo);
}

 

Conclusiones

WCF mola mucho y se pueden hacer muchísimas cosas de una forma más o menos sencilla. En este caso en particular yo no estoy muy de acuerdo en utilizar este tipo de cosas, intento explicarme, tengo claro que la forma habitual de trabajar en .NET es que si pasas un tipo complejo a un método este siempre lo pasa por referencia, esto es cierto siempre y cuando ocupen el mismo espacio de memoria, que en el caso que nos ocupa NUNCA ocurre, porque para eso estamos a través de un proxy. Es preferible que tú método devuelva el mismo tipo y te lo pase cambiado y que los desarrolladores tengan muy clara la diferencia.

Aunque bueno para gustos los colores así que cada uno tendrá su opinión.

 

Hasta la próxima…

Mario Ropero…a medio gas…