从开发角度比较 ASP.NET Web 服务与 WCF

Windows Communication Foundation(WCF)具有 ASP.NET 兼容模式选项,使 WCF 应用程序能够像 ASP.NET Web 服务一样编程和配置,并模拟其行为。 以下各节根据使用这两种技术开发应用程序所需的内容比较 ASP.NET Web 服务和 WCF。

数据表示形式

使用 ASP.NET 的 Web 服务的开发通常从定义服务要使用的任何复杂数据类型开始。 ASP.NET 依赖于 XmlSerializer 将 .NET Framework 类型表示的数据转换为 XML,以便传入或传出服务并将作为 XML 接收的数据转换为 .NET Framework 对象。 定义 ASP.NET 服务要使用的复杂数据类型需要使用 .NET Framework 类的定义,这些类可由 XmlSerializer 序列化为 XML 或从 XML 进行反序列化。 可以使用命令行 XML 架构/数据类型支持实用工具(xsd.exe)手动编写此类,也可以从 XML 架构中的类型定义生成。

下面是定义可由 XmlSerializer 序列化为 XML 或从 XML 序列化的 .NET Framework 类时需要了解的关键问题列表:

  • 只有 .NET Framework 对象的公共字段和属性才会转换为 XML。

  • 仅当类实现 IEnumerableICollection 接口时,集合类的实例才能序列化为 XML。

  • 实现 IDictionary 接口的类(如 Hashtable)无法序列化为 XML。

  • 命名空间中的 System.Xml.Serialization 大量属性类型可以添加到 .NET Framework 类及其成员,以控制类的实例在 XML 中的表示方式。

WCF 应用程序开发通常也从复杂类型的定义开始。 WCF 可以使用与 ASP.NET Web 服务相同的 .NET Framework 类型。

WCFDataContractAttributeDataMemberAttribute 可以添加到 .NET Framework 类型,以指示类型的实例要序列化为 XML,以及要序列化该类型的特定字段或属性,如以下示例代码所示。

//Example One:
[DataContract]
public class LineItem
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}

//Example Two:
public class LineItem
{
    [DataMember]
    private string itemNumber;
    [DataMember]
    private decimal quantity;
    [DataMember]
    private decimal unitPrice;

    public string ItemNumber
    {
      get
      {
          return this.itemNumber;
      }

      set
      {
          this.itemNumber = value;
      }
    }

    public decimal Quantity
    {
        get
        {
            return this.quantity;
        }

        set
        {
            this.quantity = value;
        }
    }

    public decimal UnitPrice
    {
      get
      {
          return this.unitPrice;
      }

      set
      {
          this.unitPrice = value;
      }
    }
}

//Example Three:
public class LineItem
{
     private string itemNumber;
     private decimal quantity;
     private decimal unitPrice;

     [DataMember]
     public string ItemNumber
     {
       get
       {
          return this.itemNumber;
       }

       set
       {
           this.itemNumber = value;
       }
     }

     [DataMember]
     public decimal Quantity
     {
          get
          {
              return this.quantity;
          }

          set
          {
             this.quantity = value;
          }
     }

     [DataMember]
     public decimal UnitPrice
     {
          get
          {
              return this.unitPrice;
          }

          set
          {
              this.unitPrice = value;
          }
     }
}

DataContractAttribute 表示可以序列化零个或多个类型的字段或属性,而 DataMemberAttribute 则指示要序列化特定字段或属性。 DataContractAttribute可以应用于类或结构。 DataMemberAttribute可以应用于字段或属性,应用属性的字段和属性可以是公共的或私有的。 在 WCF 中,被应用 DataContractAttribute 的类型实例称为数据契约。 它们通过 DataContractSerializer 序列化为 XML。

下面是使用 DataContractSerializerXmlSerializer 之间的重要差异,以及 System.Xml.Serialization 命名空间的各种属性的列表。

  • 命名空间 XmlSerializer 的属性 System.Xml.Serialization 旨在使你能够将 .NET Framework 类型映射到 XML 架构中定义的任何有效类型,因此它们提供对 XML 中类型表示方式的非常精确的控制。 DataContractSerializerDataContractAttributeDataMemberAttribute对如何在XML中表示类型提供了非常有限的控制。 只能指定用于表示 XML 中的类型及其字段或属性的命名空间和名称,以及字段和属性出现在 XML 中的序列:

    [DataContract(
    Namespace="urn:Contoso:2006:January:29",
    Name="LineItem")]
    public class LineItem
    {
          [DataMember(Name="ItemNumber",IsRequired=true,Order=0)]
          public string itemNumber;
          [DataMember(Name="Quantity",IsRequired=false,Order = 1)]
          public decimal quantity;
          [DataMember(Name="Price",IsRequired=false,Order = 2)]
          public decimal unitPrice;
    }
    

    有关用于表示 .NET 类型的 XML 结构的其他所有内容都由 DataContractSerializer该属性决定。

  • 通过不允许过多地控制如何以 XML 方式表示类型,DataContractSerializer 序列化过程的可预知程度变得很高,因此,更容易实现优化。 设计 DataContractSerializer 的一个实际好处是性能提高了,大约提高了百分之十。

  • 用于XmlSerializer的属性不会指示类型的哪些字段或属性被序列化为XML,而用于DataMemberAttributeDataContractSerializer会明确地显示哪些字段或属性被序列化。 因此,数据协定是有关应用程序发送和接收的数据结构的显式协定。

  • XmlSerializer 只能将 .NET 对象的公共成员转换为 XML,而 DataContractSerializer 则可以无视成员的访问修饰符,将对象的所有成员转换为 XML。

  • 由于能够将类型的非公共成员序列化为 XML,因此 DataContractSerializer 对可以序列化为 XML 的各种 .NET 类型的限制更少。 具体而言,它可以转换为实现 Hashtable 接口的类似于 IDictionary 的 XML 类型。 更有可能 DataContractSerializer 能够将任何预先存在的 .NET 类型的实例序列化为 XML,而无需修改类型的定义或为其开发包装器。

  • 由于 DataContractSerializer 可以访问类型的非公共成员,因此,它还要求完全信任,而 XmlSerializer 不要求。 使用完全信任代码访问权限,即可完全访问可使用执行代码所用的凭据访问的计算机中的所有资源。 此选项应谨慎使用,因为完全受信任的代码会访问计算机上的所有资源。

  • DataContractSerializer 包含对版本控制的某些支持:

    • DataMemberAttribute 具有一个 IsRequired 属性,将它赋予 False 值可指示早期版本中不存在将成员添加到的新版本数据协定,因此使得包含新版协定的应用程序可以处理早期的版本。

    • 通过让数据协定实现 IExtensibleDataObject 接口,可以允许 DataContractSerializer 通过具有早期版本的协定的应用程序传递在较新版本的数据协定中定义的成员。

尽管存在所有差异,但假如 XML 的命名空间被显式定义,则 XmlSerializer 默认情况下序列化的类型 XML 在语义上与 DataContractSerializer 序列化的类型 XML 相同。 以下类具有用于这两个序列化程序的属性,通过XmlSerializerDataContractAttribute转换为语义上相同的XML:

[Serializable]
[XmlRoot(Namespace="urn:Contoso:2006:January:29")]
[DataContract(Namespace="urn:Contoso:2006:January:29")]
public class LineItem
{
     [DataMember]
     public string ItemNumber;
     [DataMember]
     public decimal Quantity;
     [DataMember]
     public decimal UnitPrice;
}

Windows 软件开发工具包(SDK)包括名为 ServiceModel 元数据实用工具工具(Svcutil.exe)的命令行工具。 与与 ASP.NET Web 服务一起使用的 xsd.exe 工具一样,Svcutil.exe 可以从 XML 架构生成 .NET 数据类型的定义。 如果 DataContractSerializer 可以使用 XML 架构定义的格式发出 XML,则类型为数据契约;否则,它们用于使用 XmlSerializer 进行序列化。 Svcutil.exe 还可以使用其 dataContractOnly 开关从数据协定生成 XML 架构。

注释

尽管 ASP.NET Web 服务使用 XmlSerializer,并且 WCF 的 ASP.NET 兼容模式使 WCF 服务可以模仿 ASP.NET Web 服务的行为,但 ASP.NET 兼容性选项不会限制使用 XmlSerializer。 在 ASP.NET 兼容模式下运行的服务中,仍然可以使用 DataContractSerializer

服务开发

若要使用 ASP.NET 开发服务,通常会将 WebService 属性添加到类,并将 WebMethodAttribute 属性添加到该类中的任何用于服务操作的方法。

[WebService]
public class Service : T:System.Web.Services.WebService
{
    [WebMethod]
    public string Echo(string input)
    {
       return input;
    }
}

ASP.NET 2.0 引入了一个选项,可以将属性 WebServiceWebMethodAttribute 添加到接口(而不是类),并通过编写类来实现该接口。

[WebService]
public interface IEcho
{
    [WebMethod]
    string Echo(string input);
}

public class Service : IEcho
{

   public string Echo(string input)
   {
        return input;
    }
}

使用此选项是首选项,因为具有 WebService 特性的接口构成了服务执行操作的协定,这些操作可以与可能以不同方式实现同一协定的各种类重复使用。

WCF 服务通过定义一个或多个 WCF 终结点来提供。 终结点由地址、绑定和服务协定定义。 该地址定义服务所在的位置。 绑定指定如何与服务通信。 服务合同定义了服务可以执行的操作。

通常首先通过添加 ServiceContractAttributeOperationContractAttribute 添加到接口来定义服务协定:

[ServiceContract]
public interface IEcho
{
     [OperationContract]
     string Echo(string input);
}

ServiceContractAttribute 指定接口定义了 WCF 服务协定,OperationContractAttribute 指示哪些接口方法定义了服务协定的操作(如果有)。

定义服务协定后,该协定在类中实现,方法是让类实现服务协定定义的接口:

public class Service : IEcho
{
    public string Echo(string input)
    {
       return input;
    }
}

实现服务协定的类称为 WCF 中的服务类型。

下一步是将地址和绑定与服务类型相关联。 这通常通过直接编辑文件或使用 WCF 提供的配置编辑器在配置文件中完成。 下面是配置文件的示例。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
     <system.serviceModel>
      <services>
      <service name="Service ">
       <endpoint
        address="EchoService"
        binding="basicHttpBinding"
        contract="IEchoService "/>
      </service>
      </services>
     </system.serviceModel>
</configuration>

绑定指定用于与应用程序通信的协议集。 下表列出了表示常见选项的系统提供的绑定。

名称 目的
BasicHttpBinding(基本 HTTP 绑定) 与支持 WS-BasicProfile 1.1 和基本安全配置文件 1.0 的 Web 服务和客户端的互作性。
WSHttpBinding(用于WCF框架中的安全HTTP通信绑定) 与通过 HTTP 支持 WS-* 协议的 Web 服务和客户端的互作性。
WSDualHttpBinding 双工 HTTP 通信,初始消息的接收方不直接回复初始发送方,但可以通过使用符合 WS-* 协议的 HTTP 在一段时间内传输任意数量的响应。
WSFederationBinding HTTP通信中,可以根据由明确标识的凭据提供者颁发的凭据来控制对服务资源的访问。
NetTcpBinding 跨网络在 WCF 软件实体之间安全、可靠、高性能的通信。
NetNamedPipeBinding 在同一台计算机上的 WCF 软件实体之间安全、可靠、高性能的通信。
NetMsmqBinding 使用 MSMQ 在 WCF 软件实体之间进行通信。
MsmqIntegrationBinding 使用 MSMQ 在 WCF 软件实体和另一个软件实体之间进行通信。
NetPeerTcpBinding 通过 Windows 点对点网络在 WCF 软件实体之间进行通信。

系统提供的绑定 BasicHttpBinding包含 ASP.NET Web 服务支持的协议集。

WCF 应用程序的自定义绑定很容易定义为 WCF 用于实现单个协议的绑定元素类的集合。 可以编写新的绑定元素来表示其他协议。

可以使用称为行为的类系列的属性来调整服务类型的内部行为。 在这里,该 ServiceBehaviorAttribute 类用于指定服务类型是多线程的。

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
public class DerivativesCalculatorServiceType: IDerivativesCalculator

某些行为(例如 ServiceBehaviorAttribute)是属性。 其他属性(管理员想要设置的属性)可以在应用程序的配置中修改。

在编程服务类型中,经常使用 OperationContext 类。 其静态 Current 属性提供对操作运行环境信息的访问。 OperationContextHttpContextContextUtil类类似。

托管

ASP.NET Web服务被编译成类库程序集。 提供了名为服务文件的文件,其扩展名为 .asmx,并包含一个 @ WebService 指令,该指令标识包含服务的代码及其所在程序集的代码的类。

<%@ WebService Language="C#" Class="Service,ServiceAssembly" %>

服务文件将复制到 Internet Information Services(IIS)中的 ASP.NET 应用程序根目录,并将程序集复制到该应用程序根目录的 \bin 子目录中。 然后,可以使用应用程序根目录中服务文件的统一资源定位符(URL)访问该应用程序。

WCF 服务随时可以托管在 IIS 5.1 或 6.0、作为 IIS 7.0 的一部分提供的 Windows 进程激活服务(WAS)以及任何 .NET 应用程序中。 若要在 IIS 5.1 或 6.0 中托管服务,服务必须使用 HTTP 作为通信传输协议。

若要在 IIS 5.1、6.0 或 WAS 中托管服务,请使用以下步骤:

  1. 将服务类型编译为类库程序集。

  2. 使用 @ ServiceHost 指令创建具有 .svc 扩展名的服务文件,以标识服务类型:

    <%@ServiceHost language="c#" Service="MyService" %>

  3. 将服务文件复制到虚拟目录中,并将程序集复制到该虚拟目录的 \bin 子目录中。

  4. 将配置文件复制到虚拟目录中,并将其命名为 Web.config。

然后,可以使用应用程序根中的服务文件的 URL 访问该应用程序。

若要在 .NET 应用程序中托管 WCF 服务,将服务类型编译为应用程序引用的类库程序集,并使用类对应用程序进行编程以承载服务 ServiceHost 。 下面是所需的基本编程示例:

string httpBaseAddress = "http://www.contoso.com:8000/";
string tcpBaseAddress = "net.tcp://www.contoso.com:8080/";

Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);

Uri[] baseAddresses = new Uri[] {
 httpBaseAddressUri,
 tcpBaseAddressUri};

using(ServiceHost host = new ServiceHost(
typeof(Service), //"Service" is the name of the service type baseAddresses))
{
     host.Open();

     […] //Wait to receive messages
     host.Close();
}

此示例演示如何在构造中指定一个或多个传输协议的 ServiceHost地址。 这些地址称为基址。

为任何 WCF 服务的终结点提供的地址是与该终结点主机的基础地址相关的地址。 主机可以为每个通信传输协议提供一个基址。 在上述配置文件中的示例配置中, BasicHttpBinding 为终结点选择的终结点使用 HTTP 作为传输协议,因此终结点 EchoService的地址相对于主机的 HTTP 基址。 在前面的示例中主机的情况下,HTTP 基址为 http://www.contoso.com:8000/。 对于 IIS 或 WAS 中托管的服务,基址是服务的服务文件的 URL。

只有承载在 IIS 或 WAS 中的服务(以独占方式将 HTTP 配置为传输协议),才能使用 WCF ASP.NET 兼容模式选项。 启用该选项需要执行以下步骤。

  1. 程序员必须将属性 AspNetCompatibilityRequirementsAttribute 添加到服务类型,并指定允许或需要 ASP.NET 兼容模式。

    [System.ServiceModel.Activation.AspNetCompatibilityRequirements(
          RequirementsMode=AspNetCompatibilityRequirementsMode.Require)]
    public class DerivativesCalculatorServiceType: IDerivativesCalculator
    
  2. 管理员必须将应用程序配置为使用 ASP.NET 兼容模式。

    <configuration>
         <system.serviceModel>
          <services>
          […]
          </services>
          <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
        </system.serviceModel>
    </configuration>
    

    WCF 应用程序还可以配置为使用 .asmx 作为其服务文件的扩展名,而不是 .svc。

    <system.web>
         <compilation>
          <compilation debug="true">
          <buildProviders>
           <remove extension=".asmx"/>
           <add extension=".asmx"
            type="System.ServiceModel.ServiceBuildProvider,
            System.ServiceModel,
            Version=3.0.0.0,
            Culture=neutral,
            PublicKeyToken=b77a5c561934e089" />
          </buildProviders>
          </compilation>
         </compilation>
    </system.web>
    

    此选项可以使您在将服务修改为使用 WCF 时,免于修改配置为使用 .asmx 服务文件 URL 的客户端。

客户端开发

ASP.NET Web 服务的客户端是使用命令行工具 WSDL.exe生成的,该工具提供 .asmx 文件的 URL 作为输入。 WCF 提供的相应工具是 ServiceModel 元数据实用工具工具(Svcutil.exe)。 它生成一个代码模块,其中包含服务协定的定义和 WCF 客户端类的定义。 它还生成一个配置文件,其中包含服务的地址和绑定。

在对远程服务的客户端进行编程时,通常建议根据异步模式进行编程。 默认情况下,WSDL.exe 工具生成的代码始终提供同步模式和异步模式。 ServiceModel 元数据实用工具工具(Svcutil.exe)生成的代码可以为任一模式提供。 它默认提供同步模式。 如果使用 /async 开关执行该工具,则生成的代码将支持异步模式。

不能保证 ASP.NET WSDL.exe 工具生成的 WCF 客户端类中的名称与由 Svcutil.exe 工具生成的 WCF 客户端类中的名称匹配。 具体而言,必须使用 XmlSerializer 序列化的类的属性名,在默认情况下,由 Svcutil.exe 工具生成的代码会在属性名后附加后缀 Property,但 WSDL.exe 工具则不这么做。

消息表示形式

可以自定义 ASP.NET Web 服务发送和接收的 SOAP 消息的标头。 类派生自 SoapHeader 定义标头的结构,然后使用 SoapHeaderAttribute 该类来指示标头的存在。

public class SomeProtocol : SoapHeader
{
     public long CurrentValue;
     public long Total;
}

[WebService]
public interface IEcho
{
     SomeProtocol ProtocolHeader
     {
      get;
     set;
     }

     [WebMethod]
     [SoapHeader("ProtocolHeader")]
     string PlaceOrders(PurchaseOrderType order);
}

public class Service: WebService, IEcho
{
     private SomeProtocol protocolHeader;

     public SomeProtocol ProtocolHeader
     {
         get
         {
              return this.protocolHeader;
         }

         set
         {
              this.protocolHeader = value;
         }
     }

     string PlaceOrders(PurchaseOrderType order)
     {
         long currentValue = this.protocolHeader.CurrentValue;
     }
}

WCF 提供属性,MessageContractAttributeMessageHeaderAttribute以及MessageBodyMemberAttribute描述服务发送和接收的 SOAP 消息的结构。

[DataContract]
public class SomeProtocol
{
     [DataMember]
     public long CurrentValue;
     [DataMember]
     public long Total;
}

[DataContract]
public class Item
{
     [DataMember]
     public string ItemNumber;
     [DataMember]
     public decimal Quantity;
     [DataMember]
     public decimal UnitPrice;
}

[MessageContract]
public class ItemMessage
{
     [MessageHeader]
     public SomeProtocol ProtocolHeader;
     [MessageBody]
     public Item Content;
}

[ServiceContract]
public interface IItemService
{
     [OperationContract]
     public void DeliverItem(ItemMessage itemMessage);
}

此语法生成消息结构的显式表示形式,而消息的结构由 ASP.NET Web 服务的代码隐含。 此外,在 ASP.NET 语法中,消息标头表示为服务的属性,如 ProtocolHeader 上一示例中的属性,而在 WCF 语法中,它们更准确地表示为消息的属性。 此外,WCF 允许将消息标头添加到终结点的配置中。

<service name="Service ">
     <endpoint
      address="EchoService"
      binding="basicHttpBinding"
      contract="IEchoService ">
      <headers>
      <dsig:X509Certificate
       xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
       ...
      </dsig:X509Certificate>
      </headers>
     </endpoint>
</service>

使用此选项,可以在客户端或服务的代码中避免任何对基础协议标头的引用:标头将根据终结点的配置方式添加到消息中。

服务说明

使用查询 WSDL 发出对 ASP.NET Web 服务的 .asmx 文件的 HTTP GET 请求会导致 ASP.NET 生成 WSDL 来描述该服务。 它将返回 WSDL 作为对请求的响应。

ASP.NET 2.0 可以验证服务是否符合 Web Services-Interoperability 组织的基本配置文件 1.1(WS-I),并插入服务符合其 WSDL 的声明。 这是通过 ConformsTo 属性的 EmitConformanceClaimsWebServiceBindingAttribute 参数来完成的。

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
     ConformsTo = WsiProfiles.BasicProfile1_1,
     EmitConformanceClaims=true)]
public interface IEcho

可以自定义 ASP.NET 为服务生成的 WSDL。 通过创建 ServiceDescriptionFormatExtension 的派生类来向 WSDL 添加项目,从而进行定制。

对 IIS 51、IIS 6.0 或 WAS 中承载 HTTP 终结点的 WCF 服务的 .svc 文件使用查询 WSDL 发出 HTTP GET 请求将导致 WCF 以描述服务的 WSDL 作为响应。 向 .NET 应用程序中托管的服务的 HTTP 基址发出具有查询 WSDL 的 HTTP GET 请求,如果 httpGetEnabled 设置为 true,则效果相同。

此外,WCF 还会利用其生成的 WSDL 来响应 WS-MetadataExchange 请求,以描述服务。 ASP.NET Web 服务没有对 WS-MetadataExchange 请求的内置支持。

WCF 生成的 WSDL 可以广泛自定义。 该 ServiceMetadataBehavior 类提供了一些用于自定义 WSDL 的设施。 WCF 还可以配置为不生成 WSDL,而是在给定 URL 中使用静态 WSDL 文件。

<behaviors>
     <behavior name="DescriptionBehavior">
     <metadataPublishing
      enableMetadataExchange="true"
      enableGetWsdl="true"
      enableHelpPage="true"
      metadataLocation=
      "http://localhost/DerivativesCalculatorService/Service.WSDL"/>
     </behavior>
</behaviors>

异常处理

在 ASP.NET Web 服务中,未经处理的异常作为 SOAP 错误返回到客户端。 还可以显式抛出 SoapException 类的实例,并更好地控制传输到客户端的 SOAP 错误的内容。

在 WCF 服务中,未经处理的异常不会作为 SOAP 错误返回给客户端,以防止通过异常无意中公开敏感信息。 提供了一个配置设置,用于将未经处理的异常返回给客户端,以便进行调试。

若要将 SOAP 错误返回客户端,您可以使用数据协定类型作为泛型类型抛出泛型类型的实例 FaultException<TDetail>。 还可以向操作添加 FaultContractAttribute 属性,以指定操作可能引发的错误。

[DataContract]
public class MathFault
{
     [DataMember]
     public string operation;
     [DataMember]
     public string problemType;
}

[ServiceContract]
public interface ICalculator
{
     [OperationContract]
     [FaultContract(typeof(MathFault))]
     int Divide(int n1, int n2);
}

这样做会导致可能的故障被公布在服务的 WSDL 中,使客户端程序员能够预测哪些故障可能由操作导致,从而编写相应的 catch 语句。

try
{
     result = client.Divide(value1, value2);
}
catch (FaultException<MathFault> e)
{
 Console.WriteLine("FaultException<MathFault>: Math fault while doing "
  + e.Detail.operation
  + ". Problem: "
  + e.Detail.problemType);
}

状态管理

用于实现 ASP.NET Web 服务的类可能派生自 WebService

public class Service : WebService, IEcho
{

 public string Echo(string input)
 {
  return input;
 }
}

在这种情况下,可以将类编程为使用 WebService 基类的 Context 属性访问 HttpContext 对象。 该 HttpContext 对象可用于使用应用程序属性更新和检索应用程序状态信息,并可用于使用其 Session 属性更新和检索会话状态信息。

ASP.NET 提供了对通过会话属性 HttpContext 访问的会话状态信息实际存储位置的相当大的控制。 它可以存储在 Cookie、数据库、当前服务器的内存中或指定服务器的内存中。 选择是在服务的配置文件中做出的。

WCF 为状态管理提供可扩展对象。 可扩展对象是实现 IExtensibleObject<T>的对象。 最重要的可扩展对象是 ServiceHostBaseInstanceContextServiceHostBase 允许保持同一主机上所有服务类型的所有实例可以访问的状态,同时 InstanceContext 允许维护可由同一服务类型的同一实例中运行的任何代码访问的状态。

在这里,服务类型 TradingSystem 具有一个 ServiceBehaviorAttribute,它指定来自同一 WCF 客户端实例的所有调用都路由到服务类型的同一实例。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TradingSystem: ITradingService

DealData定义可由在同一服务类型的实例中运行的任何代码访问的状态。

internal class DealData: IExtension<InstanceContext>
{
 public string DealIdentifier = null;
 public Trade[] Trades = null;
}

在实现服务协定作之一的服务类型的代码中, DealData 状态对象将添加到服务类型的当前实例的状态。

string ITradingService.BeginDeal()
{
 string dealIdentifier = Guid.NewGuid().ToString();
 DealData state = new DealData(dealIdentifier);
 OperationContext.Current.InstanceContext.Extensions.Add(state);
 return dealIdentifier;
}

然后,该状态对象可由实现服务合同的另一个操作的代码进行检索和修改。

void ITradingService.AddTrade(Trade trade)
{
 DealData dealData =  OperationContext.Current.InstanceContext.Extensions.Find<DealData>();
 dealData.AddTrade(trade);
}

虽然 ASP.NET 提供对类中 HttpContext 状态信息实际存储的位置的控制,但 WCF 至少在其初始版本中无法控制存储可扩展对象的位置。 这构成了为 WCF 服务选择 ASP.NET 兼容模式的最佳原因。 如果需要可配置状态管理,可以选择 ASP.NET 兼容模式,这样您就能像在 ASP.NET 中一样使用 HttpContext 类的功能,并且可以配置使用 HttpContext 类管理的状态信息的存储位置。

安全

用于保护 ASP.NET Web 服务的选项是用于保护任何 IIS 应用程序的选项。 由于 WCF 应用程序不仅可以托管在 IIS 中,还可以托管在任何 .NET 可执行文件中,因此保护 WCF 应用程序的选项必须独立于 IIS 的设施。 但是,为 ASP.NET Web 服务提供的设施也可用于在 ASP.NET 兼容模式下运行的 WCF 服务。

安全性:身份验证

IIS 提供设施来控制应用程序的访问,您可以选择匿名访问或各种身份验证模式:Windows 身份验证、摘要式身份验证、基本身份验证和 .NET Passport 身份验证。 Windows 身份验证选项可用于控制对 ASP.NET Web 服务的访问。 但是,当 WCF 应用程序托管在 IIS 中时,IIS 必须配置为允许匿名访问应用程序,以便 WCF 本身可以管理身份验证,WCF 本身支持 Windows 身份验证等各种选项。 其他内置选项包括用户名令牌、X.509 证书、SAML 令牌和 CardSpace 卡,但也可以定义自定义身份验证机制。

安全性:身份冒充

ASP.NET 提供一个标识元素,通过该元素可以生成 ASP.NET Web 服务来模拟特定用户或随当前请求提供的用户凭据。 该元素可用于在 ASP.NET 兼容模式下运行的 WCF 应用程序中配置模拟。

WCF 配置系统提供自己的标识元素,用于指定要模拟的特定用户。 此外,WCF 客户端和服务可以独立配置为身份模拟。 客户端可配置为在传输请求时模拟当前用户。

<behaviors>
     <behavior name="DerivativesCalculatorClientBehavior">
      <clientCredentials>
      <windows allowedImpersonationLevel="Impersonation"/>
      </clientCredentials>
     </behavior>
</behaviors>

可将服务操作配置为模拟当前请求提供了任意用户的凭据。

[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void Receive(Message input)

安全性:使用访问控制列表进行授权

访问控制列表(ACL)可用于限制对 .asmx 文件的访问。 但是,WCF .svc 文件上的 ACL 将被忽略,但 ASP.NET 兼容模式除外。

安全性:基于角色的授权

IIS Windows 身份验证选项可与 ASP.NET 配置语言提供的授权元素结合使用,以便根据用户分配到的 Windows 组,为 ASP.NET Web 服务提供基于角色的授权。 ASP.NET 2.0 引入了更常规的基于角色的授权机制:角色提供程序。

角色提供程序是一些类,它们都实现了一个基本接口,用于查询用户分配到的角色,但每个角色提供程序知道如何从不同的源检索该信息。 ASP.NET 2.0 提供了一个角色提供程序,该角色提供程序可以从 Microsoft SQL Server 数据库检索角色分配,另一个角色提供程序可以从 Windows Server 2003 授权管理器检索角色分配。

角色提供程序机制实际上可以在任何 .NET 应用程序中独立于 ASP.NET 使用,包括 WCF 应用程序。 以下 WCF 应用程序的示例配置演示了如何通过 ServiceAuthorizationBehavior 选项选择使用 ASP.NET 角色提供程序。

<system.serviceModel>
     <services>
         <service name="Service.ResourceAccessServiceType"
             behaviorConfiguration="ServiceBehavior">
             <endpoint
              address="ResourceAccessService"
              binding="wsHttpBinding"
              contract="Service.IResourceAccessContract"/>
         </service>
     </services>
     <behaviors>
       <behavior name="ServiceBehavior">
       <serviceAuthorization principalPermissionMode="UseAspNetRoles"/>
      </behavior>
     </behaviors>
</system.serviceModel>

安全性:基于声明的授权

WCF 的最重要的创新之一就是它全面支持基于声明授权访问受保护的资源。 例如,声明由类型、权利和值组成,比如驾驶执照。 它提出了一套关于持有者的说法,其中一个是持有者的出生日期。 该声明的类型是出生日期,而声明的值是司机的出生日期。 声明赋予持有者的权利明确规定持有者可以如何使用声明的价值。 对于驾驶者出生日期的索赔,权利是所有权:司机拥有该出生日期,但不能改变它。 基于声明的授权包含基于角色的授权,因为角色属于声明的一种类型。

基于声明的授权通过将一组声明与操作的访问要求进行比较来实现,并根据该比较的结果授予或拒绝操作访问权限。 在 WCF 中,您可以指定一个类来运行基于声明的授权,同样也可通过为 ServiceAuthorizationManagerServiceAuthorizationBehavior 属性分配一个值实现。

<behaviors>
     <behavior name='ServiceBehavior'>
     <serviceAuthorization
     serviceAuthorizationManagerType=
                   'Service.AccessChecker, Service' />
     </behavior>
</behaviors>

用于运行基于声明的授权的类必须派生自只有一种重写方法(即 ServiceAuthorizationManager)的 AccessCheck()。 每当服务的某个操作被调用时,WCF 就会调用该方法,并提供一个 OperationContext 对象,该对象含有用户声明,存储在其 ServiceSecurityContext.AuthorizationContext 属性中。 WCF 将组合任何来自用户为身份验证提供的安全令牌的用户声明,这将引发这些声明是否满足操作需要的评估任务。

WCF 自动从任何类型的安全令牌组装声明是一项非常重要的创新,因为这使得基于声明进行授权的代码完全独立于身份验证机制。 相比之下,在 ASP.NET 中使用 ACL 或角色的授权与 Windows 身份验证密切相关。

安全性:机密性

通过在 IIS 中将应用程序配置为使用安全超文本传输协议(HTTPS),可以在传输级别确保与 ASP.NET Web 服务交换的消息的机密性。 IIS 中托管的 WCF 应用程序可以执行相同的操作。 但是,也可以将托管在 IIS 外部的 WCF 应用程序配置为使用安全传输协议。 更重要的是,WCF 应用程序还可以配置为在使用 WS-Security 协议传输消息之前保护消息。 使用 WS-Security 仅保护消息的正文,可以在到达最终目标之前,在中介之间保密传输消息。

全球化

通过 ASP.NET 配置语言,您可以为这些服务单独指定区域性。 WCF 不支持该配置设置,但 ASP.NET 兼容模式除外。 若要本地化不使用 ASP.NET 兼容模式的 WCF 服务,请将服务类型编译为区域性特定的程序集,并为每个区域性特定的程序集使用不同的区域性特定的终结点。

另请参阅