WsdlDocumentation 示例演示如何:
将 System.ServiceModel.Description.IWsdlExportExtension 实施在自定义 System.ServiceModel.Description.IContractBehavior 属性上,以将属性的属性导出为 WSDL 注释。
实现 System.ServiceModel.Description.IWsdlImportExtension 以导入自定义 WSDL 批注。
分别在自定义契约行为和自定义操作行为上实现 System.ServiceModel.Description.IServiceContractGenerationExtension 和 System.ServiceModel.Description.IOperationContractGenerationExtension,以便在 CodeDom 中将导入的注释作为导入的契约和操作的评论。
使用 System.ServiceModel.Description.MetadataExchangeClient 下载 WSDL,使用 System.ServiceModel.Description.WsdlImporter 自定义 WSDL 导入器导入 WSDL,使用 System.ServiceModel.Description.ServiceContractGenerator 生成包含 WSDL 注释的 Windows Communication Foundation(WCF)客户端代码,注释在 C# 中为 ///,在 Visual Basic 中为 '''。
注释
本示例的设置过程和生成说明位于本主题末尾。
服务
此示例中的服务标记有两个自定义属性。 第一个WsdlDocumentationAttribute
在构造函数中接受一个字符串,可以用来提供一个描述其用法的字符串的契约接口或操作。 第二个WsdlParamOrReturnDocumentationAttribute
可以应用于返回值或参数,以描述操作中的这些值。 以下示例显示了使用这些属性描述的服务协定 ICalculator
。
// 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
实现IContractBehavior和IOperationBehavior,因此在服务打开时,属性实例将被添加到相应的ContractDescription或OperationDescription。 该属性还实现 IWsdlExportExtension。 当调用 ExportContract(WsdlExporter, WsdlContractConversionContext) 时, WsdlExporter 用于导出元数据,而包含服务说明对象的 WsdlContractConversionContext 作为参数传入,从而能够修改导出的元数据。
在此示例中,根据导出上下文对象是否具有一个 ContractDescription 或一个 OperationDescription注释,使用文本属性从属性中提取注释,并将其添加到 WSDL 注释元素,如以下代码所示。
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);
}
}
}
如果导出的是操作,该示例则使用反射来获取参数的任何 WsdlParamOrReturnDocumentationAttribute
值和返回值,并将它们添加到该操作的 WSDL 批注元素中,如下所示。
// 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);
}
}
然后,此示例使用以下配置文件以标准方式发布元数据。
<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>
Svcutil 客户端
此示例不使用 Svcutil.exe。 协定在generatedClient.cs文件中提供,以便在示例演示自定义 WSDL 导入和代码生成后,可以调用该服务。 若要对此示例使用以下自定义 WSDL 导入程序,可以运行 Svcutil.exe 并指定 /svcutilConfig
选项,从而提供此示例中使用的客户端配置文件的路径,该配置文件引用库 WsdlDocumentation.dll
。 但是,若要加载该 WsdlDocumentationImporter
库,Svuctil.exe 必须能够找到和加载 WsdlDocumentation.dll
库,这意味着它已注册到全局程序集缓存、路径中或位于与 Svcutil.exe相同的目录中。 对于这个基本示例,最简单的方法是将 Svcutil.exe 和客户端配置文件复制到与 WsdlDocumentation.dll
相同的目录中,并从该目录运行它。
自定义 WSDL 导入程序
自定义 IWsdlImportExtension 对象 WsdlDocumentationImporter
还实现要添加到导入的 ServiceEndpoints 中的 IContractBehavior 和 IOperationBehavior,以及要在创建协定或操作代码时为了修改代码而调用的 IServiceContractGenerationExtension 和 IOperationContractGenerationExtension。
首先,在 ImportContract(WsdlImporter, WsdlContractConversionContext) 方法中,该示例确定 WSDL 批注是在协定级别还是操作级别,并在相应的范围将其自身添加为一个行为,从而将导入的批注文本传递给其构造函数。
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));
}
}
}
}
然后,生成代码时,系统会调用 GenerateContract(ServiceContractGenerationContext) 和 GenerateOperation(OperationContractGenerationContext) 方法,并传递相应的上下文信息。 该示例设置自定义 WSDL 批注的格式,并将其作为注释插入 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.");
}
客户端应用程序
客户端应用程序通过在应用程序配置文件中指定自定义 WSDL 导入程序来加载该导入程序。
<client>
<endpoint address="http://localhost/servicemodelsamples/service.svc"
binding="wsHttpBinding"
contract="ICalculator" />
<metadata>
<wsdlImporters>
<extension type="Microsoft.ServiceModel.Samples.WsdlDocumentationImporter, WsdlDocumentation"/>
</wsdlImporters>
</metadata>
</client>
指定自定义导入程序后,WCF 元数据系统会将自定义导入程序加载到为该目的创建的任何 WsdlImporter 导入程序。 此示例使用 MetadataExchangeClient 下载元数据,使用经过正确配置的 WsdlImporter 将元数据通过示例创建的自定义导入程序导入,并使用 ServiceContractGenerator 将修改后的契约信息编译到 Visual Basic 和 C# 客户端代码中,这些代码可用于在 Visual Studio 中支持 Intellisense 或编译为 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);
}
设置、生成和运行示例
确保已为 Windows Communication Foundation 示例 执行One-Time 安装过程。
若要生成解决方案的 C# 或 Visual Basic .NET 版本,请按照 生成 Windows Communication Foundation 示例中的说明进行操作。
若要在单台计算机或跨计算机配置中运行示例,请按照 运行 Windows Communication Foundation 示例中的说明进行操作。