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()>
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()>
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()>
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>
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()>
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(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()>
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
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.
C#
[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.
<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 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.
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()>
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(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.
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()>
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()>
Function IsLibraryItemAvailable(item As LibraryItem) As Boolean
'Code Omitted
<DataContract()>
<KnownType(GetType(Book))>
<KnownType(GetType(Magazine))>
Public Class LibraryItem
'Code Omitted
End Class
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 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.
C#
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:
C#
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.
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ść .
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:
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.
Dołącz do serii meetup, aby tworzyć skalowalne rozwiązania sztucznej inteligencji oparte na rzeczywistych przypadkach użycia z innymi deweloperami i ekspertami.
Dowiedz się więcej o klasie Message, która jest podstawowa dla usługi WCF. Należy programować przy użyciu klasy Message bezpośrednio tylko w niektórych zaawansowanych scenariuszach.
Dowiedz się więcej o narzędziu XmlSerializer, którego program WCF używa do serializacji danych w aplikacji do kodu XML przesyłanego między klientami i usługami.
Dowiedz się, jak za pomocą atrybutów kontraktu komunikatów utworzyć kontrakt komunikatów określający strukturę komunikatu PROTOKOŁU SOAP w usłudze WFC.
Dowiedz się więcej na temat: ServiceModel Attributes and ServiceDescription Reference (Atrybuty usługi ServiceModel i dokumentacja klasy ServiceDescription)