Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Przykład programu Compression pokazuje, jak zaimplementować niestandardowy koder przy użyciu platformy Windows Communication Foundation (WCF).
Przykładowe szczegóły
Ten przykład składa się z programu konsolowego klienta (.exe), programu konsolowego usługi samoobsługowej (.exe) i biblioteki enkodera wiadomości kompresji (.dll). Usługa implementuje kontrakt, który definiuje wzorzec komunikacji typu żądanie-odpowiedź. Kontrakt jest definiowany przez ISampleServer interfejs, który udostępnia podstawowe operacje odzwierciedlania ciągu (Echo i BigEcho). Klient wysyła synchroniczne żądania do danej operacji, a usługa odpowiada, powtarzając komunikat z powrotem do klienta. Działanie klienta i usługi jest widoczne w oknach konsoli. Celem tego przykładu jest pokazanie, jak napisać koder niestandardowy i zademonstrować wpływ kompresji komunikatu na przewody. Instrumentację można dodać do kodera komunikatów kompresji, aby obliczyć rozmiar komunikatu, czas przetwarzania lub oba te elementy.
Uwaga / Notatka
W programie .NET Framework 4 automatyczne dekompresja została włączona na kliencie WCF, jeśli serwer wysyła skompresowaną odpowiedź (utworzoną przy użyciu algorytmu takiego jak GZip lub Deflate). Jeśli usługa jest hostowana w sieci Web na serwerze usług internetowych (IIS), można skonfigurować IIS, aby usługa wysyłała skompresowaną odpowiedź. Ten przykład można użyć, jeśli wymagane jest wykonanie kompresji i dekompresji zarówno na kliencie, jak i w usłudze, lub jeśli usługa jest hostowana samodzielnie.
W przykładzie pokazano, jak skompilować i zintegrować niestandardowy koder komunikatów z aplikacją WCF. Biblioteka GZipEncoder.dll jest wdrażana zarówno z klientem, jak i usługą. W tym przykładzie pokazano również wpływ kompresowania komunikatów. Kod w GZipEncoder.dll przedstawia następujące elementy:
Tworzenie niestandardowego enkodera i fabryki enkoderów.
Tworzenie elementu wiążącego dla niestandardowego enkodera.
Używanie niestandardowej konfiguracji powiązania do integrowania niestandardowych elementów powiązania.
Tworzenie niestandardowego modułu konfiguracji, aby umożliwić konfigurację za pomocą pliku dla niestandardowego elementu powiązania.
Jak wskazano wcześniej, istnieje kilka warstw, które są implementowane w koderze niestandardowym. Aby lepiej zilustrować relację między poszczególnymi warstwami, uproszczona kolejność zdarzeń uruchamiania usługi znajduje się na poniższej liście:
Serwer zostanie uruchomiony.
Informacje o konfiguracji są odczytywane.
Konfiguracja usługi rejestruje niestandardową procedurę obsługi konfiguracji.
Host usługi jest tworzony i otwierany.
Niestandardowy element konfiguracji tworzy i zwraca niestandardowy element wiążący.
Niestandardowy element powiązania tworzy i zwraca fabrykę kodera komunikatów.
Odebrano komunikat.
Fabryka kodera komunikatów zwraca koder komunikatów do odczytu w komunikacie i zapisywania odpowiedzi.
Warstwa kodera jest implementowana jako fabryka klas. Tylko fabryka klas enkodera musi być publicznie udostępniona dla niestandardowego enkodera. Obiekt fabryki jest zwracany przez element powiązania, gdy obiekt ServiceHost lub ChannelFactory<TChannel> jest tworzony. Kodery komunikatów mogą działać w trybie buforowania lub przesyłania strumieniowego. W tym przykładzie pokazano zarówno tryb buforowany, jak i tryb przesyłania strumieniowego.
Dla każdego trybu istnieją towarzyszące metoda ReadMessage i metoda WriteMessage w abstrakcyjnej klasie MessageEncoder. Większość pracy kodowania odbywa się w tych metodach. Przykład opakowuje istniejące kodery komunikatów tekstowych i binarnych. Pozwala to przykładowi delegować odczytywanie i zapisywanie reprezentacji na drut komunikatów do wewnętrznego kodera, a także umożliwia koderowi kompresji na kompresowanie lub dekompresowanie wyników. Ponieważ nie ma przepływu pracy kodowania komunikatów, jest to jedyny model używania wielu koderów w systemie WCF. Gdy komunikat zostanie zdekompresowany, wynikowy komunikat jest przekazywany w górę stosu, aby obsłużył go stos kanału. Podczas kompresji wynikowy skompresowany komunikat jest zapisywany bezpośrednio w udostępnionym strumieniu.
W tym przykładzie użyto metod pomocnika (CompressBuffer i DecompressBuffer) do przeprowadzenia konwersji z buforów na strumienie w celu użycia klasy GZipStream.
Klasy buforowane ReadMessage i WriteMessage wykorzystują klasę BufferManager. Koder jest dostępny tylko za pośrednictwem fabryki kodera. Klasa abstrakcyjna MessageEncoderFactory udostępnia właściwość o nazwie Encoder na potrzeby uzyskiwania dostępu do bieżącego kodera i metody o nazwie CreateSessionEncoder do tworzenia kodera obsługującego sesje. Taki koder może być używany w scenariuszu, w którym kanał obsługuje sesje, jest uporządkowany i niezawodny. Ten scenariusz umożliwia optymalizację podczas każdej sesji transmisji danych przez sieć. Jeśli nie jest to pożądane, metoda podstawowa nie powinna być przeciążona. Właściwość Encoder udostępnia mechanizm uzyskiwania dostępu do kodera bez sesji, a domyślna implementacja CreateSessionEncoder metody zwraca wartość właściwości. Ponieważ przykład opakowuje istniejący koder, aby zapewnić kompresję, implementacja MessageEncoderFactory akceptuje MessageEncoderFactory, które reprezentuje fabrykę kodera wewnętrznego.
Po zdefiniowaniu kodera i fabryki kodowania można ich używać z klientem i usługą WCF. Jednak te kodery należy dodać do stosu kanałów. Można dziedziczyć klasy z ServiceHost i ChannelFactory<TChannel> oraz zastąpić metody OnInitialize, aby ręcznie dodać tę fabrykę kodera. Fabrykę kodera można również uwidocznić za pomocą niestandardowego elementu powiązania.
Aby utworzyć nowy niestandardowy element powiązania, utwórz klasę z BindingElement klasy . Istnieje jednak kilka typów elementów powiązania. Aby upewnić się, że niestandardowy element powiązania jest rozpoznawany jako element powiązania kodowania komunikatów, należy również zaimplementować element MessageEncodingBindingElement. Metoda MessageEncodingBindingElement uwidacznia metodę tworzenia nowej fabryki kodera komunikatów (CreateMessageEncoderFactory), która jest implementowana w celu zwrócenia wystąpienia zgodnej fabryki kodera komunikatów. Ponadto właściwość MessageEncodingBindingElement ma właściwość wskazującą wersję adresowania. Ponieważ ten przykład opakowuje istniejące kodery, przykładowa implementacja również opakowuje istniejące elementy powiązania kodera i przyjmuje jako parametr konstruktora wewnętrzny element powiązania kodera, udostępniając go za pośrednictwem właściwości. Poniższy przykładowy kod przedstawia implementację GZipMessageEncodingBindingElement klasy.
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));
}
}
Należy pamiętać, że GZipMessageEncodingBindingElement klasa implementuje IPolicyExportExtension interfejs, dzięki czemu ten element powiązania można wyeksportować jako politykę do metadanych, jak pokazano w poniższym przykładzie.
<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>
Klasa GZipMessageEncodingBindingElementImporter implementuje interfejs IPolicyImportExtension, a także importuje zasady dla GZipMessageEncodingBindingElement. Svcutil.exe narzędzie może służyć do importowania zasad do pliku konfiguracji. Aby obsługiwać GZipMessageEncodingBindingElement, należy dodać następujące elementy do 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>
Teraz, gdy istnieje pasujący element powiązania dla kodera kompresji, można go programowo podłączyć do usługi lub klienta, tworząc nowy obiekt powiązania niestandardowego i dodając do niego niestandardowy element powiązania, jak pokazano w poniższym przykładowym kodzie.
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";
Chociaż może to być wystarczające w przypadku większości scenariuszy użytkownika, obsługa konfiguracji plików ma kluczowe znaczenie, jeśli usługa ma być hostowana w sieci Web. Aby obsługiwać scenariusz hostowany w sieci Web, należy opracować niestandardową procedurę obsługi konfiguracji, aby umożliwić skonfigurowanie niestandardowego elementu powiązania w pliku.
Można utworzyć procedurę obsługi konfiguracji dla elementu powiązania w oparciu o system konfiguracji. Procedura obsługi konfiguracji elementu powiązania musi pochodzić z BindingElementExtensionElement klasy . Element BindingElementExtensionElement.BindingElementType informuje system konfiguracji o typie elementu powiązania, który ma zostać utworzony dla tej sekcji. Wszystkie aspekty BindingElement , które można ustawić, powinny być uwidocznione jako właściwości w klasie pochodnej BindingElementExtensionElement .
ConfigurationPropertyAttribute pomaga w mapowaniu atrybutów elementu konfiguracji na właściwości oraz w ustawianiu wartości domyślnych, gdy brakuje atrybutów. Po załadowaniu i zastosowaniu wartości z konfiguracji do właściwości BindingElementExtensionElement.CreateBindingElement wywoływana jest metoda, która konwertuje właściwości na konkretne wystąpienie elementu powiązania. Metoda BindingElementExtensionElement.ApplyConfiguration służy do konwertowania właściwości klasy pochodnej BindingElementExtensionElement na wartości, które mają zostać ustawione na nowo utworzony element powiązania.
Poniższy przykładowy kod przedstawia implementację pliku 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;
}
}
Ten moduł konfiguracji odpowiada następującej reprezentacji w App.config lub Web.config w przypadku usługi lub klienta.
<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
Aby użyć tej procedury obsługi konfiguracji, należy ją zarejestrować w elemecie <system.serviceModel> , jak pokazano w poniższej przykładowej konfiguracji.
<extensions>
<bindingElementExtensions>
<add
name="gzipMessageEncoding"
type=
"Microsoft.ServiceModel.Samples.GZipMessageEncodingElement,
GZipEncoder, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
Po uruchomieniu serwera żądania operacji i odpowiedzi są wyświetlane w oknie konsoli. Naciśnij ENTER w oknie, aby zamknąć serwer.
Press Enter key to Exit.
Server Echo(string input) called:
Client message: Simple hello
Server BigEcho(string[] input) called:
64 client messages
Po uruchomieniu klienta żądania operacji i odpowiedzi są wyświetlane w oknie konsoli. Naciśnij ENTER w oknie klienta, aby zamknąć klienta.
Calling Echo(string):
Server responds: Simple hello Simple hello
Calling BigEcho(string[]):
Server responds: Hello 0
Press <ENTER> to terminate client.
Aby skonfigurować, skompilować i uruchomić przykładowy program
Zainstaluj ASP.NET 4.0 przy użyciu następującego polecenia:
%windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enableUpewnij się, że wykonano procedurę instalacji One-Time dla przykładów programu Windows Communication Foundation.
Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).
Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w Uruchamianie przykładów programu Windows Communication Foundation.