Udostępnij za pośrednictwem


Niestandardowa publikacja WSDL

Przykład WsdlDocumentation pokazuje, jak:

Uwaga / Notatka

Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego tematu.

Usługa

Usługa w tym przykładzie jest oznaczona dwoma atrybutami niestandardowymi. Pierwszy z nich WsdlDocumentationAttribute przyjmuje ciąg w konstruktorze i może być użyty do dostarczenia interfejsu kontraktowego lub operacji z ciągiem opisującym jego zastosowanie. Drugi element , WsdlParamOrReturnDocumentationAttributemożna zastosować do zwracania wartości lub parametrów w celu opisania tych wartości w operacji. W poniższym przykładzie przedstawiono kontrakt usługi , ICalculatoropisany przy użyciu tych atrybutów.

// Define a service contract.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
// Document it.
[WsdlDocumentation("The ICalculator contract performs basic calculation services.")]
public interface ICalculator
{
    [OperationContract]
    [WsdlDocumentation("The Add operation adds two numbers and returns the result.")]
    [return:WsdlParamOrReturnDocumentation("The result of adding the two arguments together.")]
    double Add(
      [WsdlParamOrReturnDocumentation("The first value to add.")]double n1,
      [WsdlParamOrReturnDocumentation("The second value to add.")]double n2
    );

    [OperationContract]
    [WsdlDocumentation("The Subtract operation subtracts the second argument from the first.")]
    [return:WsdlParamOrReturnDocumentation("The result of the second argument subtracted from the first.")]
    double Subtract(
      [WsdlParamOrReturnDocumentation("The value from which the second is subtracted.")]double n1,
      [WsdlParamOrReturnDocumentation("The value that is subtracted from the first.")]double n2
    );

    [OperationContract]
    [WsdlDocumentation("The Multiply operation multiplies two values.")]
    [return:WsdlParamOrReturnDocumentation("The result of multiplying the first and second arguments.")]
    double Multiply(
      [WsdlParamOrReturnDocumentation("The first value to multiply.")]double n1,
      [WsdlParamOrReturnDocumentation("The second value to multiply.")]double n2
    );

    [OperationContract]
    [WsdlDocumentation("The Divide operation returns the value of the first argument divided by the second argument.")]
    [return:WsdlParamOrReturnDocumentation("The result of dividing the first argument by the second.")]
    double Divide(
      [WsdlParamOrReturnDocumentation("The numerator.")]double n1,
      [WsdlParamOrReturnDocumentation("The denominator.")]double n2
    );
}

WsdlDocumentationAttribute implementuje IContractBehavior i IOperationBehavior, więc wystąpienia atrybutów są dodawane do odpowiednich ContractDescription lub OperationDescription po otwarciu usługi. Atrybut również implementuje IWsdlExportExtension. Gdy wywoływana jest ExportContract(WsdlExporter, WsdlContractConversionContext), WsdlExporter, który jest używany do eksportowania metadanych, oraz WsdlContractConversionContext, który zawiera obiekty opisu usługi, są przekazywane jako parametry umożliwiające modyfikację wyeksportowanych metadanych.

W tym przykładzie, w zależności od tego, czy obiekt kontekstu eksportu ma ContractDescription czy OperationDescription, komentarz jest wyodrębniany z atrybutu przy użyciu właściwości tekst i jest dodawany do elementu adnotacji WSDL, jak pokazano w poniższym kodzie.

public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
{
    if (contractDescription != null)
    {
        // Inside this block it is the contract-level comment attribute.
        // This.Text returns the string for the contract attribute.
        // Set the doc element; if this isn't done first, there is no XmlElement in the
        // DocumentElement property.
        context.WsdlPortType.Documentation = string.Empty;
        // Contract comments.
        XmlDocument owner = context.WsdlPortType.DocumentationElement.OwnerDocument;
        XmlElement summaryElement = owner.CreateElement("summary");
        summaryElement.InnerText = this.Text;
        context.WsdlPortType.DocumentationElement.AppendChild(summaryElement);
    }
    else
    {
        Operation operation = context.GetOperation(operationDescription);
        if (operation != null)
        {
            // We are dealing strictly with the operation here.
            // This.Text returns the string for the operation-level attributes.
            // Set the doc element; if this isn't done first, there is no XmlElement in the
            // DocumentElement property.
            operation.Documentation = String.Empty;

            // Operation C# triple comments.
            XmlDocument owner = operation.DocumentationElement.OwnerDocument;
            XmlElement newSummaryElement = owner.CreateElement("summary");
            newSummaryElement.InnerText = this.Text;
            operation.DocumentationElement.AppendChild(newSummaryElement);
        }
    }
}

Jeśli operacja jest eksportowana, przykład używa odbicia w celu uzyskania dowolnych WsdlParamOrReturnDocumentationAttribute wartości parametrów i zwracanych wartości i dodaje je do elementów adnotacji WSDL dla tej operacji w następujący sposób.

// Get returns information
ParameterInfo returnValue = operationDescription.SyncMethod.ReturnParameter;
object[] returnAttrs = returnValue.GetCustomAttributes(typeof(WsdlParamOrReturnDocumentationAttribute), false);
if (returnAttrs.Length != 0)
{
    // <returns>text.</returns>
    XmlElement returnsElement = owner.CreateElement("returns");
    returnsElement.InnerText = ((WsdlParamOrReturnDocumentationAttribute)returnAttrs[0]).ParamComment;
    operation.DocumentationElement.AppendChild(returnsElement);
}

// Get parameter information.
ParameterInfo[] args = operationDescription.SyncMethod.GetParameters();
for (int i = 0; i < args.Length; i++)
{
    object[] docAttrs = args[i].GetCustomAttributes(typeof(WsdlParamOrReturnDocumentationAttribute), false);
    if (docAttrs.Length == 1)
    {
        // <param name="Int1">Text.</param>
        XmlElement newParamElement = owner.CreateElement("param");
        XmlAttribute paramName = owner.CreateAttribute("name");
        paramName.Value = args[i].Name;
        newParamElement.InnerText = ((WsdlParamOrReturnDocumentationAttribute)docAttrs[0]).ParamComment;
        newParamElement.Attributes.Append(paramName);
        operation.DocumentationElement.AppendChild(newParamElement);
    }
}

Następnie przykład publikuje metadane w standardowy sposób przy użyciu następującego pliku konfiguracji.

<services>
  <service
      name="Microsoft.ServiceModel.Samples.CalculatorService"
      behaviorConfiguration="CalculatorServiceBehavior">
    <!-- ICalculator is exposed at the base address provided by host: http://localhost/servicemodelsamples/service.svc  -->
    <endpoint address=""
              binding="wsHttpBinding"
              contract="Microsoft.ServiceModel.Samples.ICalculator" />
    <!-- the mex endpoint is exposed at http://localhost/servicemodelsamples/service.svc/mex -->
    <endpoint address="mex"
              binding="mexHttpBinding"
              contract="IMetadataExchange" />
  </service>
</services>

<!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
<behaviors>
  <serviceBehaviors>
    <behavior name="CalculatorServiceBehavior">
      <serviceMetadata httpGetEnabled="True"/>
      <serviceDebug includeExceptionDetailInFaults="False" />
    </behavior>
  </serviceBehaviors>
</behaviors>

Klient Svcutil

Ten przykład nie używa Svcutil.exe. Kontrakt jest udostępniany w pliku generatedClient.cs, tak aby po pokazaniu importowania niestandardowego WSDL i generowania kodu, można było wywołać usługę. Aby użyć niestandardowego importera WSDL w tym przykładzie, można uruchomić Svcutil.exe i określić opcję /svcutilConfig, podając ścieżkę do pliku konfiguracji klienta używanego w tym przykładzie. Plik ten odwołuje się do biblioteki WsdlDocumentation.dll. Aby załadować element WsdlDocumentationImporter, Svuctil.exe musi mieć możliwość zlokalizowania i załadowania biblioteki WsdlDocumentation.dll, co oznacza, że biblioteka musi być zarejestrowana w globalnej pamięci podręcznej zestawów .NET, w zmiennej środowiskowej PATH albo znajdować się w tym samym katalogu co Svcutil.exe. W przypadku przykładu podstawowego, takiego jak ten, najłatwiej jest skopiować Svcutil.exe i plik konfiguracji klienta do tego samego katalogu, co WsdlDocumentation.dll i uruchomić go stamtąd.

Importer niestandardowy WSDL

Niestandardowy obiekt IWsdlImportExtension również implementuje WsdlDocumentationImporter i IContractBehavior aby został dodany do zaimportowanych punktów końcowych usługi i IOperationBehavior i IServiceContractGenerationExtension, które służą do wywoływania w celu modyfikacji generowania kodu podczas tworzenia kontraktu lub kodu operacji.

Najpierw w metodzie ImportContract(WsdlImporter, WsdlContractConversionContext) próbka określa, czy adnotacja WSDL jest na poziomie kontraktu czy operacji, i dodaje się jako element zachowania w odpowiednim zakresie, przekazując importowany tekst adnotacji do konstruktora.

public void ImportContract(WsdlImporter importer, WsdlContractConversionContext context)
{
    // Contract Documentation
    if (context.WsdlPortType.Documentation != null)
    {
        // System examines the contract behaviors to see whether any implement IWsdlImportExtension.
        context.Contract.Behaviors.Add(new WsdlDocumentationImporter(context.WsdlPortType.Documentation));
    }
    // Operation Documentation
    foreach (Operation operation in context.WsdlPortType.Operations)
    {
        if (operation.Documentation != null)
        {
            OperationDescription operationDescription = context.Contract.Operations.Find(operation.Name);
            if (operationDescription != null)
            {
                // System examines the operation behaviors to see whether any implement IWsdlImportExtension.
                operationDescription.Behaviors.Add(new WsdlDocumentationImporter(operation.Documentation));
            }
        }
    }
}

Następnie, gdy kod jest generowany, system wywołuje metody GenerateContract(ServiceContractGenerationContext) i GenerateOperation(OperationContractGenerationContext), przekazując odpowiednie informacje kontekstowe. Przykład formatuje niestandardowe adnotacje WSDL i wstawia je jako komentarze do obiektu CodeDom.

public void GenerateContract(ServiceContractGenerationContext context)
{
    Debug.WriteLine("In generate contract.");
    context.ContractType.Comments.AddRange(FormatComments(text));
}

public void GenerateOperation(OperationContractGenerationContext context)
{
    context.SyncMethod.Comments.AddRange(FormatComments(text));
    Debug.WriteLine("In generate operation.");
}

Aplikacja kliencka

Aplikacja kliencka ładuje niestandardowy importer WSDL, określając go w pliku konfiguracji aplikacji.

<client>
  <endpoint address="http://localhost/servicemodelsamples/service.svc"
  binding="wsHttpBinding"
  contract="ICalculator" />
  <metadata>
    <wsdlImporters>
      <extension type="Microsoft.ServiceModel.Samples.WsdlDocumentationImporter, WsdlDocumentation"/>
    </wsdlImporters>
  </metadata>
</client>

Po określeniu niestandardowego importera system metadanych programu WCF ładuje niestandardowy importer do dowolnego WsdlImporter utworzonego w tym celu. W tym przykładzie użyto elementu MetadataExchangeClient, aby pobrać metadane, elementu WsdlImporter, który został prawidłowo skonfigurowany do zaimportowania metadanych za pomocą niestandardowego importera stworzonego przez przykład, oraz elementu ServiceContractGenerator, aby skompilować zmodyfikowane informacje o kontrakcie do kodu klienta zarówno w języku Visual Basic, jak i C#, który można użyć w programie Visual Studio do obsługi funkcji IntelliSense lub skompilować do dokumentacji XML.

/// From WSDL Documentation:
///
/// <summary>The ICalculator contract performs basic calculation
/// services.</summary>
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://Microsoft.ServiceModel.Samples", ConfigurationName="ICalculator")]
public interface ICalculator
{

    /// From WSDL Documentation:
    ///
    /// <summary>The Add operation adds two numbers and returns the
    /// result.</summary><returns>The result of adding the two arguments
    /// together.</returns><param name="n1">The first value to add.</param><param
    /// name="n2">The second value to add.</param>
    ///
    [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")]
    double Add(double n1, double n2);

    /// From WSDL Documentation:
    ///
    /// <summary>The Subtract operation subtracts the second argument from the
    /// first.</summary><returns>The result of the second argument subtracted from the
    /// first.</returns><param name="n1">The value from which the second is
    /// subtracted.</param><param name="n2">The value that is subtracted from the
    /// first.</param>
    ///
    [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Subtract", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/SubtractResponse")]
    double Subtract(double n1, double n2);

    /// From WSDL Documentation:
    ///
    /// <summary>The Multiply operation multiplies two values.</summary><returns>The
    /// result of multiplying the first and second arguments.</returns><param
    /// name="n1">The first value to multiply.</param><param name="n2">The second value
    /// to multiply.</param>
    ///
    [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Multiply", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/MultiplyResponse")]
    double Multiply(double n1, double n2);

    /// From WSDL Documentation:
    ///
    /// <summary>The Divide operation returns the value of the first argument divided
    /// by the second argument.</summary><returns>The result of dividing the first
    /// argument by the second.</returns><param name="n1">The numerator.</param><param
    /// name="n2">The denominator.</param>
    ///
    [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Divide", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/DivideResponse")]
    double Divide(double n1, double n2);
}

Aby skonfigurować, skompilować i uruchomić przykładowy program

  1. Upewnij się, że wykonano procedurę instalacji One-Time dla przykładów programu Windows Communication Foundation.

  2. Aby skompilować wersję rozwiązania w języku C# lub Visual Basic .NET, postępuj zgodnie z instrukcjami w Kompilowanie przykładów Windows Communication Foundation.

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