Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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 je. Zprávy jsou definovány pomocí operačních kontraktů. 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 toCitypak 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 byly strunové parametry fromCity a toCity použity k popisu zprávy požadavku a návratovou hodnotu typu float 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 toCity ve zprávě požadavku, a čí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á komponentu nazvanou třída DataContractSerializer k provedení tohoto převodu. Podporuje se většina primitivních typů (například int, string, floata 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
Občas není DataContractSerializer adekvátní k serializaci vašich typů. WCF podporuje alternativní serializační modul, který XmlSerializermůžete použít také k serializaci parametrů. Systém XmlSerializer umožňuje mít více kontroly 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. Napří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 void návratový 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. Neexistuje způsob, jak z této operace signalizovat poruchu.
Popis zpráv pomocí smluv o zprávách
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 se třída DataContractSerializer stále používá jako výchozí. Třídu XmlSerializer lze také použít s kontrakty zpráv. Abyste toho dosáhli, aplikujte atribut XmlSerializerFormatAttribute na operaci nebo kontrakt a používejte typy kompatibilní s třídou XmlSerializer v záhlavích zpráv a členech těla.
Popis zpráv pomocí streamů
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 Streamtyp – 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í příklad ukazuje použití datových proudů.
[OperationContract]
public Stream DownloadFile(string fileName);
<OperationContract()>
Function DownloadFile(fileName As String) As String
Nelze kombinovat Stream a nestreamovaná data v jednom těle 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. Dosáhnete toho vyvoláním výjimky FaultException ve své implementaci 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.
Nelze použít třídu XmlSerializer k popisu chyb. XmlSerializerFormatAttribute nemá žá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, Book a Magazine, jsou odvozeny z 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 XmlIncludeAttribute můžete použít 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, kterou je třeba zavolat k získání seznamu známých typů, stejně jako atribut KnownTypeAttribute. Další informace naleznete v tématu Známé typy kontraktů dat.
Specifikace použití a stylu
Při popisu služeb pomocí jazyka WSDL (Web Services Description Language) jsou běžně používány dva styly: dokumentový 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 použít vlastnost MaxItemsInObjectGraph k nastavení kvóty, která omezuje maximální počet objektů, jež DataContractSerializer deserializuje. Vlastnost IgnoreExtensionDataObject můžete použít k vypnutí funkce obousměrného verzování. Další informace o kvótách najdete v tématu Důležité informace o zabezpečení dat. Další informace o obousměrném zpracování najdete v tématu Forward-Compatible Kontrakty dat.
[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 XmlSerializerOperationBehavior, která jsou automaticky zapojena 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, IgnoreExtensionDataObjecta 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 viz Zástupci datových smluv.
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 použitý službou v případě samostatného hostování:
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í továrny na hostování služby ji zapojit.
Řízení nastavení serializace v konfiguraci
MaxItemsInObjectGraph a IgnoreExtensionDataObject lze řídit pomocí konfigurace použitím koncového bodu dataContractSerializer nebo chování služby, 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ů datových kontraktů, nikoli názvů 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 názvy skutečných typů .NET, čímž vytvoříte úzkou vazbu mezi klientem a serverem, podobně jako technologie vzdálené komunikace pomocí 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 použít třídu NetDataContractSerializer místo třídy 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. Například si představte instanci PurchaseOrder, která má dvě pole typu Adresa nazvaná 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 reprezentovat grafy objektů v jazyce XML (s výjimkou staršího standardu zakódovaného protokolem SOAP, který je k dispozici na XmlSerializer, jak je popsáno v předchozí části o Style a Use). Serializace grafů objektů jako stromů má určité nevýhody, například grafy s cyklickými odkazy nelze serializovat. Občas je nutné přepnout na skutečnou serializaci objektového grafu, i když není interoperabilní. To lze provést pomocí DataContractSerializer, který je vytvořen s parametrem preserveObjectReferences nastaveným 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 abstrakci XmlObjectSerializer, ze které vycházejí jak DataContractSerializer, tak i NetDataContractSerializer.
Předchozí tři případy (zachování typu .NET, zachování objektového grafu a zcela vlastní XmlObjectSerializerserializace) vyžadují, aby se vlastní serializátor připojil. Postupujte takto:
Napište své vlastní chování odvozené od DataContractSerializerOperationBehavior.
Přepište dvě
CreateSerializermetody, aby vracely váš vlastní serializátor (buď NetDataContractSerializer, DataContractSerializer spreserveObjectReferencesnastaveným natrue, nebo váš vlastní upravený XmlObjectSerializer).Před otevřením hostitele služby nebo vytvořením kanálu klienta odstraňte stávající chování DataContractSerializerOperationBehavior a zapojte 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.