Dela via


Utforma tjänstkontrakt

Det här avsnittet beskriver vad tjänstkontrakt är, hur de definieras, vilka åtgärder som är tillgängliga (och konsekvenserna för de underliggande meddelandeutbytena), vilka datatyper som används och andra problem som hjälper dig att utforma åtgärder som uppfyller kraven i ditt scenario.

Skapa ett tjänstkontrakt

Tjänster exponerar ett antal åtgärder. I WCF-program (Windows Communication Foundation) definierar du åtgärderna genom att skapa en metod och markera den med attributet OperationContractAttribute . Om du sedan vill skapa ett tjänstkontrakt grupperar du dina åtgärder, antingen genom att deklarera dem i ett gränssnitt som markerats med ServiceContractAttribute attributet eller genom att definiera dem i en klass som har markerats med samma attribut. (Ett grundläggande exempel finns i Anvisningar: Definiera ett tjänstkontrakt.)

Alla metoder som inte har ett OperationContractAttribute attribut är inte tjänståtgärder och exponeras inte av WCF-tjänster.

I det här avsnittet beskrivs följande beslutspunkter när du utformar ett tjänstkontrakt:

  • Om du vill använda klasser eller gränssnitt.

  • Så här anger du de datatyper som du vill utbyta.

  • De typer av utbytesmönster som du kan använda.

  • Om du kan göra explicita säkerhetskrav till en del av kontraktet.

  • Begränsningarna för indata och utdata för åtgärden.

Klasser eller gränssnitt

Både klasser och gränssnitt representerar en gruppering av funktioner och därför kan båda användas för att definiera ett WCF-tjänstkontrakt. Vi rekommenderar dock att du använder gränssnitt eftersom de direkt modellerar tjänstkontrakt. Utan en implementering definierar gränssnitt bara en gruppering av metoder med vissa signaturer. Implementera ett tjänstkontraktsgränssnitt och du har implementerat en WCF-tjänst.

Alla fördelar med hanterade gränssnitt gäller för tjänstkontraktsgränssnitt:

  • Servicekontraktsgränssnitt kan utöka valfritt antal andra tjänstkontraktsgränssnitt.

  • En enda klass kan implementera valfritt antal tjänstkontrakt genom att implementera dessa tjänstkontraktsgränssnitt.

  • Du kan ändra implementeringen av ett tjänstkontrakt genom att ändra gränssnittsimplementeringen, medan tjänstkontraktet förblir detsamma.

  • Du kan version din tjänst genom att implementera det gamla gränssnittet och det nya. Gamla klienter ansluter till den ursprungliga versionen, medan nyare klienter kan ansluta till den nyare versionen.

Kommentar

När du ärver från andra tjänstkontraktsgränssnitt kan du inte åsidosätta åtgärdsegenskaper, till exempel namn eller namnområde. Om du försöker göra det skapar du en ny åtgärd i det aktuella tjänstkontraktet.

Ett exempel på hur du använder ett gränssnitt för att skapa ett tjänstkontrakt finns i Så här skapar du en tjänst med ett kontraktsgränssnitt.

Du kan dock använda en klass för att definiera ett tjänstkontrakt och implementera det kontraktet samtidigt. Fördelen med att skapa dina tjänster genom att tillämpa ServiceContractAttribute och OperationContractAttribute direkt på klassen och metoderna i klassen är snabbhet och enkelhet. Nackdelarna är att hanterade klasser inte stöder flera arv, och därför kan de bara implementera ett tjänstkontrakt i taget. Dessutom ändrar alla ändringar av klass- eller metodsignaturerna det offentliga kontraktet för tjänsten, vilket kan förhindra att oförändrade klienter använder din tjänst. Mer information finns i Implementera tjänstkontrakt.

Ett exempel som använder en klass för att skapa ett tjänstkontrakt och implementerar det samtidigt finns i How to: Create a Service with a Contract Class (Så här skapar du en tjänst med en kontraktsklass).

Nu bör du förstå skillnaden mellan att definiera ditt tjänstkontrakt med hjälp av ett gränssnitt och genom att använda en klass. Nästa steg är att bestämma vilka data som kan skickas fram och tillbaka mellan en tjänst och dess klienter.

Parametrar och returvärden

Varje åtgärd har ett returvärde och en parameter, även om dessa är void. Men till skillnad från en lokal metod, där du kan skicka referenser till objekt från ett objekt till ett annat, skickar inte tjänståtgärder referenser till objekt. I stället skickar de kopior av objekten.

Detta är viktigt eftersom varje typ som används i en parameter eller ett returvärde måste vara serialiserbar. Det måste alltså vara möjligt att konvertera ett objekt av den typen till en ström med byte och från en ström med byte till ett objekt.

Primitiva typer kan serialiseras som standard, liksom många typer i .NET Framework.

Kommentar

Värdet för parameternamnen i åtgärdssignaturen är en del av kontraktet och är skiftlägeskänsliga. Om du vill använda samma parameternamn lokalt men ändra namnet i publicerade metadata läser System.ServiceModel.MessageParameterAttributedu .

Datakontrakt

Tjänstorienterade program som WCF-program (Windows Communication Foundation) är utformade för att samverka med bredast möjliga antal klientprogram på både Microsoft- och icke-Microsoft-plattformar. För största möjliga samverkan rekommenderar vi att du markerar dina typer med attributen DataContractAttribute och DataMemberAttribute för att skapa ett datakontrakt, vilket är den del av tjänstkontraktet som beskriver de data som dina tjänståtgärder utbyter.

Datakontrakt är opt-in-stilkontrakt: Ingen typ eller datamedlem serialiseras om du inte uttryckligen tillämpar datakontraktsattributet. Datakontrakt är inte relaterade till åtkomstomfånget för den hanterade koden: Privata datamedlemmar kan serialiseras och skickas någon annanstans för att nås offentligt. (Ett grundläggande exempel på ett datakontrakt finns i Gör så här: Skapa ett grundläggande datakontrakt för en klass eller struktur.) WCF hanterar definitionen av underliggande SOAP-meddelanden som aktiverar åtgärdens funktioner samt serialiseringen av dina datatyper till och från meddelandenas brödtext. Så länge dina datatyper är serialiserbara behöver du inte tänka på den underliggande infrastrukturen för meddelandeutbyte när du utformar dina åtgärder.

Även om det typiska WCF-programmet använder attributen DataContractAttribute och DataMemberAttribute för att skapa datakontrakt för åtgärder, kan du använda andra serialiseringsmekanismer. Standardmekanismerna ISerializable, SerializableAttributeoch IXmlSerializable fungerar alla för att hantera serialiseringen av dina datatyper i de underliggande SOAP-meddelandena som bär dem från ett program till ett annat. Du kan använda fler serialiseringsstrategier om dina datatyper kräver särskilt stöd. Mer information om alternativen för serialisering av datatyper i WCF-program finns i Ange dataöverföring i tjänstkontrakt.

Mappa parametrar och returnera värden till meddelandeutbyten

Tjänståtgärder stöds av ett underliggande utbyte av SOAP-meddelanden som överför programdata fram och tillbaka, utöver de data som krävs av programmet för att stödja vissa standardfunktioner för säkerhet, transaktioner och sessioner. Eftersom så är fallet dikterar signaturen för en tjänståtgärd ett visst underliggande mönster för meddelandeutbyte (MEP) som kan stödja dataöverföringen och de funktioner som en åtgärd kräver. Du kan ange tre mönster i WCF-programmeringsmodellen: mönster för begäran/svar, enkelriktade och duplexmeddelanden.

Begäran/svar

Ett mönster för begäran/svar är ett där en begärandesändare (ett klientprogram) tar emot ett svar som begäran är korrelerad med. Detta är standard-MEP eftersom det stöder en åtgärd där en eller flera parametrar skickas till åtgärden och ett returvärde skickas tillbaka till anroparen. I följande C#-kodexempel visas till exempel en grundläggande tjänståtgärd som tar en sträng och returnerar en sträng.

[OperationContractAttribute]  
string Hello(string greeting);  

Följande är motsvarande Visual Basic-kod.

<OperationContractAttribute()>  
Function Hello (ByVal greeting As String) As String  

Den här åtgärdssignaturen dikterar formen av underliggande meddelandeutbyte. Om det inte finns någon korrelation kan WCF inte avgöra för vilken åtgärd returvärdet är avsett för.

Observera att om du inte anger ett annat underliggande meddelandemönster är även tjänståtgärder som returnerar void (Nothing i Visual Basic) utbyten av begärande-/svarsmeddelanden. Resultatet för åtgärden är att om inte en klient anropar åtgärden asynkront slutar klienten bearbeta tills returmeddelandet tas emot, även om meddelandet är tomt i det normala fallet. Följande C#-kodexempel visar en åtgärd som inte returneras förrän klienten har fått ett tomt meddelande som svar.

[OperationContractAttribute]  
void Hello(string greeting);  

Följande är motsvarande Visual Basic-kod.

<OperationContractAttribute()>  
Sub Hello (ByVal greeting As String)  

Föregående exempel kan försämra klientens prestanda och svarstider om åtgärden tar lång tid att utföra, men det finns fördelar med begärande-/svarsåtgärder även när de returnerar void. Den mest uppenbara är att SOAP-fel kan returneras i svarsmeddelandet, vilket indikerar att något tjänstrelaterat feltillstånd har inträffat, oavsett om det är i kommunikation eller bearbetning. SOAP-fel som anges i ett tjänstkontrakt skickas till klientprogrammet som ett FaultException<TDetail> objekt, där typparametern är den typ som anges i tjänstkontraktet. Detta gör det enkelt att meddela klienter om feltillstånd i WCF-tjänster. Mer information om undantag, SOAP-fel och felhantering finns i Ange och hantera fel i Kontrakt och tjänster. Ett exempel på en begäran/svarstjänst och -klient finns i Så här skapar du ett begärande-svar-kontrakt. Mer information om problem med mönstret för begäran-svar finns i Begärandesvarstjänster.

Enkelriktad

Om klienten för ett WCF-tjänstprogram inte ska vänta tills åtgärden har slutförts och inte bearbetar SOAP-fel kan åtgärden ange ett enkelriktad meddelandemönster. En enkelriktad åtgärd är en åtgärd där en klient anropar en åtgärd och fortsätter bearbetningen efter att WCF har skrivit meddelandet till nätverket. Detta innebär vanligtvis att om inte de data som skickas i det utgående meddelandet är extremt stora fortsätter klienten att köras nästan omedelbart (om det inte uppstår ett fel när data skickas). Den här typen av meddelandeutbytesmönster stöder händelseliknande beteende från en klient till ett tjänstprogram.

Ett meddelandeutbyte där ett meddelande skickas och inget tas emot kan inte stödja en tjänståtgärd som anger ett annat returvärde än void; i det här fallet utlöses ett InvalidOperationException undantag.

Inget returmeddelande innebär också att det inte kan finnas något SOAP-fel som returneras för att indikera eventuella fel i bearbetningen eller kommunikationen. (Kommunikation av felinformation när åtgärder är enkelriktade åtgärder kräver ett duplex-meddelandeutbytesmönster.)

Om du vill ange ett enkelriktad meddelandeutbyte för en åtgärd som returnerar voidanger du IsOneWay egenskapen till true, som i följande C#-kodexempel.

[OperationContractAttribute(IsOneWay=true)]  
void Hello(string greeting);  

Följande är motsvarande Visual Basic-kod.

<OperationContractAttribute(IsOneWay := True)>  
Sub Hello (ByVal greeting As String)  

Den här metoden är identisk med föregående exempel på begäran/svar, men om du anger IsOneWay egenskapen till true innebär det att även om metoden är identisk skickar inte tjänståtgärden ett returmeddelande och klienterna returnerar omedelbart när det utgående meddelandet har överlämnats till kanallagret. Ett exempel finns i How to: Create a One-Way Contract (Så här skapar du ett enkelriktade kontrakt). Mer information om enkelriktade mönster finns i Enkelriktade tjänster.

Duplex

Ett duplexmönster kännetecknas av både tjänstens och klientens förmåga att skicka meddelanden till varandra oberoende av om de använder enkelriktade meddelanden eller begärande-/svarsmeddelanden. Den här typen av dubbelriktad kommunikation är användbar för tjänster som måste kommunicera direkt till klienten eller för att tillhandahålla en asynkron upplevelse på båda sidor av ett meddelandeutbyte, inklusive händelseliknande beteende.

Duplex-mönstret är något mer komplext än mönster för begäran/svar eller enkelriktade mönster på grund av den ytterligare mekanismen för kommunikation med klienten.

Om du vill utforma ett duplex-kontrakt måste du också utforma ett återanropskontrakt och tilldela typen av återanropskontrakt till egenskapen för CallbackContract attributet ServiceContractAttribute som markerar ditt tjänstkontrakt.

Om du vill implementera ett duplexmönster måste du skapa ett andra gränssnitt som innehåller de metoddeklarationer som anropas på klienten.

Ett exempel på hur du skapar en tjänst och en klient som har åtkomst till tjänsten finns i How to: Create a Duplex Contract and How to: Access Services with a Duplex Contract (Så här skapar du ett Duplex-kontrakt och Så här gör du för att: Få åtkomst till tjänster med ett Duplex-kontrakt). Ett arbetsexempel finns i Duplex. Mer information om problem med duplexkontrakt finns i Duplex Services.

Varning

När en tjänst tar emot ett duplex-meddelande tittar den på elementet ReplyTo i det inkommande meddelandet för att avgöra var svaret ska skickas. Om den kanal som används för att ta emot meddelandet inte är skyddad kan en icke-betrodd klient skicka ett skadligt meddelande med måldatorns ReplyTo, vilket leder till doS (Denial of Service) för måldatorn.

Ut- och referensparametrar

I de flesta fall kan du använda in parametrar (ByVal i Visual Basic) och out parametrar ref (ByRef i Visual Basic). Eftersom både out och ref parametrar anger att data returneras från en åtgärd, anger en åtgärdssignatur, till exempel följande, att en begäran/svar-åtgärd krävs trots att åtgärdens signatur returnerar void.

[ServiceContractAttribute]  
public interface IMyContract  
{  
  [OperationContractAttribute]  
  public void PopulateData(ref CustomDataType data);  
}  

Följande är motsvarande Visual Basic-kod.

<ServiceContractAttribute()> _  
Public Interface IMyContract  
  <OperationContractAttribute()> _  
  Public Sub PopulateData(ByRef data As CustomDataType)  
End Interface  

De enda undantagen är de fall där din signatur har en viss struktur. Du kan till exempel bara använda bindningen NetMsmqBinding för att kommunicera med klienter om metoden som används för att deklarera en åtgärd returnerar void. Det kan inte finnas något utdatavärde, oavsett om det är ett returvärde eller refout en parameter.

Dessutom kräver användning out eller ref parametrar att åtgärden har ett underliggande svarsmeddelande för att överföra det ändrade objektet. Om åtgärden är en enkelriktad åtgärd utlöses ett InvalidOperationException undantag vid körning.

Ange meddelandeskyddsnivå för kontraktet

När du utformar ditt kontrakt måste du också bestämma meddelandeskyddsnivån för tjänster som implementerar ditt kontrakt. Detta är bara nödvändigt om meddelandesäkerhet tillämpas på bindningen i kontraktets slutpunkt. Om bindningen har säkerhet inaktiverad (d.v.s. om den systembaserade bindningen anger System.ServiceModel.SecurityMode värdet SecurityMode.None) behöver du inte bestämma meddelandeskyddsnivån för kontraktet. I de flesta fall ger systembaserade bindningar med säkerhet på meddelandenivå en tillräcklig skyddsnivå och du behöver inte ta hänsyn till skyddsnivån för varje åtgärd eller för varje meddelande.

Skyddsnivån är ett värde som anger om de meddelanden (eller meddelandedelar) som stöder en tjänst signeras, signeras och krypteras eller skickas utan signaturer eller kryptering. Skyddsnivån kan anges på olika omfång: På tjänstnivå, för en viss åtgärd, för ett meddelande inom den åtgärden eller en meddelandedel. Värden som anges med ett omfång blir standardvärdet för mindre omfång om de inte uttryckligen åsidosätts. Om en bindningskonfiguration inte kan tillhandahålla den lägsta skyddsnivå som krävs för kontraktet utlöses ett undantag. Och när inga skyddsnivåvärden uttryckligen anges i kontraktet styr bindningskonfigurationen skyddsnivån för alla meddelanden om bindningen har meddelandesäkerhet. Det här är standardbeteendet.

Viktigt!

Beslut om att uttryckligen ange olika omfattningar för ett kontrakt till mindre än den fullständiga skyddsnivån ProtectionLevel.EncryptAndSign för är i allmänhet ett beslut som handlar med en viss grad av säkerhet för ökad prestanda. I dessa fall måste dina beslut kretsa kring dina åtgärder och värdet på de data som de utbyter. Mer information finns i Skydda tjänster.

I följande kodexempel anges till exempel inte antingen ProtectionLevel egenskapen eller ProtectionLevel för kontraktet.

[ServiceContract]  
public interface ISampleService  
{  
  [OperationContractAttribute]  
  public string GetString();  
  
  [OperationContractAttribute]  
  public int GetInt();
}  

Följande är motsvarande Visual Basic-kod.

<ServiceContractAttribute()> _  
Public Interface ISampleService  
  
  <OperationContractAttribute()> _  
  Public Function GetString()As String  
  
  <OperationContractAttribute()> _  
  Public Function GetData() As Integer  
  
End Interface  

När du interagerar med en ISampleService implementering i en slutpunkt med ett standardvärde WSHttpBinding (standardvärdet System.ServiceModel.SecurityMode, som är Message), krypteras och signeras alla meddelanden eftersom detta är standardskyddsnivån. Men när en ISampleService tjänst används med ett standardvärde BasicHttpBinding (standardvärdet SecurityMode, vilket är None), skickas alla meddelanden som text eftersom det inte finns någon säkerhet för den här bindningen och därför ignoreras skyddsnivån (det vill sägs att meddelandena varken är krypterade eller signerade). Om har SecurityMode ändrats till Messageskulle dessa meddelanden krypteras och signeras (eftersom det nu skulle vara bindningens standardskyddsnivå).

Om du uttryckligen vill ange eller justera skyddskraven för ditt kontrakt anger ProtectionLevel du egenskapen (eller någon av ProtectionLevel egenskaperna i ett mindre omfång) till den nivå som ditt tjänstkontrakt kräver. I det här fallet kräver användning av en explicit inställning bindningen för att stödja den inställningen som ett minimum för det omfång som används. I följande kodexempel anges till exempel ett ProtectionLevel värde explicit för åtgärden GetGuid .

[ServiceContract]  
public interface IExplicitProtectionLevelSampleService  
{  
  [OperationContractAttribute]  
  public string GetString();  
  
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]  
  public int GetInt();
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]  
  public int GetGuid();
}  

Följande är motsvarande Visual Basic-kod.

<ServiceContract()> _
Public Interface IExplicitProtectionLevelSampleService
    <OperationContract()> _
    Public Function GetString() As String
    End Function
  
    <OperationContract(ProtectionLevel := ProtectionLevel.None)> _
    Public Function GetInt() As Integer
    End Function
  
    <OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _
    Public Function GetGuid() As Integer
    End Function
  
End Interface  

En tjänst som implementerar det här IExplicitProtectionLevelSampleService kontraktet och har en slutpunkt som använder standardvärdet WSHttpBinding (standardvärdet System.ServiceModel.SecurityMode, som är Message) har följande beteende:

  • Åtgärdsmeddelandena GetString krypteras och signeras.

  • Åtgärdsmeddelandena GetInt skickas som okrypterad och osignerad (d.v.s. oformaterad) text.

  • Åtgärden GetGuidSystem.Guid returneras i ett meddelande som är krypterat och signerat.

Mer information om skyddsnivåer och hur du använder dem finns i Förstå skyddsnivå. Mer information om säkerhet finns i Skydda tjänster.

Krav för annan åtgärdssignatur

Vissa programfunktioner kräver en viss typ av åtgärdssignatur. Bindningen NetMsmqBinding stöder till exempel varaktiga tjänster och klienter, där ett program kan startas om mitt i kommunikationen och fortsätta där den slutade utan att några meddelanden saknas. (Mer information finns i Köer i WCF.) Varaktiga åtgärder får dock bara ta en in parameter och har inget returvärde.

Ett annat exempel är användningen av Stream typer i åtgärder. Eftersom parametern Stream innehåller hela meddelandetexten, om indata eller utdata (dvs ref . parameter, out parameter eller returvärde) är av typen Streammåste det vara den enda indata eller utdata som angetts i åtgärden. Dessutom måste parametern eller returtypen vara antingen Stream, System.ServiceModel.Channels.Messageeller System.Xml.Serialization.IXmlSerializable. Mer information om strömmar finns i Stora data och strömning.

Namn, namnområden och obfuscation

Namnen och namnrymderna för .NET-typerna i definitionen av kontrakt och åtgärder är viktiga när kontrakt konverteras till WSDL och när kontraktmeddelanden skapas och skickas. Därför rekommenderar vi starkt att namn och namnrymder för tjänstkontrakt uttryckligen anges med hjälp Name av egenskaperna och Namespace för alla stödkontraktsattribut, till exempel attributen ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttributeoch andra kontrakt.

Ett resultat av detta är att om namnen och namnrymderna inte uttryckligen anges ändrar användningen av IL-fördunkling på sammansättningen kontrakttypnamnen och namnrymderna och resulterar i ändrade WSDL- och trådutbyten som vanligtvis misslyckas. Om du inte uttryckligen anger kontraktnamnen och namnrymderna, men tänker använda fördunkling, använder du attributen ObfuscationAttribute och ObfuscateAssemblyAttribute för att förhindra att kontrakttypnamnen och namnrymderna ändras.

Se även