2009-12-09 12 views
56

Ho una classe lato server che rendo disponibile sul lato client attraverso un [DataContract]. Questa classe 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 possibile aggiungere una proprietà [DataMember] senza averli entrambi impostati.WCF: esposizione diretta delle proprietà DataMember senza set?

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 sarà la soluzione è utilizzare il [DataMember] come il campo - (come per esempio mostrato here)? 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 fare questo ...)

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

La tua idea di un setter NOOP farebbe sì che la proprietà non venga trasferita correttamente se stai usando WCF sul lato client: nella deserializzazione di 'DataContract', la classe viene istanziata senza chiamare alcun costruttore, e i setter di qualsiasi' Le proprietà DataMember vengono quindi passate qualsiasi cosa sia stata restituita dal getter di quella proprietà alla serializzazione. Quindi il tuo setter NOOP scarterebbe la proprietà, lasciando il suo valore come predefinito alla deserializzazione. Invece, scrivi un vero setter privato funzionante, oppure contrassegna la variabile di supporto come 'DataMember' invece di contrassegnare la proprietà. –

risposta

48

La tua classe "server-side" non sarà "resa disponibile" al client, davvero.

Ciò che accade è questo: basato sul contratto dati, il client creerà una nuova classe separata dallo schema XML del servizio. È impossibile utilizzare la classe lato server di per sé!

Ricreerà una nuova classe dalla definizione dello schema XML, ma quello schema non contiene nessuno dei particolari specifici di .NET come la visibilità oi modificatori di accesso - dopotutto è solo uno schema XML. La classe lato client verrà creata in modo tale da avere lo stesso "ingombro" sul filo - ad es. si serializza nello stesso formato XML, in pratica.

È non "trasporto" know-how specifico .NET sulla classe attraverso un servizio basato su SOAP barattolo standard - dopo tutto, tutto quello che stai passando intorno sono messaggi serializzati - non classi!

Controllare i "quattro principi della SOA" (definito da Don Box di Microsoft):

  1. I confini sono espliciti
  2. I servizi sono autonome
  3. Servizi schema condividere e contratto, non di classe
  4. La compatibilità si basa sulla politica

Vedere punto n. 3 - schema di condivisione servizi e contratto, non classe: l'interfaccia e lo schema XML per il contratto di dati sono sempre e solo gli unici, nessuna classe .NET.

+1

Questo lo spiega molto bene. Grazie per un chiarimento! – stiank81

+0

felice di poterti aiutare! –

+28

Questa informazione è buona, ma non penso che risponda direttamente alla domanda. – atoumey

-3

Definire l'appalto di servizi (Interface) Prima di implementare il contratto utilizzando la classe.

+0

Cosa intendi? DataMember è nella classe DTO disponibile tramite DataContract. Ho anche un contratto di servizio, ma qui non è molto correlato - vero? – stiank81

+1

è un datacontract, non un contratto di servizio –

10

inserire l'attributo DataMember su un campo e non la proprietà.

Ricordare 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 classe - chiunque utilizzi il servizio avrà pieno accesso al campo dalla loro parte.

+0

Ah, sì, come ho notato, ho provato a impostare il campo come DataMember, ma non è stato esposto come readonly sul lato client. Ma non c'è modo di renderlo di sola lettura sul lato client allora? – stiank81

+4

no. READONLY è un termine C#, non un SOA. Grazie, non puoi rendere parte di XML in sola lettura –

+0

. Sembra che sia l'opzione migliore se non quella di creare una classe specifica per i dati ... e per le piccole app, è solo un eccesso di codice. – ChrisG

7

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

Non proprio "consigliato", ma questo sembrava il minore dei due mali da passare sopra la proprietà Total a silverlight (solo per il mapping visivo).

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

Il problema che ho riscontrato con questa soluzione è che, poiché non è possibile utilizzare ISerializable e DataContract insieme, DataContract definisce anche la propria serializzazione. Se provassi a serializzare questo oggetto, genererebbe un'eccezione durante la deserializzazione. Quindi, per hackerare il no-op sembra l'unica opzione, rispetto a lanciare un'eccezione. –

+0

In questa situazione, l'implementazione di "Total" come metodo di estensione funziona bene (ho capito che i metodi di estensione * potrebbero * non essere stati in circolazione quando hai scritto questo post). –

+0

@PaulSuart Ho bisogno che sia una proprietà per il databinding quindi deve essere una proprietà e non esiste nulla come le proprietà dell'estensione :-(ma sì tendo a dimenticare i metodi di estensione eccetto quando estendo le librerie –

4

C'è un modo per raggiungere questo obiettivo. Ma sappiate che viola direttamente il seguente principio citato in this answer:

"3. Servizi condivisione dello schema di contratto e, non di classe."

Se questa violazione non riguarda voi, questo è ciò che si fa:

  1. spostare il servizio di dati e di contratti in un (portatile) libreria di classi separato. (Chiamiamo questa assemblea SomeService.Contracts.) Questo è il modo che ci si definisce un immutabile [DataContract] classe:

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

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

  2. Fare riferimento all'assembly del contratto dal progetto di applicazione di servizio (chiamerò il mio SomeService.Web) e dai progetti del client (il mio è chiamato SomeService.Client). Questo potrebbe comportare le seguenti dipendenze del progetto all'interno della vostra soluzione:

    screenshot highlighting the project dependencies in Solution Explorer

  3. successivo, quando si aggiunge il riferimento al servizio al progetto client, assicurarsi di avere l'opzione "tipi riuso" abilitati, e garantire che l'assembly del contratto (SomeService.Contracts) sarà incluso in questo:

    screenshot highlighting the relevant service reference setting

Voilà! Visual Studio, invece di generare un nuovo tipo dallo schema WSDL del servizio, riutilizzerà il tipo immodificabile Foo dall'assembly del contratto.

Un ultimo avviso: hai già deviato dai principi di servizio citati in that other answer. 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 possibile agli stupidi oggetti di trasferimento dati (DTO) che puoi gestire.