Publicação personalizada de WSDL
O exemplo WsdlDocumentation demonstra como:
Implementar um System.ServiceModel.Description.IWsdlExportExtension em um atributo System.ServiceModel.Description.IContractBehavior personalizado para exportar propriedades de atributo como anotações WSDL.
Implementar System.ServiceModel.Description.IWsdlImportExtension para importar as anotações personalizadas do WSDL.
Implementar System.ServiceModel.Description.IServiceContractGenerationExtension e System.ServiceModel.Description.IOperationContractGenerationExtension em um comportamento de contrato personalizado e um comportamento de operação personalizado, respectivamente, para gravar anotações importadas como comentários no CodeDom para o contrato e a operação importados.
Usar o System.ServiceModel.Description.MetadataExchangeClient para baixar o WSDL, um System.ServiceModel.Description.WsdlImporter para importar o WSDL usando o importador WSDL personalizado e o System.ServiceModel.Description.ServiceContractGenerator para gerar código do cliente WCF (Windows Communication Foundation) com as anotações WSDL como /// e comentários ''' em C# e Visual Basic.
Observação
O procedimento de instalação e as instruções de compilação dessa amostra estão no final deste tópico.
Serviço
O serviço nesse exemplo é marcado com dois atributos personalizados. O primeiro, o WsdlDocumentationAttribute
, aceita uma cadeia de caracteres no construtor e pode ser aplicado para fornecer uma interface ou operação de contrato com uma cadeia de caracteres que descreve seu uso. O segundo, WsdlParamOrReturnDocumentationAttribute
, pode ser aplicado a valores ou parâmetros retornados para descrever esses valores na operação. O exemplo a seguir mostra um contrato de serviço, ICalculator
, descrito usando esses atributos.
// 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
);
}
O WsdlDocumentationAttribute
implementa IContractBehavior e IOperationBehavior, portanto, as instâncias de atributo são adicionadas ao correspondente ContractDescription ou OperationDescription quando o serviço é aberto. O atributo também implementa IWsdlExportExtension. Quando ExportContract(WsdlExporter, WsdlContractConversionContext) é chamado, o WsdlExporter que é usado para exportar os metadados e o WsdlContractConversionContext que contém os objetos de descrição do serviço são passados como parâmetros que permitem a modificação dos metadados exportados.
Neste exemplo, dependendo se o objeto de contexto de exportação tem um ContractDescription ou um OperationDescription, um comentário é extraído do atributo usando a propriedade de texto e é adicionado ao elemento de anotação WSDL, conforme mostrado no código a seguir.
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);
}
}
}
Se uma operação estiver sendo exportada, o exemplo usará reflexão para obter quaisquer valores WsdlParamOrReturnDocumentationAttribute
para parâmetros e valores retornados e os adicionará aos elementos de anotação WSDL para essa operação da seguinte maneira.
// 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);
}
}
Em seguida, o exemplo publica metadados da maneira padrão, usando o arquivo de configuração a seguir.
<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>
Cliente Svcutil
Este exemplo não usa Svcutil.exe. O contrato é fornecido no arquivo generatedClient.cs para que, depois que o exemplo demonstrar a importação e a geração de código WSDL personalizadas, o serviço possa ser invocado. Para usar o seguinte importador WSDL personalizado neste exemplo, você pode executar Svcutil.exe e especificar a opção /svcutilConfig
, fornecendo o caminho para o arquivo de configuração do cliente usado neste exemplo, que faz referência à biblioteca WsdlDocumentation.dll
. No entanto, para carregar o WsdlDocumentationImporter
, o Svuctil.exe precisa ser capaz de localizar e carregar a biblioteca, o WsdlDocumentation.dll
que significa que ela está registrada no cache de assembly global, no caminho ou está no mesmo diretório que Svcutil.exe. Para um exemplo básico como este, a coisa mais fácil de fazer é copiar Svcutil.exe e o arquivo de configuração do cliente no mesmo diretório que WsdlDocumentation.dll
e executá-lo a partir daí.
O importador WSDL personalizado
O WsdlDocumentationImporter
do objeto IWsdlImportExtension personalizado também implementa IContractBehavior e IOperationBehavior, que devem ser adicionados aos ServiceEndpoints importados, e IServiceContractGenerationExtension e IOperationContractGenerationExtension devem ser invocados para modificar a geração de código quando o contrato ou código de operação estiver sendo criado.
Primeiro, no método ImportContract(WsdlImporter, WsdlContractConversionContext), o exemplo determina se a anotação WSDL está no nível de contrato ou operação e se adiciona como um comportamento no escopo apropriado, passando o texto de anotação importado para seu construtor.
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));
}
}
}
}
Em seguida, quando o código é gerado, o sistema invoca os métodos GenerateContract(ServiceContractGenerationContext) e GenerateOperation(OperationContractGenerationContext), passando as informações de contexto apropriadas. O exemplo formata as anotações personalizadas do WSDL e as insere como comentários no 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.");
}
O aplicativo cliente
O aplicativo cliente carrega o importador WSDL personalizado especificando-o no arquivo de configuração do aplicativo.
<client>
<endpoint address="http://localhost/servicemodelsamples/service.svc"
binding="wsHttpBinding"
contract="ICalculator" />
<metadata>
<wsdlImporters>
<extension type="Microsoft.ServiceModel.Samples.WsdlDocumentationImporter, WsdlDocumentation"/>
</wsdlImporters>
</metadata>
</client>
Depois que o importador personalizado for especificado, o sistema de metadados do WCF carregará o importador personalizado em qualquer WsdlImporter criado para essa finalidade. Este exemplo usa o MetadataExchangeClient para baixar os metadados, o WsdlImporter configurado corretamente para importar os metadados usando o importador personalizado que o exemplo cria e o ServiceContractGenerator para compilar as informações de contrato modificadas no código cliente do Visual Basic e do C# que podem ser usados no Visual Studio para dar suporte ao Intellisense ou compilados na documentação 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);
}
Para configurar, compilar, e executar o exemplo
Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para compilar a edição .NET do C# ou do Visual Basic da solução, siga as instruções contidas em Como Compilar as Amostras do Windows Communication Foundation.
Para executar a amostra em uma configuração de computador único ou entre computadores, siga as instruções contidas em Como executar as amostras do Windows Communication Foundation.