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 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 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, 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  

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 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í 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 BookMagazine, 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ě, DataContractSerializerOperationBehaviorMaxItemsInObjectGraph, 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 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 StyleUse). 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í XmlObjectSerializerserializace) vyžadují, aby se vlastní serializátor připojil. Postupujte při tom podle následujících pokynů:

  1. Napište své vlastní chování odvozené od DataContractSerializerOperationBehavior.

  2. Přepište dvě CreateSerializer metody pro vrácení vlastní serializátoru (buď NetDataContractSerializer, DataContractSerializer s preserveObjectReferences nastavenou na true, nebo vlastní XmlObjectSerializer).

  3. 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.

Viz také