Určování přenosu dat v kontraktech služby
Windows Communication Foundation (WCF) si můžete představit jako infrastrukturu zasílání zpráv. Operace služby mohou přijímat zprávy, zpracovávat je a odesílat zprávy. Zprávy jsou popsány pomocí kontraktů operací. Představte si například následující kontrakt.
[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
V tomto případě GetAirfare
operace přijme zprávu s informacemi o fromCity
a toCity
pak vrátí zprávu obsahující číslo.
Toto téma vysvětluje různé způsoby, kterými může kontrakt operace popisovat zprávy.
Popis zpráv pomocí parametrů
Nejjednodušší způsob, jak popsat zprávu, je použít seznam parametrů a návratovou hodnotu. V předchozím příkladu fromCity
se k popisu zprávy požadavku použily parametry řetězce toCity
a návratová hodnota float byla použita k popisu zprávy odpovědi. Pokud samotná návratová hodnota nestačí k popisu zprávy odpovědi, můžou se použít výstupní parametry. Například následující operace má fromCity
a ve zprávě požadavku a toCity
číslo společně s měnou ve zprávě odpovědi:
[OperationContract]
float GetAirfare(string fromCity, string toCity, out string currency);
<OperationContract()>
Function GetAirfare(fromCity As String, toCity As String) As Double
Kromě toho můžete použít referenční parametry k vytvoření části požadavku i zprávy odpovědi. Parametry musí být typu, které lze serializovat (převést na XML). Wcf ve výchozím nastavení používá k provedení tohoto převodu komponentu, která DataContractSerializer se nazývá třída. Podporuje se většina primitivních typů (například int
, string
, float
a DateTime
.). Uživatelem definované typy musí normálně obsahovat kontrakt dat. Další informace najdete v tématu Použití kontraktů dat.
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
DataContractSerializer
Občas není adekvátní k serializaci typů. WCF podporuje alternativní serializační modul, který XmlSerializermůžete použít také k serializaci parametrů. Umožňuje XmlSerializer používat větší kontrolu nad výsledným XML pomocí atributů, jako je například XmlAttributeAttribute
. Pokud chcete přepnout na použití XmlSerializer konkrétní operace nebo celé služby, použijte XmlSerializerFormatAttribute atribut na operaci nebo službu. Příklad:
[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
Další informace naleznete v tématu Použití Třídy XmlSerializer. Mějte na paměti, že ruční přepnutí na XmlSerializer zde uvedené není doporučeno, pokud nemáte konkrétní důvody k tomu, jak je popsáno v tomto tématu.
Chcete-li izolovat názvy parametrů .NET od názvů kontraktů, můžete použít MessageParameterAttribute atribut a použít Name
vlastnost k nastavení názvu kontraktu. Například následující kontrakt operace je ekvivalentní prvnímu příkladu v tomto tématu.
[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
Popis prázdných zpráv
Prázdnou zprávu požadavku je možné popsat bez vstupních nebo referenčních parametrů. Například v jazyce C#:
[OperationContract]
public int GetCurrentTemperature();
Například v jazyce Visual Basic:
<OperationContract()>
Function GetCurrentTemperature() as Integer
Prázdnou zprávu odpovědi můžete popsat tak, že budete mít návratový void
typ a žádný výstupní nebo referenční parametry. Například v:
[OperationContract]
public void SetTemperature(int temperature);
<OperationContract()>
Sub SetTemperature(temperature As Integer)
To se liší od jednosměrné operace, například:
[OperationContract(IsOneWay=true)]
public void SetLightbulbStatus(bool isOn);
<OperationContract(IsOneWay:=True)>
Sub SetLightbulbStatus(isOne As Boolean)
Operace SetTemperatureStatus
vrátí prázdnou zprávu. Místo toho může vrátit chybu, pokud dojde k problému se zpracováním vstupní zprávy. Operace SetLightbulbStatus
nevrací nic. Z této operace neexistuje způsob, jak komunikovat stav selhání.
Popis zpráv pomocí kontraktů zpráv
K reprezentaci celé zprávy můžete použít jeden typ. I když je možné k tomuto účelu použít datový kontrakt, doporučeným způsobem je použít kontrakt zprávy – zabráníte tak zbytečným úrovním zabalení ve výsledném XML. Kontrakty zpráv navíc umožňují vykonávat větší kontrolu nad výslednými zprávami. Můžete se například rozhodnout, které části informací by měly být v textu zprávy a které by měly být v záhlaví zprávy. Následující příklad ukazuje použití kontraktů zpráv.
[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
Další informace naleznete v tématu Použití kontraktů zpráv.
V předchozím příkladu DataContractSerializer se třída stále používá ve výchozím nastavení. Třídu XmlSerializer lze také použít s kontrakty zpráv. Uděláte to tak, že použijete XmlSerializerFormatAttribute atribut na operaci nebo kontrakt a použijete typy kompatibilní s XmlSerializer třídou v záhlavích zpráv a členů textu.
Popis zpráv pomocí Toky
Dalším způsobem, jak popsat zprávy v operacích, je použít Stream třídu nebo jednu z jejích odvozených tříd v kontraktu operace nebo jako člen těla smlouvy zprávy (v tomto případě musí být jediným členem). U příchozích zpráv musí být Stream
typ – nelze použít odvozené třídy.
Namísto vyvolání serializátoru WCF načte data z datového proudu a umístí je přímo do odchozí zprávy nebo načte data z příchozí zprávy a vloží je přímo do datového proudu. Následující ukázka ukazuje použití datových proudů.
[OperationContract]
public Stream DownloadFile(string fileName);
<OperationContract()>
Function DownloadFile(fileName As String) As String
Data, která nejsou streamovaná, nelze kombinovat Stream
v jednom textu zprávy. Použijte kontrakt zprávy k vložení dodatečných dat do záhlaví zpráv. Následující příklad ukazuje nesprávné použití datových proudů při definování kontraktu operace.
//Incorrect:
// [OperationContract]
// public void UploadFile (string fileName, Stream fileData);
'Incorrect:
'<OperationContract()>
Public Sub UploadFile(fileName As String, fileData As StreamingContext)
Následující ukázka ukazuje správné použití datových proudů při definování kontraktu operace.
[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
Další informace najdete v tématu Velké objemy dat a streamování.
Používání třídy Message
Pokud chcete mít úplnou programovou kontrolu nad zprávami odeslaných nebo přijatých, můžete třídu použít Message přímo, jak je znázorněno v následujícím ukázkovém kódu.
[OperationContract]
public void LogMessage(Message m);
<OperationContract()>
Sub LogMessage(m As Message)
Jedná se o pokročilý scénář, který je podrobně popsán v použití třídy zprávy.
Popis chybových zpráv
Kromě zpráv, které jsou popsány návratovou hodnotou a výstupními nebo referenčními parametry, může jakákoli operace, která není jednosměrná, vrátit alespoň dvě možné zprávy: normální zprávu odpovědi a chybovou zprávu. Zvažte následující kontrakt operace.
[OperationContract]
float GetAirfare(string fromCity, string toCity, DateTime date);
<OperationContract()>
Function GetAirfare(fromCity As String, toCity As String, date as DateTime)
Tato operace může buď vrátit normální zprávu obsahující float
číslo, nebo chybová zpráva obsahující kód chyby a popis. Toho můžete dosáhnout vyvoláním FaultException implementace služby.
Pomocí atributu FaultContractAttribute můžete zadat další možné chybové zprávy. Další chyby musí být serializovatelné pomocí DataContractSerializer, jak je znázorněno v následujícím příkladu kódu.
[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
Tyto další chyby lze vygenerovat vyvoláním FaultException<TDetail> příslušného typu datového kontraktu. Další informace naleznete v tématu Zpracování výjimek a chyb.
Třídu nelze použít XmlSerializer k popisu chyb. Nemá XmlSerializerFormatAttribute žádný vliv na kontrakty chyb.
Použití odvozených typů
Můžete použít základní typ v operaci nebo kontraktu zprávy a potom použít odvozený typ při skutečné vyvolání operace. V tomto případě musíte použít ServiceKnownTypeAttribute buď atribut, nebo nějaký alternativní mechanismus, aby bylo možné používat odvozené typy. Zvažte následující operaci.
[OperationContract]
public bool IsLibraryItemAvailable(LibraryItem item);
<OperationContract()>
Function IsLibraryItemAvailable(item As LibraryItem) As Boolean
Předpokládejme, že dva typy a Book
Magazine
, odvozené od LibraryItem
. Pokud chcete v IsLibraryItemAvailable
operaci použít tyto typy, můžete operaci změnit následujícím způsobem:
[OperationContract]
[ServiceKnownType(typeof(Book))]
[ServiceKnownType(typeof(Magazine))]
public bool IsLibraryItemAvailable(LibraryItem item);
Případně můžete použít KnownTypeAttribute atribut, pokud se používá výchozí DataContractSerializer hodnota, jak je znázorněno v následujícím ukázkovém kódu.
[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
Atribut můžete použít XmlIncludeAttribute při použití .XmlSerializer
Atribut můžete použít ServiceKnownTypeAttribute na operaci nebo na celou službu. Přijímá buď typ, nebo název metody volání získat seznam známých typů, stejně jako KnownTypeAttribute atribut. Další informace naleznete v tématu Známé typy kontraktů dat.
Zadání stylu a použití
Při popisu služeb pomocí jazyka WSDL (Web Services Description Language) jsou dvěma běžně používanými styly volání procedur dokument a vzdálené volání procedur (RPC). Ve stylu dokumentu je celý text zprávy popsán pomocí schématu a WSDL popisuje různé části textu zprávy odkazem na prvky v tomto schématu. Ve stylu RPC odkazuje WSDL na typ schématu pro každou část zprávy, nikoli na prvek. V některých případech musíte některý z těchto stylů vybrat ručně. Můžete to provést použitím atributu DataContractFormatAttribute a nastavením Style
vlastnosti (pokud DataContractSerializer se používá) nebo nastavením Style
atributu XmlSerializerFormatAttribute (při použití XmlSerializer).
Kromě toho XmlSerializer podporuje dvě formy serializované XML: Literal
a Encoded
. Literal
je nejčastěji přijímaný formulář a je jediným formulářem, který DataContractSerializer podporuje. Encoded
je starší verze, která je popsaná v části 5 specifikace SOAP a nedoporučuje se pro nové služby. Chcete-li přepnout do Encoded
režimu, nastavte Use
vlastnost atributu XmlSerializerFormatAttribute na Encoded
.
Ve většině případů byste neměli měnit výchozí nastavení pro vlastnosti Style
a Use
vlastnosti.
Řízení procesu serializace
Pokud chcete přizpůsobit způsob serializace dat, můžete provést řadu věcí.
Změna Nastavení serializace serveru
Pokud se používá výchozí nastavení DataContractSerializer , můžete řídit některé aspekty procesu serializace ve službě použitím ServiceBehaviorAttribute atributu na službu. Konkrétně můžete vlastnost použít MaxItemsInObjectGraph
k nastavení kvóty, která omezuje maximální počet objektů DataContractSerializer deserializes. Tuto vlastnost můžete použít IgnoreExtensionDataObject
k vypnutí funkce správy verzí s odezvou. Další informace o kvótách najdete v tématu Důležité informace o zabezpečení dat. Další informace o odezvě naleznete v tématu Kontrakty dat kompatibilní s předáváním.
[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
Chování serializace
Ve WCF jsou k dispozici dvě chování, DataContractSerializerOperationBehavior a to, XmlSerializerOperationBehavior které jsou automaticky zapojeny v závislosti na tom, který serializátor se používá pro konkrétní operaci. Vzhledem k tomu, že se tato chování použijí automaticky, obvykle o nich nemusíte vědět.
Nicméně, DataContractSerializerOperationBehavior
má MaxItemsInObjectGraph
, IgnoreExtensionDataObject
a DataContractSurrogate
vlastnosti, které můžete použít k přizpůsobení serializace procesu. První dvě vlastnosti mají stejný význam jako probírané v předchozí části. Vlastnost můžete použít DataContractSurrogate
k povolení náhradních smluv dat, což je výkonný mechanismus pro přizpůsobení a rozšíření procesu serializace. Další informace naleznete v tématu Náhradní smlouvy dat.
Můžete použít DataContractSerializerOperationBehavior
k přizpůsobení serializace klienta i serveru. Následující příklad ukazuje, jak zvýšit kvótu MaxItemsInObjectGraph
klienta.
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
Následující kód je ekvivalentní kód ve službě v případě místního prostředí:
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()
V případě hostování webu je nutné vytvořit novou ServiceHost
odvozenou třídu a pomocí hostitelské továrny služby ji připojit.
Řízení serializace Nastavení v konfiguraci
IgnoreExtensionDataObject
Chování MaxItemsInObjectGraph
koncového dataContractSerializer
bodu nebo služby je možné řídit pomocí konfigurace, jak je znázorněno v následujícím příkladu.
<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>
Serializace sdíleného typu, zachování grafu objektů a vlastní serializátory
Serializuje DataContractSerializer pomocí názvů kontraktů dat, nikoli názvy typů .NET. To je konzistentní se sadami architektury orientovanými na služby a umožňuje velkou flexibilitu – typy .NET se můžou měnit, aniž by to ovlivnilo drátový kontrakt. Ve výjimečných případech můžete chtít serializovat skutečné názvy typů .NET, což představuje úzkou vazbu mezi klientem a serverem, podobně jako technologie vzdálené komunikace rozhraní .NET Framework. Nejedná se o doporučený postup, s výjimkou výjimečných případů, ke kterým obvykle dochází při migraci na WCF ze vzdálené komunikace rozhraní .NET Framework. V tomto případě musíte místo třídy použít NetDataContractSerializer třídu DataContractSerializer .
Obvykle DataContractSerializer serializuje objektové grafy jako stromy objektů. To znamená, že pokud je stejný objekt odkazován více než jednou, je serializován více než jednou. Představte si PurchaseOrder
například instanci, která má dvě pole typu Adresa s názvem billTo
a shipTo
. Pokud jsou obě pole nastavena na stejnou instanci Address, existují po serializaci a deserializaci dvě identické instance Adresy. To se provádí, protože neexistuje standardní interoperabilní způsob, jak znázorňovat grafy objektů v jazyce XML (s výjimkou staršího standardu XmlSerializerzakódovaného protokolem SOAP, který je k dispozici v předchozí části a Style
Use
). Serializace grafů objektů jako stromů má určité nevýhody, například grafy s cyklický odkaz nelze serializovat. Občas je nutné přepnout na serializaci grafu objektů true, i když není interoperabilní. To lze provést pomocí DataContractSerializer vytvořené s parametrem nastaveným preserveObjectReferences
na true
.
V některých případech nejsou integrované serializátory pro váš scénář dostatečné. Ve většině případů můžete stále používat XmlObjectSerializer abstrakci, ze které DataContractSerializerNetDataContractSerializer se odvozuje i odvoz.
Předchozí tři případy (zachování typu .NET, zachování objektového grafu a zcela vlastní XmlObjectSerializer
serializace) vyžadují, aby se vlastní serializátor připojil. Postupujte při tom podle následujících pokynů:
Napište své vlastní chování odvozené od DataContractSerializerOperationBehavior.
Přepište dvě
CreateSerializer
metody pro vrácení vlastní serializátoru (buď NetDataContractSerializer, DataContractSerializer spreserveObjectReferences
nastavenou natrue
, nebo vlastní XmlObjectSerializer).Před otevřením hostitele služby nebo vytvořením kanálu klienta odeberte stávající DataContractSerializerOperationBehavior chování a připojte vlastní odvozenou třídu, kterou jste vytvořili v předchozích krocích.
Další informace o pokročilých konceptech serializace naleznete v tématu Serializace a deserializace.