WCF: esposizione delle proprietà DataMember di sola lettura senza set?

Ho una class lato server che rendo disponibile sul lato client attraverso un [DataContract]. Questa class ha un campo di sola lettura che mi piacerebbe rendere disponibile attraverso una proprietà. Tuttavia, non sono in grado di farlo perché non mi sembra ansible aggiungere una proprietà [DataMember] senza averli entrambi impostati.

Quindi, c’è un modo per avere una proprietà [DataMember] senza 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 soluzione utilizzerà il [DataMember] come campo – (come ad esempio mostrato qui )? Ho provato anche a farlo, ma non sembra che il campo sia di mia competenza ..?

Modifica : è l’unico modo per creare una proprietà readonly hackerandola in questo modo? (no – non voglio farlo …)

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

La tua class “server-side” non sarà “resa disponibile” al cliente, davvero.

Quello che succede è questo: basato sul contratto dati, il client creerà una nuova class separata dallo schema XML del servizio. Non può usare la class lato server di per sé!

Ricreerà una nuova class dalla definizione dello schema XML, ma quello schema non contiene nessuno dei particolari specifici di .NET come la visibilità o i modificatori di accesso – dopotutto è solo uno schema XML. La class lato client verrà creata in modo tale da avere lo stesso “ingombro” sul filo, ad esempio la serializzazione nello stesso formato XML, in pratica.

Non è ansible “trasportare” il know-how specifico di .NET sulla class attraverso un servizio basato su SOAP standard – dopotutto, tutto ciò che si passa in giro sono messaggi serializzati – nessuna class!

Controlla i “Quattro principi di SOA” (definiti da Don Box di Microsoft):

  1. I confini sono espliciti
  2. I servizi sono autonomi
  3. I servizi condividono schema e contratto, non class
  4. La compatibilità si basa sulla politica

Vedere il punto n. 3: i servizi condividono lo schema e il contratto, non la class: condividete sempre l’interfaccia e lo schema XML per il contratto dati, tutto qui, senza classi .NET.

metti l’attributo DataMember su un campo e non la proprietà.

Ricorda il pensiero, che WCF non conosce l’incapsulamento. L’incapsulamento è un termine OOP, non un termine SOA.

Detto questo, ricorda che il campo sarà in sola lettura per le persone che usano la tua class – chiunque utilizzi il servizio avrà pieno accesso al campo dalla loro parte.

Avevo alcune proprietà in una class nel mio livello di servizio che volevo passare a Silverlight. Non volevo creare una nuova class.

Non proprio “raccomandato”, ma questo sembrava il minore dei due mali da passare sopra la proprietà Total a Silverlight (solo per il visual database).

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

C’è un modo per raggiungere questo objective. Ma ti avverto che viola direttamente il seguente principio citato in questa risposta :

“3. I servizi condividono schema e contratto, non class.”

Se questa violazione non ti riguarda, questo è quello che fai:

  1. Spostare i contratti di servizio e dati in una libreria di classi (portatile) separata. (Chiamiamo questo assembly SomeService.Contracts ). Ecco come definiresti una class [DataContract] immutabile:

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

    Si noti che [DataMember] viene applicato al campo di supporto e non alla corrispondente proprietà di sola lettura.

  2. SomeService.Web riferimento all’assembly del contratto sia dal tuo progetto di applicazione di servizio (chiamerò il mio SomeService.Web ) che dai tuoi progetti client (il mio è chiamato SomeService.Client ). Ciò potrebbe comportare le seguenti dipendenze del progetto all’interno della soluzione:

    screenshot che evidenzia le dipendenze del progetto in Solution Explorer

  3. Successivamente, quando aggiungi il riferimento al servizio al progetto client, assicurati di avere l’opzione “riutilizzare i tipi” abilitata e assicurati che l’assembly del tuo contratto ( SomeService.Contracts ) sia incluso in questo:

    schermata che evidenzia l'impostazione di riferimento del servizio pertinente

Ecco! Visual Studio, invece di generare un nuovo tipo Foo dallo schema WSDL del servizio, riutilizzerà il tipo Foo immutabile dall’assembly del contratto.

Un ultimo avvertimento: ti sei già allontanato dai principi di servizio citati in quell’altra risposta . Ma cerca di non allontanarti ulteriormente. Potresti essere tentato di iniziare ad aggiungere logica (di business) alle tue classi di contratto dati; non lo fanno. Dovrebbero rimanere il più vicino ansible agli stupidi oggetti di trasferimento dati (DTO) che puoi gestire.

Definire il contratto di servizio (interfaccia) Prima di implementare il contratto utilizzando la class.