Anpassad meddelandekodare: Komprimeringskodare
Komprimeringsexemplet visar hur du implementerar en anpassad kodare med hjälp av WCF-plattformen (Windows Communication Foundation).
Exempelinformation
Det här exemplet består av ett klientkonsolprogram (.exe), ett lokalt tjänstkonsolprogram (.exe) och ett kodarbibliotek för komprimeringsmeddelanden (.dll). Tjänsten implementerar ett kontrakt som definierar ett kommunikationsmönster för begäran-svar. Kontraktet definieras av ISampleServer
gränssnittet, som exponerar grundläggande strängekoåtgärder (Echo
och BigEcho
). Klienten gör synkrona begäranden till en viss åtgärd och tjänsten svarar genom att upprepa meddelandet tillbaka till klienten. Klient- och tjänstaktiviteten visas i konsolfönstren. Avsikten med det här exemplet är att visa hur du skriver en anpassad kodare och visar effekten av komprimering av ett meddelande på tråden. Du kan lägga till instrumentation i kodaren för komprimeringsmeddelandet för att beräkna meddelandestorlek, bearbetningstid eller båda.
Kommentar
I .NET Framework 4 har automatisk dekomprimering aktiverats på en WCF-klient om servern skickar ett komprimerat svar (skapat med en algoritm som GZip eller Deflate). Om tjänsten är webbaserad i Internet Information Server (IIS) kan IIS konfigureras för att tjänsten ska skicka ett komprimerat svar. Det här exemplet kan användas om kravet är att utföra komprimering och dekomprimering på både klienten och tjänsten eller om tjänsten är lokalt installerad.
Exemplet visar hur du skapar och integrerar en anpassad meddelandekodare i ett WCF-program. Biblioteket GZipEncoder.dll distribueras med både klienten och tjänsten. Det här exemplet visar också effekten av att komprimera meddelanden. Koden i GZipEncoder.dll visar följande:
Skapa en anpassad kodare och kodarfabrik.
Utveckla ett bindningselement för en anpassad kodare.
Använda den anpassade bindningskonfigurationen för att integrera anpassade bindningselement.
Utveckla en anpassad konfigurationshanterare för att tillåta filkonfiguration av ett anpassat bindningselement.
Som tidigare nämnts finns det flera lager som implementeras i en anpassad kodare. För att bättre illustrera relationen mellan vart och ett av dessa lager finns en förenklad ordning för händelser för tjänststart i följande lista:
Servern startar.
Konfigurationsinformationen läse.
Tjänstkonfigurationen registrerar den anpassade konfigurationshanteraren.
Tjänstvärden skapas och öppnas.
Det anpassade konfigurationselementet skapar och returnerar det anpassade bindningselementet.
Det anpassade bindningselementet skapar och returnerar en meddelandekodarfabrik.
Ett meddelande tas emot.
Meddelandekodarfabriken returnerar en meddelandekodare för att läsa i meddelandet och skriva ut svaret.
Kodarskiktet implementeras som en klassfabrik. Endast kodarklassfabriken måste vara offentligt exponerad för den anpassade kodaren. Fabriksobjektet returneras av bindningselementet när ServiceHost objektet eller ChannelFactory<TChannel> skapas. Meddelandekodare kan fungera i buffrat läge eller direktuppspelningsläge. Det här exemplet visar både buffrat läge och strömningsläge.
För varje läge finns det en tillhörande ReadMessage
WriteMessage
och metod i den abstrakta MessageEncoder
klassen. En majoritet av kodningsarbetet sker i dessa metoder. Exemplet omsluter befintliga text- och binärmeddelandekodare. På så sätt kan exemplet delegera läsning och skrivning av trådrepresentationen av meddelanden till den inre kodaren och gör att komprimeringskodaren kan komprimera eller dekomprimera resultaten. Eftersom det inte finns någon pipeline för meddelandekodning är detta den enda modellen för att använda flera kodare i WCF. När meddelandet har dekomprimerats skickas det resulterande meddelandet upp i stacken som kanalstacken ska hantera. Under komprimering skrivs det resulterande komprimerade meddelandet direkt till den dataström som tillhandahålls.
Det här exemplet använder hjälpmetoder (CompressBuffer
och DecompressBuffer
) för att utföra konvertering från buffertar till strömmar för att använda GZipStream
klassen.
De buffrade ReadMessage
klasserna och WriteMessage
använder BufferManager
klassen. Kodaren är endast tillgänglig via kodarfabriken. Den abstrakta MessageEncoderFactory
klassen innehåller en egenskap med namnet Encoder
för åtkomst till den aktuella kodaren och en metod med namnet CreateSessionEncoder
för att skapa en kodare som stöder sessioner. En sådan kodare kan användas i scenariot där kanalen stöder sessioner, ordnas och är tillförlitlig. Det här scenariot möjliggör optimering i varje session av data som skrivits till tråden. Om detta inte önskas bör basmetoden inte överbelastas. Egenskapen Encoder
ger en mekanism för åtkomst till den sessionslösa kodaren och standardimplementeringen av CreateSessionEncoder
metoden returnerar värdet för egenskapen. Eftersom exemplet omsluter en befintlig kodare för att ge komprimering accepterar MessageEncoderFactory
implementeringen en MessageEncoderFactory
som representerar den inre kodarfabriken.
Nu när kodaren och kodarfabriken har definierats kan de användas med en WCF-klient och -tjänst. Dessa kodare måste dock läggas till i kanalstacken. Du kan härleda klasser från ServiceHost klasserna och ChannelFactory<TChannel> och åsidosätta metoderna för att lägga till den här kodarfabriken OnInitialize
manuellt. Du kan också exponera kodarfabriken via ett anpassat bindningselement.
Om du vill skapa ett nytt anpassat bindningselement härleder du en klass från BindingElement klassen. Det finns dock flera typer av bindningselement. För att säkerställa att det anpassade bindningselementet identifieras som ett meddelandekodningsbindningselement måste du även implementera MessageEncodingBindingElement. Exponerar MessageEncodingBindingElement en metod för att skapa en ny meddelandekodarfabrik (CreateMessageEncoderFactory
), som implementeras för att returnera en instans av den matchande meddelandekodarfabriken. Dessutom MessageEncodingBindingElement har en egenskap som anger adresseringsversionen. Eftersom det här exemplet omsluter befintliga kodare omsluter exempelimplementeringen även de befintliga kodarbindningselementen och tar ett inre kodarbindningselement som en parameter till konstruktorn och exponerar det via en egenskap. Följande exempelkod visar implementeringen av GZipMessageEncodingBindingElement
klassen.
public sealed class GZipMessageEncodingBindingElement
: MessageEncodingBindingElement //BindingElement
, IPolicyExportExtension
{
//We use an inner binding element to store information
//required for the inner encoder.
MessageEncodingBindingElement innerBindingElement;
//By default, use the default text encoder as the inner encoder.
public GZipMessageEncodingBindingElement()
: this(new TextMessageEncodingBindingElement()) { }
public GZipMessageEncodingBindingElement(MessageEncodingBindingElement messageEncoderBindingElement)
{
this.innerBindingElement = messageEncoderBindingElement;
}
public MessageEncodingBindingElement InnerMessageEncodingBindingElement
{
get { return innerBindingElement; }
set { innerBindingElement = value; }
}
//Main entry point into the encoder binding element.
// Called by WCF to get the factory that creates the
//message encoder.
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new
GZipMessageEncoderFactory(innerBindingElement.CreateMessageEncoderFactory());
}
public override MessageVersion MessageVersion
{
get { return innerBindingElement.MessageVersion; }
set { innerBindingElement.MessageVersion = value; }
}
public override BindingElement Clone()
{
return new
GZipMessageEncodingBindingElement(this.innerBindingElement);
}
public override T GetProperty<T>(BindingContext context)
{
if (typeof(T) == typeof(XmlDictionaryReaderQuotas))
{
return innerBindingElement.GetProperty<T>(context);
}
else
{
return base.GetProperty<T>(context);
}
}
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.BindingParameters.Add(this);
return context.BuildInnerChannelFactory<TChannel>();
}
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.BindingParameters.Add(this);
return context.BuildInnerChannelListener<TChannel>();
}
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
{
if (context == null)
throw new ArgumentNullException("context");
context.BindingParameters.Add(this);
return context.CanBuildInnerChannelListener<TChannel>();
}
void IPolicyExportExtension.ExportPolicy(MetadataExporter exporter, PolicyConversionContext policyContext)
{
if (policyContext == null)
{
throw new ArgumentNullException("policyContext");
}
XmlDocument document = new XmlDocument();
policyContext.GetBindingAssertions().Add(document.CreateElement(
GZipMessageEncodingPolicyConstants.GZipEncodingPrefix,
GZipMessageEncodingPolicyConstants.GZipEncodingName,
GZipMessageEncodingPolicyConstants.GZipEncodingNamespace));
}
}
Observera att GZipMessageEncodingBindingElement
klassen implementerar IPolicyExportExtension
gränssnittet så att det här bindningselementet kan exporteras som en princip i metadata, som du ser i följande exempel.
<wsp:Policy wsu:Id="BufferedHttpSampleServer_ISampleServer_policy">
<wsp:ExactlyOne>
<wsp:All>
<gzip:text xmlns:gzip=
"http://schemas.microsoft.com/ws/06/2004/mspolicy/netgzip1" />
<wsaw:UsingAddressing />
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
Klassen GZipMessageEncodingBindingElementImporter
implementerar gränssnittet, den här klassen importerar IPolicyImportExtension
principen för GZipMessageEncodingBindingElement
. Svcutil.exe verktyget kan användas för att importera principer till konfigurationsfilen, för att hantera GZipMessageEncodingBindingElement
, bör följande läggas till i Svcutil.exe.config.
<configuration>
<system.serviceModel>
<extensions>
<bindingElementExtensions>
<add name="gzipMessageEncoding"
type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingElement, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<client>
<metadata>
<policyImporters>
<remove type=
"System.ServiceModel.Channels.MessageEncodingBindingElementImporter, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<extension type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingBindingElementImporter, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</policyImporters>
</metadata>
</client>
</system.serviceModel>
</configuration>
Nu när det finns ett matchande bindningselement för komprimeringskodaren kan det kopplas programmatiskt till tjänsten eller klienten genom att konstruera ett nytt anpassat bindningsobjekt och lägga till det anpassade bindningselementet till det, enligt följande exempelkod.
ICollection<BindingElement> bindingElements = new List<BindingElement>();
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
GZipMessageEncodingBindingElement compBindingElement = new GZipMessageEncodingBindingElement ();
bindingElements.Add(compBindingElement);
bindingElements.Add(httpBindingElement);
CustomBinding binding = new CustomBinding(bindingElements);
binding.Name = "SampleBinding";
binding.Namespace = "http://tempuri.org/bindings";
Även om detta kan vara tillräckligt för de flesta användarscenarier är det viktigt att stödja en filkonfiguration om en tjänst ska vara webbhanterad. För att stödja det webbaserade scenariot måste du utveckla en anpassad konfigurationshanterare så att ett anpassat bindningselement kan konfigureras i en fil.
Du kan skapa en konfigurationshanterare för bindningselementet ovanpå konfigurationssystemet. Konfigurationshanteraren för bindningselementet måste härledas från BindingElementExtensionElement klassen. BindingElementExtensionElement.BindingElementType Informerar konfigurationssystemet om vilken typ av bindningselement som ska skapas för det här avsnittet. Alla aspekter av som BindingElement
kan anges ska exponeras som egenskaper i den BindingElementExtensionElement härledda klassen. Hjälper ConfigurationPropertyAttribute till med att mappa konfigurationselementattributen till egenskaperna och ange standardvärden om attribut saknas. När värdena från konfigurationen har lästs in och tillämpats på egenskaperna BindingElementExtensionElement.CreateBindingElement anropas metoden, som konverterar egenskaperna till en konkret instans av ett bindningselement. Metoden BindingElementExtensionElement.ApplyConfiguration används för att konvertera egenskaperna för den BindingElementExtensionElement härledda klassen till de värden som ska anges för det nyligen skapade bindningselementet.
Följande exempelkod visar implementeringen av GZipMessageEncodingElement
.
public class GZipMessageEncodingElement : BindingElementExtensionElement
{
public GZipMessageEncodingElement()
{
}
//Called by the WCF to discover the type of binding element this
//config section enables
public override Type BindingElementType
{
get { return typeof(GZipMessageEncodingBindingElement); }
}
//The only property we need to configure for our binding element is
//the type of inner encoder to use. Here, we support text and
//binary.
[ConfigurationProperty("innerMessageEncoding",
DefaultValue = "textMessageEncoding")]
public string InnerMessageEncoding
{
get { return (string)base["innerMessageEncoding"]; }
set { base["innerMessageEncoding"] = value; }
}
//Called by the WCF to apply the configuration settings (the
//property above) to the binding element
public override void ApplyConfiguration(BindingElement bindingElement)
{
GZipMessageEncodingBindingElement binding =
(GZipMessageEncodingBindingElement)bindingElement;
PropertyInformationCollection propertyInfo =
this.ElementInformation.Properties;
if (propertyInfo["innerMessageEncoding"].ValueOrigin !=
PropertyValueOrigin.Default)
{
switch (this.InnerMessageEncoding)
{
case "textMessageEncoding":
binding.InnerMessageEncodingBindingElement =
new TextMessageEncodingBindingElement();
break;
case "binaryMessageEncoding":
binding.InnerMessageEncodingBindingElement =
new BinaryMessageEncodingBindingElement();
break;
}
}
}
//Called by the WCF to create the binding element
protected override BindingElement CreateBindingElement()
{
GZipMessageEncodingBindingElement bindingElement =
new GZipMessageEncodingBindingElement();
this.ApplyConfiguration(bindingElement);
return bindingElement;
}
}
Den här konfigurationshanteraren mappar till följande representation i App.config eller Web.config för tjänsten eller klienten.
<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
Om du vill använda den här konfigurationshanteraren måste den vara registrerad i elementet <system.serviceModel> , enligt följande exempelkonfiguration.
<extensions>
<bindingElementExtensions>
<add
name="gzipMessageEncoding"
type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingElement,
GZipEncoder, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
När du kör servern visas åtgärdsbegäranden och svar i konsolfönstret. Tryck på RETUR i fönstret för att stänga av servern.
Press Enter key to Exit.
Server Echo(string input) called:
Client message: Simple hello
Server BigEcho(string[] input) called:
64 client messages
När du kör klienten visas åtgärdsbegäranden och svar i konsolfönstret. Tryck på RETUR i klientfönstret för att stänga av klienten.
Calling Echo(string):
Server responds: Simple hello Simple hello
Calling BigEcho(string[]):
Server responds: Hello 0
Press <ENTER> to terminate client.
Så här konfigurerar du, skapar och kör exemplet
Installera ASP.NET 4.0 med följande kommando:
%windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
Kontrollera att du har utfört engångsinstallationsproceduren för Windows Communication Foundation-exempel.
Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.
Om du vill köra exemplet i en konfiguration med en eller flera datorer följer du anvisningarna i Köra Windows Communication Foundation-exempel.