WCF: ¿Exponiendo solo las propiedades de DataMember sin set?

Tengo una clase de servidor que pongo a disposición del lado del cliente a través de [DataContract]. Esta clase tiene un campo de solo lectura que me gustaría poner a disposición a través de una propiedad. Sin embargo, no puedo hacerlo porque no parece que se me permita agregar una propiedad [DataMember] sin tener ambos get y set.

Entonces, ¿hay alguna manera de tener una propiedad [DataMember] sin setter?

[DataContract] class SomeClass { private readonly int _id; public SomeClass() { .. } [DataMember] public int Id { get { return _id; } } [DataMember] public string SomeString { get; set; } } 

¿O la solución será usar el [DataMember] como el campo – (como, por ejemplo, se muestra aquí )? Intenté hacer esto también, pero no parece importarle que el campo sea de solo lectura …

Editar : ¿Es la única forma de hacer una propiedad de solo lectura pirateándola de esta manera? (No, no quiero hacer esto …)

 [DataMember] public int Id { get { return _id; } private set { /* NOOP */ } } 

En realidad, su clase “del lado del servidor” no estará “disponible” para el cliente.

Lo que ocurre es esto: según el contrato de datos, el cliente creará una nueva clase separada del esquema XML del servicio. ¡No puede usar la clase del lado del servidor per se!

Volverá a crear una nueva clase a partir de la definición de esquema XML, pero ese esquema no contiene ninguno de los elementos específicos de .NET, como la visibilidad o los modificadores de acceso: después de todo, es solo un esquema XML. La clase del lado del cliente se creará de tal manera que tenga la misma “huella” en el cable – por ejemplo, se serializa en el mismo formato XML, básicamente.

No se puede “transportar” el conocimiento específico de .NET sobre la clase a través de un servicio estándar basado en SOAP; después de todo, todo lo que se pasa son mensajes serializados , ¡no hay clases!

Verifique los “Cuatro principios de SOA” (definidos por Don Box of Microsoft):

  1. Los límites son explícitos
  2. Los servicios son autónomos
  3. Los servicios comparten esquema y contrato, no clase
  4. La compatibilidad se basa en la política

Vea el punto n. ° 3: los servicios comparten el esquema y el contrato, no la clase, usted solo comparte la interfaz y el esquema XML para el contrato de datos, eso es todo, sin clases .NET.

poner el atributo DataMember en un campo, no en la propiedad.

Recuerde, pensó, que WCF no conoce la encapsulación. La encapsulación es un término OOP, no un término SOA.

Dicho esto, recuerde que el campo será de solo lectura para las personas que usan su clase: cualquiera que use el servicio tendrá acceso completo al campo de su lado.

Tenía algunas propiedades en una clase en mi capa de servicio que quería pasar a Silverlight. No quería crear una clase completamente nueva.

No es realmente ‘recomendado’, pero este parecía el menor de los dos males que pasaba sobre la propiedad Total a Silverlight (únicamente para el enlace de datos visual).

 public class PricingSummary { public int TotalItemCount { get; set; } // doesnt ideally belong here but used by top bar when out of store area public decimal SubTotal { get; set; } public decimal? Taxes { get; set; } public decimal Discount { get; set; } public decimal? ShippingTotal { get; set; } public decimal Total { get { return + SubTotal + (ShippingTotal ?? 0) + (Taxes ?? 0) - Discount; } set { throw new ApplicationException("Cannot be set"); } } } 

Hay una manera de lograr esto. Pero ten en cuenta que viola directamente el siguiente principio citado en esta respuesta :

“3. Los servicios comparten esquema y contrato, no clase”.

Si esta violación no le concierne, esto es lo que hace:

  1. Mueva los contratos de servicio y datos a una biblioteca de clases separada (portátil). (Llamemos a este conjunto SomeService.Contracts ). Así es como definiría una clase inmutable [DataContract] :

     namespace SomeService.Contracts { [DataContract] public sealed class Foo { public Foo(int x) { this.x = x; } public int X { get { return x; } } [DataMember] // NB: applied to the backing field, not to the property! private readonly int x; } } 

    Tenga en cuenta que [DataMember] se aplica al campo de respaldo, y no a la propiedad de solo lectura correspondiente.

  2. SomeService.Web referencia al ensamblaje del contrato desde su proyecto de aplicación de servicio (llamaré a la mina SomeService.Web ) y desde sus proyectos de cliente (el mío se llama SomeService.Client ). Esto puede dar como resultado las siguientes dependencias de proyecto dentro de su solución:

    captura de pantalla que destaca las dependencias del proyecto en el Explorador de soluciones

  3. Luego, cuando agrega la referencia de servicio a su proyecto de cliente, asegúrese de tener habilitada la opción “tipos de reutilización” y asegúrese de que su ensamblado de contrato ( SomeService.Contracts ) se incluya en esto:

    captura de pantalla que resalta la configuración de referencia de servicio relevante

Voilà! Visual Studio, en lugar de generar un nuevo tipo de Foo partir del esquema WSDL del servicio, reutilizará el tipo de Foo inmutable de su ensamblado de contrato.

Una última advertencia: ya se ha desviado de los principios del servicio citados en esa otra respuesta . Pero trata de no alejarte más. Puede sentirse tentado de comenzar a agregar lógica (comercial) a sus clases de contrato de datos; no lo hagas Deben permanecer lo más cerca posible de los objetos tontos de transferencia de datos (DTO) que pueda administrar.

Definir el contrato de servicio (interfaz) antes de implementar el contrato utilizando la clase.