Określanie transferu danych w kontraktach usług

Program Windows Communication Foundation (WCF) może być uważany za infrastrukturę obsługi komunikatów. Operacje usługi mogą odbierać komunikaty, przetwarzać je i wysyłać do nich komunikaty. Komunikaty są opisywane przy użyciu kontraktów operacji. Rozważmy na przykład następującą umowę.

[ServiceContract]  
public interface IAirfareQuoteService  
{  
    [OperationContract]  
    float GetAirfare(string fromCity, string toCity);  
}  
<ServiceContract()>  
Public Interface IAirfareQuoteService  
  
    <OperationContract()>  
    Function GetAirfare(fromCity As String, toCity As String) As Double  
End Interface  

GetAirfare W tym miejscu operacja akceptuje komunikat z informacjami o fromCity i toCity, a następnie zwraca komunikat zawierający liczbę.

W tym temacie opisano różne sposoby, w jaki kontrakt operacji może opisywać komunikaty.

Opisywanie komunikatów przy użyciu parametrów

Najprostszym sposobem opisania komunikatu jest użycie listy parametrów i wartości zwracanej. W poprzednim przykładzie fromCity użyto parametrów i toCity string do opisania komunikatu żądania, a wartość zwracana zmiennoprzecinkowa została użyta do opisania wiadomości odpowiedzi. Jeśli sama wartość zwracana nie jest wystarczająca do opisania wiadomości odpowiedzi, mogą być używane parametry out. Na przykład następująca operacja zawiera fromCity i toCity w komunikacie żądania oraz liczbę wraz z walutą w komunikacie odpowiedzi:

[OperationContract]  
float GetAirfare(string fromCity, string toCity, out string currency);  
<OperationContract()>  
    Function GetAirfare(fromCity As String, toCity As String) As Double  

Ponadto możesz użyć parametrów referencyjnych, aby utworzyć część parametru zarówno żądania, jak i komunikatu odpowiedzi. Parametry muszą być typami, które można serializować (konwertowane na XML). Domyślnie program WCF używa składnika o nazwie DataContractSerializer klasa do wykonania tej konwersji. Większość typów pierwotnych (takich jak int, string, floati DateTime.) jest obsługiwana. Typy zdefiniowane przez użytkownika muszą zwykle mieć kontrakt danych. Aby uzyskać więcej informacji, zobacz Using Data Contracts (Korzystanie z kontraktów danych).

public interface IAirfareQuoteService  
{  
    [OperationContract]  
    float GetAirfare(Itinerary itinerary, DateTime date);  
  
    [DataContract]  
    public class Itinerary  
    {  
        [DataMember]  
        public string fromCity;  
        [DataMember]  
        public string toCity;  
   }  
}  
Public Interface IAirfareQuoteService  
    <OperationContract()>  
    GetAirfare(itinerary as Itinerary, date as DateTime) as Double  
  
    <DataContract()>  
    Class Itinerary  
  
        <DataMember()>  
        Public fromCity As String  
        <DataMember()>  
        Public toCity As String  
    End Class  
End Interface  

Czasami element DataContractSerializer nie jest odpowiedni do serializacji typów. Program WCF obsługuje alternatywny aparat serializacji , XmlSerializerktórego można również użyć do serializacji parametrów. Dzięki XmlSerializer temu można użyć większej kontroli nad wynikowym kodem XML przy użyciu atrybutów, takich jak XmlAttributeAttribute. Aby przełączyć się na używanie XmlSerializer elementu dla określonej operacji lub całej usługi, zastosuj XmlSerializerFormatAttribute atrybut do operacji lub usługi. Na przykład:

[ServiceContract]  
public interface IAirfareQuoteService  
{  
    [OperationContract]  
    [XmlSerializerFormat]  
    float GetAirfare(Itinerary itinerary, DateTime date);  
}  
public class Itinerary  
{  
    public string fromCity;  
    public string toCity;  
    [XmlAttribute]  
    public bool isFirstClass;  
}  
<ServiceContract()>  
Public Interface IAirfareQuoteService  
    <OperationContract()>  
    <XmlSerializerFormat>  
    GetAirfare(itinerary as Itinerary, date as DateTime) as Double  
  
End Interface  
  
Class Itinerary  
  
    Public fromCity As String  
    Public toCity As String  
    <XmlSerializerFormat()>  
    Public isFirstClass As Boolean  
End Class  

Aby uzyskać więcej informacji, zobacz Using the XmlSerializer Class (Używanie klasy XmlSerializer). Pamiętaj, że ręczne przełączenie do elementu XmlSerializer , jak pokazano poniżej, nie jest zalecane, chyba że masz określone powody, aby to zrobić zgodnie z opisem w tym temacie.

Aby odizolować nazwy parametrów platformy .NET od nazw kontraktów, możesz użyć atrybutu MessageParameterAttribute i użyć Name właściwości , aby ustawić nazwę kontraktu. Na przykład poniższy kontrakt operacji jest odpowiednikiem pierwszego przykładu w tym temacie.

[OperationContract]  
public float GetAirfare(  
    [MessageParameter(Name="fromCity")] string originCity,  
    [MessageParameter(Name="toCity")] string destinationCity);  
<OperationContract()>  
  Function GetAirfare(<MessageParameter(Name := "fromCity")> fromCity As String, <MessageParameter(Name := "toCity")> toCity As String) As Double  

Opisywanie pustych komunikatów

Pusty komunikat żądania można opisać bez parametrów wejściowych ani referencyjnych. Na przykład w języku C#:

[OperationContract]

public int GetCurrentTemperature();

Na przykład w Visual Basic:

<OperationContract()>

Function GetCurrentTemperature() as Integer

Pusta wiadomość odpowiedzi może być opisana przez zwrócenie void typu i brak parametrów wyjściowych ani referencyjnych. Na przykład w:

[OperationContract]  
public void SetTemperature(int temperature);  
<OperationContract()>  
Sub SetTemperature(temperature As Integer)  

Różni się to od operacji jednokierunkowej, takiej jak:

[OperationContract(IsOneWay=true)]  
public void SetLightbulbStatus(bool isOn);  
<OperationContract(IsOneWay:=True)>  
Sub SetLightbulbStatus(isOne As Boolean)  

Operacja SetTemperatureStatus zwraca pusty komunikat. Zamiast tego może zwrócić błąd, jeśli wystąpił problem podczas przetwarzania komunikatu wejściowego. Operacja SetLightbulbStatus nie zwraca nic. Nie ma możliwości przekazywania warunku błędu z tej operacji.

Opisywanie komunikatów przy użyciu kontraktów komunikatów

Możesz użyć pojedynczego typu do reprezentowania całego komunikatu. Chociaż w tym celu można użyć kontraktu danych, zalecanym sposobem jest użycie kontraktu komunikatów — pozwala to uniknąć niepotrzebnych poziomów zawijania w wynikowym pliku XML. Ponadto kontrakty komunikatów umożliwiają wykonywanie większej kontroli nad wynikowymi komunikatami. Na przykład możesz zdecydować, które informacje powinny znajdować się w treści wiadomości i które powinny znajdować się w nagłówkach wiadomości. W poniższym przykładzie pokazano użycie kontraktów komunikatów.

[ServiceContract]  
public interface IAirfareQuoteService  
{  
    [OperationContract]  
    GetAirfareResponse GetAirfare(GetAirfareRequest request);  
}  
  
[MessageContract]  
public class GetAirfareRequest  
{  
    [MessageHeader] public DateTime date;  
    [MessageBodyMember] public Itinerary itinerary;  
}  
  
[MessageContract]  
public class GetAirfareResponse  
{  
    [MessageBodyMember] public float airfare;  
    [MessageBodyMember] public string currency;  
}  
  
[DataContract]  
public class Itinerary  
{  
    [DataMember] public string fromCity;  
    [DataMember] public string toCity;  
}  
<ServiceContract()>  
Public Interface IAirfareQuoteService  
    <OperationContract()>  
    Function GetAirfare(request As GetAirfareRequest) As GetAirfareResponse  
End Interface  
  
<MessageContract()>  
Public Class GetAirfareRequest  
    <MessageHeader()>
    Public Property date as DateTime  
    <MessageBodyMember()>  
    Public Property itinerary As Itinerary  
End Class  
  
<MessageContract()>  
Public Class GetAirfareResponse  
    <MessageBodyMember()>  
    Public Property airfare As Double  
    <MessageBodyMember()> Public Property currency As String  
End Class  
  
<DataContract()>  
Public Class Itinerary  
    <DataMember()> Public Property fromCity As String  
    <DataMember()> Public Property toCity As String  
End Class  

Aby uzyskać więcej informacji, zobacz Using Message Contracts (Używanie kontraktów komunikatów).

W poprzednim przykładzie DataContractSerializer klasa jest nadal używana domyślnie. Klasa XmlSerializer może być również używana z kontraktami komunikatów. Aby to zrobić, zastosuj XmlSerializerFormatAttribute atrybut do operacji lub kontraktu i użyj typów zgodnych z klasą XmlSerializer w nagłówkach komunikatów i składowych treści.

Opisywanie komunikatów przy użyciu Strumienie

Innym sposobem opisania komunikatów w operacjach jest użycie Stream klasy lub jednej z jej klas pochodnych w kontrakcie operacji lub jako składowa treści kontraktu komunikatu (musi być jedynym elementem członkowskim w tym przypadku). W przypadku komunikatów przychodzących typ musi mieć wartość Stream— nie można używać klas pochodnych.

Zamiast wywoływania serializatora program WCF pobiera dane ze strumienia i umieszcza je bezpośrednio w wiadomości wychodzącej lub pobiera dane z komunikatu przychodzącego i umieszcza je bezpośrednio w strumieniu. W poniższym przykładzie pokazano użycie strumieni.

[OperationContract]  
public Stream DownloadFile(string fileName);  
<OperationContract()>  
Function DownloadFile(fileName As String) As String  

Nie można łączyć Stream i nie przesyłać strumieniowo danych w jednej treści komunikatu. Użyj kontraktu komunikatów, aby umieścić dodatkowe dane w nagłówkach komunikatów. W poniższym przykładzie pokazano nieprawidłowe użycie strumieni podczas definiowania kontraktu operacji.

//Incorrect:  
// [OperationContract]  
// public void UploadFile (string fileName, Stream fileData);  
'Incorrect:  
    '<OperationContract()>  
    Public Sub UploadFile(fileName As String, fileData As StreamingContext)  

W poniższym przykładzie pokazano prawidłowe użycie strumieni podczas definiowania kontraktu operacji.

[OperationContract]  
public void UploadFile (UploadFileMessage message);  
//code omitted  
[MessageContract]  
public class UploadFileMessage  
{  
    [MessageHeader] public string fileName;  
    [MessageBodyMember] public Stream fileData;  
}  
<OperationContract()>  
Public Sub UploadFile(fileName As String, fileData As StreamingContext)  
'Code Omitted  
<MessageContract()>  
Public Class UploadFileMessage  
   <MessageHeader()>  
    Public Property fileName As String  
    <MessageBodyMember()>  
    Public Property fileData As Stream  
End Class  

Aby uzyskać więcej informacji, zobacz Duże dane i przesyłanie strumieniowe.

Używanie klasy Message

Aby mieć pełną kontrolę programową nad komunikatami wysłanymi lub odebranych, możesz użyć Message klasy bezpośrednio, jak pokazano w poniższym przykładowym kodzie.

[OperationContract]  
public void LogMessage(Message m);  
<OperationContract()>  
Sub LogMessage(m As Message)  

Jest to zaawansowany scenariusz opisany szczegółowo w temacie Using the Message Class (Używanie klasy komunikatów).

Opisywanie komunikatów o błędach

Oprócz komunikatów, które są opisane przez wartość zwracaną i dane wyjściowe lub parametry odwołania, każda operacja, która nie jest jednokierunkowa, może zwrócić co najmniej dwa możliwe komunikaty: normalny komunikat odpowiedzi i komunikat o błędzie. Rozważmy następujący kontrakt operacji.

[OperationContract]  
float GetAirfare(string fromCity, string toCity, DateTime date);  
<OperationContract()>  
Function GetAirfare(fromCity As String, toCity As String, date as DateTime)  

Ta operacja może zwrócić normalny komunikat zawierający float liczbę lub komunikat o błędzie zawierający kod błędu i opis. Można to zrobić, zgłaszając FaultException element w implementacji usługi.

Możesz określić dodatkowe możliwe komunikaty o błędach przy użyciu atrybutu FaultContractAttribute . Dodatkowe błędy muszą być serializowalne przy użyciu DataContractSerializerelementu , jak pokazano w poniższym przykładowym kodzie.

[OperationContract]  
[FaultContract(typeof(ItineraryNotAvailableFault))]  
float GetAirfare(string fromCity, string toCity, DateTime date);  
  
//code omitted  
  
[DataContract]  
public class ItineraryNotAvailableFault  
{  
    [DataMember]  
    public bool IsAlternativeDateAvailable;  
  
    [DataMember]  
    public DateTime alternativeSuggestedDate;  
}  
<OperationContract()>  
<FaultContract(GetType(ItineraryNotAvailableFault))>  
Function GetAirfare(fromCity As String, toCity As String, date as DateTime) As Double  
  
'Code Omitted  
<DataContract()>  
Public Class  
  <DataMember()>  
  Public Property IsAlternativeDateAvailable As Boolean  
  <DataMember()>  
  Public Property alternativeSuggestedDate As DateTime  
End Class  

Te dodatkowe błędy można wygenerować, zgłaszając FaultException<TDetail> odpowiedni typ kontraktu danych. Aby uzyskać więcej informacji, zobacz Obsługa wyjątków i błędów.

Nie można użyć XmlSerializer klasy do opisania błędów. Nie XmlSerializerFormatAttribute ma to wpływu na umowy o błędy.

Używanie typów pochodnych

Możesz użyć typu podstawowego w operacji lub kontraktu komunikatu, a następnie użyć typu pochodnego podczas wywoływania operacji. W takim przypadku należy użyć atrybutu ServiceKnownTypeAttribute lub innego mechanizmu, aby umożliwić korzystanie z typów pochodnych. Rozważ następującą operację.

[OperationContract]  
public bool IsLibraryItemAvailable(LibraryItem item);  
<OperationContract()>  
    Function IsLibraryItemAvailable(item As LibraryItem) As Boolean  

Załóżmy, że dwa typy Book i Magazine, pochodzą z LibraryItemklasy . Aby użyć tych typów w IsLibraryItemAvailable operacji, możesz zmienić operację w następujący sposób:

[OperationContract]

[ServiceKnownType(typeof(Book))]

[ServiceKnownType(typeof(Magazine))]

public bool IsLibraryItemAvailable(LibraryItem item);

Alternatywnie możesz użyć atrybutu KnownTypeAttribute , gdy jest używana wartość domyślna DataContractSerializer , jak pokazano w poniższym przykładowym kodzie.

[OperationContract]  
public bool IsLibraryItemAvailable(LibraryItem item);  
  
// code omitted
  
[DataContract]  
[KnownType(typeof(Book))]  
[KnownType(typeof(Magazine))]  
public class LibraryItem  
{  
    //code omitted  
}  
<OperationContract()>  
Function IsLibraryItemAvailable(item As LibraryItem) As Boolean  
  
'Code Omitted  
<DataContract()>  
<KnownType(GetType(Book))>  
<KnownType(GetType(Magazine))>  
Public Class LibraryItem  
  'Code Omitted  
End Class  

Atrybutu XmlIncludeAttribute można użyć podczas korzystania z elementu XmlSerializer.

Atrybut można zastosować ServiceKnownTypeAttribute do operacji lub do całej usługi. Akceptuje typ lub nazwę metody do wywołania w celu uzyskania listy znanych typów, podobnie jak KnownTypeAttribute atrybut. Aby uzyskać więcej informacji, zobacz Znane typy kontraktów danych.

Określanie stylu i użycia

Podczas opisywania usług przy użyciu języka opisu usług sieci Web (WSDL) dwa najczęściej używane style to Document i remote procedure call (RPC). W stylu dokumentu cała treść komunikatu jest opisana przy użyciu schematu, a plik WSDL opisuje różne części treści komunikatów, odwołując się do elementów w tym schemacie. W stylu RPC język WSDL odwołuje się do typu schematu dla każdej części komunikatu, a nie elementu. W niektórych przypadkach należy ręcznie wybrać jeden z tych stylów. Można to zrobić, stosując atrybut i ustawiając DataContractFormatAttributeStyle właściwość (gdy DataContractSerializer element jest używany) lub ustawiając Style atrybut XmlSerializerFormatAttribute (w przypadku używania XmlSerializerobiektu ).

Ponadto program XmlSerializer obsługuje dwie formy serializowanego kodu XML: Literal i Encoded. Literal jest najczęściej akceptowanym formularzem i jest jedyną formą obsługiwaną DataContractSerializer przez program . Encoded jest starszym formularzem opisanym w sekcji 5 specyfikacji protokołu SOAP i nie jest zalecany w przypadku nowych usług. Aby przełączyć się do Encoded trybu, ustaw Use właściwość atrybutu XmlSerializerFormatAttribute na Encodedwartość .

W większości przypadków nie należy zmieniać ustawień domyślnych Style właściwości i Use .

Kontrolowanie procesu serializacji

Możesz wykonać wiele czynności, aby dostosować sposób serializacji danych.

Zmienianie Ustawienia serializacji serwera

Gdy wartość domyślna DataContractSerializer jest używana, możesz kontrolować niektóre aspekty procesu serializacji w usłudze, stosując ServiceBehaviorAttribute atrybut do usługi. W szczególności można użyć MaxItemsInObjectGraph właściwości , aby ustawić limit przydziału, który ogranicza maksymalną liczbę obiektów DataContractSerializer deserializuje. Możesz użyć IgnoreExtensionDataObject właściwości , aby wyłączyć funkcję przechowywania wersji w obie strony. Aby uzyskać więcej informacji na temat limitów przydziału, zobacz Zagadnienia dotyczące zabezpieczeń danych. Aby uzyskać więcej informacji na temat zaokrąglania, zobacz Kontrakty danych zgodne z przekazywaniem.

[ServiceBehavior(MaxItemsInObjectGraph=100000)]  
public class MyDataService:IDataService  
{  
    public DataPoint[] GetData()  
    {  
       // Implementation omitted  
    }  
}  
<ServiceBehavior(MaxItemsInObjectGraph:=100000)>  
Public Class MyDataService Implements IDataService  
  
    Function GetData() As DataPoint()  
         ‘ Implementation omitted  
    End Function  
End Interface  

Zachowania serializacji

W programie WCF są dostępne dwa zachowania, a DataContractSerializerOperationBehavior i XmlSerializerOperationBehavior , które są automatycznie podłączane w zależności od tego, który serializator jest używany do określonej operacji. Ponieważ te zachowania są stosowane automatycznie, zwykle nie trzeba ich znać.

Jednak element DataContractSerializerOperationBehavior ma MaxItemsInObjectGraphwłaściwości , IgnoreExtensionDataObjecti DataContractSurrogate , których można użyć do dostosowania procesu serializacji. Pierwsze dwie właściwości mają takie samo znaczenie, jak omówiono w poprzedniej sekcji. Za pomocą DataContractSurrogate właściwości można włączyć zastępcze kontrakty danych, które są zaawansowanym mechanizmem dostosowywania i rozszerzania procesu serializacji. Aby uzyskać więcej informacji, zobacz Data Contract Surrogates (Zastępcze kontrakty danych).

Możesz użyć polecenia DataContractSerializerOperationBehavior , aby dostosować serializacji klienta i serwera. W poniższym przykładzie pokazano, jak zwiększyć MaxItemsInObjectGraph limit przydziału na kliencie.

ChannelFactory<IDataService> factory = new ChannelFactory<IDataService>(binding, address);  
foreach (OperationDescription op in factory.Endpoint.Contract.Operations)  
{  
    DataContractSerializerOperationBehavior dataContractBehavior =  
                op.Behaviors.Find<DataContractSerializerOperationBehavior>()  
                as DataContractSerializerOperationBehavior;  
    if (dataContractBehavior != null)  
    {  
        dataContractBehavior.MaxItemsInObjectGraph = 100000;  
    }  
}  
IDataService client = factory.CreateChannel();  
Dim factory As ChannelFactory(Of IDataService) = New ChannelFactory(Of IDataService)(binding, address)  
For Each op As OperationDescription In factory.Endpoint.Contract.Operations  
        Dim dataContractBehavior As DataContractSerializerOperationBehavior = op.Behaviors.Find(Of DataContractSerializerOperationBehavior)()  
        If dataContractBehavior IsNot Nothing Then  
            dataContractBehavior.MaxItemsInObjectGraph = 100000  
        End If  
     Next  
    Dim client As IDataService = factory.CreateChannel  

Poniższy kod jest odpowiednikiem w usłudze, w przypadku własnego przypadku:

ServiceHost serviceHost = new ServiceHost(typeof(IDataService))  
foreach (ServiceEndpoint ep in serviceHost.Description.Endpoints)  
{  
foreach (OperationDescription op in ep.Contract.Operations)  
{  
        DataContractSerializerOperationBehavior dataContractBehavior =  
           op.Behaviors.Find<DataContractSerializerOperationBehavior>()  
                as DataContractSerializerOperationBehavior;  
        if (dataContractBehavior != null)  
        {  
            dataContractBehavior.MaxItemsInObjectGraph = 100000;  
        }  
}  
}  
serviceHost.Open();  
Dim serviceHost As ServiceHost = New ServiceHost(GetType(IDataService))  
        For Each ep As ServiceEndpoint In serviceHost.Description.Endpoints  
            For Each op As OperationDescription In ep.Contract.Operations  
                Dim dataContractBehavior As DataContractSerializerOperationBehavior = op.Behaviors.Find(Of DataContractSerializerOperationBehavior)()  
  
                If dataContractBehavior IsNot Nothing Then  
                    dataContractBehavior.MaxItemsInObjectGraph = 100000  
                End If  
            Next  
        Next  
        serviceHost.Open()  

W przypadku hostowanym w Sieci Web należy utworzyć nową ServiceHost klasę pochodną i użyć fabryki hostów usług, aby ją podłączyć.

Kontrolowanie Ustawienia serializacji w konfiguracji

Element MaxItemsInObjectGraph i IgnoreExtensionDataObject można kontrolować za pomocą konfiguracji przy użyciu punktu końcowego dataContractSerializer lub zachowania usługi, jak pokazano w poniższym przykładzie.

<configuration>  
    <system.serviceModel>  
        <behaviors>  
            <endpointBehaviors>  
                <behavior name="LargeQuotaBehavior">  
                    <dataContractSerializer  
                      maxItemsInObjectGraph="100000" />  
                </behavior>  
            </endpointBehaviors>  
        </behaviors>  
        <client>  
            <endpoint address="http://example.com/myservice"  
                  behaviorConfiguration="LargeQuotaBehavior"  
                binding="basicHttpBinding" bindingConfiguration=""
                            contract="IDataService"  
                name="" />  
        </client>  
    </system.serviceModel>  
</configuration>  

Serializacja typu współużytkowanego, zachowywanie grafu obiektów i niestandardowe serializatory

Serializuje DataContractSerializer przy użyciu nazw kontraktów danych, a nie nazw typów platformy .NET. Jest to zgodne z założeniami architektury zorientowanej na usługę i zapewnia dużą elastyczność — typy platformy .NET mogą ulec zmianie bez wpływu na kontrakt przewodowy. W rzadkich przypadkach można serializować rzeczywiste nazwy typów platformy .NET, wprowadzając w ten sposób ścisłe sprzężenie między klientem a serwerem, podobnie jak technologia komunikacji wirtualnej programu .NET Framework. Nie jest to zalecana praktyka, z wyjątkiem rzadkich przypadków, które zwykle występują podczas migracji do programu WCF z komunikacji wirtualnej programu .NET Framework. W takim przypadku należy użyć NetDataContractSerializer klasy zamiast DataContractSerializer klasy .

Zwykle DataContractSerializer serializuje wykresy obiektów jako drzewa obiektów. Oznacza to, że jeśli ten sam obiekt jest określany więcej niż raz, jest serializowany więcej niż raz. Rozważmy na przykład PurchaseOrder wystąpienie z dwoma polami typu Address o nazwie billTo i shipTo. Jeśli oba pola są ustawione na to samo wystąpienie adresu, istnieją dwa identyczne wystąpienia adresów po serializacji i deserializacji. Dzieje się tak, ponieważ nie ma standardowego sposobu współdziałania w celu reprezentowania grafów obiektów w formacie XML (z wyjątkiem starszego standardu zakodowanego przez protokół SOAP dostępnego w XmlSerializersystemie , zgodnie z opisem w poprzedniej sekcji i StyleUse). Serializowanie grafów obiektów, ponieważ drzewa mają pewne wady, na przykład wykresy z odwołaniami okrągłymi nie mogą być serializowane. Czasami konieczne jest przełączenie się na serializacji grafu obiektów true, mimo że nie jest to możliwe do współdziałania. Można to zrobić przy użyciu DataContractSerializer konstrukcji z parametrem ustawionym preserveObjectReferences na truewartość .

Czasami wbudowane serializatory nie są wystarczające dla danego scenariusza. W większości przypadków nadal można użyć XmlObjectSerializer abstrakcji, z której pochodzą zarówno klasy DataContractSerializer , jak i NetDataContractSerializer .

Poprzednie trzy przypadki (zachowywanie typu platformy.NET, zachowywanie grafu obiektów i całkowicie niestandardowa XmlObjectSerializerserializacja) wymagają podłączenia niestandardowego serializatora. W tym celu wykonaj następujące czynności:

  1. Napisz własne zachowanie pochodzące z elementu DataContractSerializerOperationBehavior.

  2. Zastąp dwie CreateSerializer metody, aby zwrócić własny serializator ( NetDataContractSerializerz wartością , DataContractSerializer ustawioną preserveObjectReferences na true, lub własną niestandardową XmlObjectSerializer).

  3. Przed otwarciem hosta usługi lub utworzeniem kanału klienta usuń istniejące DataContractSerializerOperationBehavior zachowanie i podłącz niestandardową klasę pochodną utworzoną w poprzednich krokach.

Aby uzyskać więcej informacji na temat zaawansowanych pojęć serializacji, zobacz Serializacja i Deserializacja.

Zobacz też