WCF技术的不同应用场景及其实现分析(续)

上一篇 我总结介绍了WCF技术的背景,核心要素,和典型场景,目的是希望让大家更好地掌握WCF技术,并且在工作中知道如何选择。

由于篇幅较长,这一篇继续写完第二部分。

应用场景:

WCF 从发布到现在的将近5年左右的时间,经过了多次增强和改进,从最开始单纯的SOAP Service的方式,发展出来其他多种应用场景,分别是

1. SOAP Services

2. WebHttp Services

3. Data Services

4. Workflow Services

5. RIA Services

本文将讨论后面3种应用场景。

 

3. Data Services

Data Service,顾名思义,是指数据服务。最开始的名称叫做ADO.NET Data Service,是从.NET Framework 3.5 SP1开始提供的功能。在.NET Framework 4.0中,已经改名为WCF Data Service.

image

 

这个服务类型是WebHttp Service的一种特殊实现,也就是说,它延续了WebHttp Service的Restful的特点。但与标准的WebHttp Service,不同的是,它具有一套完整的API,包括客户端的访问API。这样也就允许,它既支持类似于Javascript这样的脚本访问,也支持在传统客户端中进行访问。

需要注意的是,WCF Data Service既然是数据服务,它天生就是为数据访问有关。这是它最强的地方,它的出现大大简化了我们编写数据访问服务的工作。好不夸张地说,确实很酷。你可以想象一下,你有一个数据库,有N张表,你想实现对这些表的增删改查操作,如果你一个一个去编写,显然是一件非常辛苦而且没有效率的事情。

 

WCF Data Service支持两种数据模型,一种是LINQ to SQL, 一种是ADO.NET Entity Frmawork。下面的例子使用了LINQ to SQL. 使用的数据库是微软提供的范例数据库Northwind.

image

有了上面这个模型之后,我们就可以添加一个WCF Data Service了。这是一个继承子DataService的类型,并且通过svc这种Self Host的方式提供宿主。

using System.Data.Services;
using System.Data.Services.Common;

namespace WebApplication_WCFDataService_
{
    public class NorthwindDataService : DataService<NorthwindDataClassesDataContext>
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(DataServiceConfiguration config)
        {
            // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
            // Examples:
            config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
            // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        }
    }
}

 

这样就完成了所有的操作,在浏览器中可以通过REST的风格测试该服务。例如,下面的语法是查看所有Customers的

image

下面是查看某个Customer信息的

image

我们可以看到,默认情况下,WCF服务是用XML返回数据的。关于如何让它返回JSON数据,我之前有一篇文章专门探讨。请参考:http://www.cnblogs.com/chenxizhang/archive/2011/06/12/2078830.html

 

值得注意的是,与WebHttp Service不同的是,Data Service可以在标准客户端中进行访问。而且它的方式是有些特殊的,具体来说,客户端也是通过一个DataServiceContext的类型来访问的. 例如下面这样(这个文件是我们添加服务引用时自动生成的)

image

我们可以通过如下这样的代码进行调用

using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var ctx = new DataModel.NorthwindDataClassesDataContext(
                new Uri("http://localhost.:9475/NorthwindDataService.svc/"));

            var query = from c in ctx.Customers
                        select c;

            foreach (var item in query)
            {
                Console.WriteLine(item.CompanyName);
            }
        }
    }
}

【注意】这里调用的方式,返回的数据是XML格式的,而不是SOAP Envelope的那种方式。

image

 

关于Data Service与WebHttp Service的相同之处,还体现在它也可以添加自定义的Operation,例如下面这篇文章介绍的那样

http://www.cnblogs.com/chenxizhang/archive/2010/02/28/1675270.html

 

常见的问题还有:Data Service如何做身份验证?我之前写过一篇文章介绍这个问题:http://www.cnblogs.com/chenxizhang/archive/2010/02/28/1675307.html

 

总结:Data Service是一种结合数据模型快速实现数据访问的方式。它依赖LINQ to SQL或者ADO.NET Entity Framework,并且提供了RESTFul的实现(它所使用的绑定就是WebHttpBinding)。它有一套专用的安全模型,与标准的WCF不一样。

 

4. Workflow Services

这是一个很有意思的服务。这是在.NET Framework 4.0中开始出现的,也就是随着Workflow Foundation升级到4.0之后,提供了一种全新的服务类型,简单地来说,它是可以直接与Workflow Foundation(工作流)想结合的一种服务。

image

创建好的项目中,包含了一个特殊的文件,叫xamlx。

image

虽然该文件很怪异,但它在浏览器中打开来看的效果却有标准的WCF服务没有什么区别,如下

image

不少朋友都不理解为什么需要这样一个服务,我相信正在看这篇文章的你也一定心存疑惑吧?

这是一个WCF服务,但为什么要与Workflow(工作流)扯上关系呢?

答案就是:如果你这个WCF服务,要提供的功能,希望用工作流的方式来设计和实现的话,你就可以用这个服务

换句话说,这里的重点,仍然是WCF服务,而不是Workflow。Workflow是实现的一种方式。具体来说,在这种方式中,你可以不写任何代码就能实现一些流程功能。

Workflow的讨论显然超出了本文的范畴,这里就不多展开了。

以上面这个服务为例,它是公开了一个方法叫“GetData”,该方法接受一个参数,叫“data”,是整数型的。默认情况下,这个服务会将用户传入的参数data作为响应直接发给调用方。

 

那么,如何理解工作流设计呢?假设我们希望为该服务添加一个简单逻辑,用户如果传入的data是偶数,则返回“偶数”,否则返回“奇数”这样的文本字符串。我们可以将这个服务稍做修改。如下图所示

image

大家请注意,实现这个功能,我们并不需要编写代码。

实际上,上面这个服务就是一段XAML的定义。这种做法自有它的好处,但这里不深入讨论了。

<WorkflowService mc:Ignorable="sap" ConfigurationName="Service1" sap:VirtualizedContainerService.HintSize="561,620" Name="Service1" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:p="http://tempuri.org/" xmlns:p1="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <p1:Sequence DisplayName="Sequential Service" sad:XamlDebuggerXmlReader.FileName="d:/temp/WCFTechonologyComparaionSolution/DeclarativeServiceLibrary1/Service1.xamlx" sap:VirtualizedContainerService.HintSize="531,590" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces">
    <p1:Sequence.Variables>
      <p1:Variable x:TypeArguments="CorrelationHandle" Name="handle" />
      <p1:Variable x:TypeArguments="x:Int32" Name="data" />
      <p1:Variable x:TypeArguments="x:String" Name="result" />
    
   
   p1:Sequence.Variables>
    <sap:WorkflowViewStateService.ViewState>
      <scg3:Dictionary x:TypeArguments="x:String, x:Object">
        <x:Boolean x:Key="IsExpanded">True
    
    x:Boolean>
      
     
     scg3:Dictionary>
    
      
      sap:WorkflowViewStateService.ViewState>
    <Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="ReceiveRequest" sap:VirtualizedContainerService.HintSize="509,90" OperationName="GetData" ServiceContractName="p:IService">
      <Receive.CorrelationInitializers>
        <RequestReplyCorrelationInitializer CorrelationHandle="[handle]" />
       
       Receive.CorrelationInitializers> <ReceiveMessageContent> <p1:OutArgument x:TypeArguments="x:Int32">[data] 
        p1:OutArgument>  
         ReceiveMessageContent>  
          Receive> <p1:If Condition="[data Mod 2 = 0]" sap:VirtualizedContainerService.HintSize="509,206"> <p1:If.Then> <p1:Assign sap:VirtualizedContainerService.HintSize="242,100"> <p1:Assign.To> <p1:OutArgument x:TypeArguments="x:String">[result] 
           p1:OutArgument>  
            p1:Assign.To> <p1:Assign.Value> <p1:InArgument x:TypeArguments="x:String">偶数 
             p1:InArgument>  
              p1:Assign.Value>  
               p1:Assign>  
                p1:If.Then> <p1:If.Else> <p1:Assign sap:VirtualizedContainerService.HintSize="242,100"> <p1:Assign.To> <p1:OutArgument x:TypeArguments="x:String">[result] 
                 p1:OutArgument>  
                  p1:Assign.To> <p1:Assign.Value> <p1:InArgument x:TypeArguments="x:String">奇数 
                   p1:InArgument>  
                    p1:Assign.Value>  
                     p1:Assign>  
                      p1:If.Else>  
                       p1:If> <SendReply Request="{x:Reference __ReferenceID0}" DisplayName="SendResponse" sap:VirtualizedContainerService.HintSize="509,90"> <SendMessageContent> <p1:InArgument x:TypeArguments="x:String">[result] 
                        p1:InArgument>  
                         SendMessageContent>  
                          SendReply>  
                           p1:Sequence>  
                            WorkflowService>

那么,这个服务怎么使用呢?没有什么特别的,就和正常的WCF服务一样就可以了:添加服务引用,然后调用服务。

image

需要注意的是,这个服务是使用basicHttpBinding的,而且也无法修改


   
   xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    
    
    security>
                
     
     binding>
            
      
      basicHttpBinding>
         
       bindings> <client> <endpoint address="http://localhost:14262/Service1.xamlx" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService" contract="WorkflowService.IService" name="BasicHttpBinding_IService" />  
        client>  
         system.serviceModel>  
          configuration>

我们看看程序调用的效果吧

using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            var proxy = new WorkflowService.ServiceClient();
            Console.WriteLine(proxy.GetData(20));//这个会返回“偶数”
            Console.WriteLine(proxy.GetData(215));//这个会返回“奇数”

            Console.Read();
        }
    }
}

image

 

总结:Workflow Services是一种将Workflow Foundation与WCF结合的实现,它采用了basicHttpBinding, 允许我们通过声明(Declarative)而不是代码(Code)设计一个服务。

【备注】需要注意的是,在Workflow 的设计中,也可以发起WCF 调用,但这是另外一个问题了。

image

大家有点晕了对吧,我就不再往下展开了吧。

 

 

 

5. RIA Services

这是本文最后一个小节,将介绍RIA Services。

RIA的意思是,Rich Internet Application。在微软平台上,Silverlight就是RIA战略中的核心产品,所以很显然,RIA Service主要就是为Silverlight服务的。这个是.NET Framework 4.0中才有的功能,并且还需要安装RIA Service Toolkit。

推荐你使用微软免费提供的Web Platform Installer来安装必要的一些组件

image

如果你对RIA Service一些基本概念不熟悉,请参考微软官方介绍 http://www.silverlight.net/getstarted/riaservices/

我们一般可以在新建Silverlight应用程序的时候,选择是否启用WCF RIA Services,如下图所示

image

启用该服务的意思是,让Silverlight程序链接到Web程序

image

为什么需要这样一个链接呢?这是因为RIA Service会有一个特殊的机制,在Silverlight应用程序编译的时候,会自动去找到所链接的Web项目,将里面的RIA Service在本地生成一个代理类。

 

RIA Services,具体到Web项目中来,其实是所谓的Domain Service。

image

与WCF Data Service相似的是,Domain Service也需要有一个数据模型。但不同的是,它支持ADO.NET Entity Framework之外,还支持自定义业务模型。

【注意】Domain Service目前不支持LINQ to SQL模型了。

image

创建Domain Service的时候可以选择到这个数据模型

image

 

该工具会自动生成如下代码


namespace SilverlightApplication1.Web
{
    using System.Data;
    using System.Linq;
    using System.ServiceModel.DomainServices.EntityFramework;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;


    // Implements application logic using the NorthwindEntities context.
    // TODO: Add your application logic to these methods or in additional methods.
    // TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access
    // Also consider adding roles to restrict access as appropriate.
    // [RequiresAuthentication]
    [EnableClientAccess()]
    public class NorthwindDomainService : LinqToEntitiesDomainService
  
  
   
   
    {

        
   
   // TODO:
        
   
   // Consider constraining the results of your query method.  If you need additional input you can
        
   
   // add parameters to this method or create additional query methods with different names.
        
   
   // To support paging you will need to add ordering to the 'Customers' query.
        [Query(IsDefault = 
   
   true)]
        
   
   public IQueryable
   
   
    
     GetCustomers()
        {
            
    
    return 
    
    this.ObjectContext.Customers;
        }

        
    
    public 
    
    void InsertCustomer(Customer customer)
        {
            
    
    if ((customer.EntityState != EntityState.Detached))
            {
                
    
    this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Added);
            }
            
    
    else
            {
                
    
    this.ObjectContext.Customers.AddObject(customer);
            }
        }

        
    
    public 
    
    void UpdateCustomer(Customer currentCustomer)
        {
            
    
    this.ObjectContext.Customers.AttachAsModified(currentCustomer, 
    
    this.ChangeSet.GetOriginal(currentCustomer));
        }

        
    
    public 
    
    void DeleteCustomer(Customer customer)
        {
            
    
    if ((customer.EntityState != EntityState.Detached))
            {
                
    
    this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Deleted);
            }
            
    
    else
            {
                
    
    this.ObjectContext.Customers.Attach(customer);
                
    
    this.ObjectContext.Customers.DeleteObject(customer);
            }
        }
    }
}


   
   
  
  

与此同时,由于我的Silverlight 应用程序是链接到了这个Web项目的,所以如果此时编译Silverlight应用程序的话,会自动根据这个DomainService生成一个类型

image

这个类型的大致代码如下

//------------------------------------------------------------------------------
// 
   
   
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.235
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// 
//------------------------------------------------------------------------------

namespace SilverlightApplication1
{
    using System;
    using System.ServiceModel.DomainServices.Client.ApplicationServices;
    
    
    /// 
    /// Context for the RIA application.
    /// 
    /// 
   
   
    /// This context extends the base to make application services and types available
    /// for consumption from code and xaml.
    /// 
    public sealed partial class WebContext : WebContextBase
    {
        
        #region Extensibility Method Definitions

        /// 
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// 
        partial void OnCreated();

        #endregion
        
        
        /// 
        /// Initializes a new instance of the WebContext class.
        /// 
        public WebContext()
        {
            this.OnCreated();
        }
        
        /// 
        /// Gets the context that is registered as a lifetime object with the current application.
        /// 
        /// 
   
   
    
     is thrown if there is no current application,
   
   
        /// no contexts have been added, or more than one context has been added.
        /// 
        /// 
   
   
        public new static WebContext Current
        {
            get
            {
                return ((WebContext)(WebContextBase.Current));
            }
        }
    }
}
namespace SilverlightApplication1.Web
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.DomainServices.Client;
    using System.ServiceModel.Web;
    
    
    /// 
    /// The 'Customer' entity class.
    /// 
    [DataContract(Namespace="http://schemas.datacontract.org/2004/07/SilverlightApplication1.Web")]
    public sealed partial class Customer : Entity
    {
        
        private string _address;
        
        private string _city;
        
        private string _companyName;
        
        private string _contactName;
        
        private string _contactTitle;
        
        private string _country;
        
        private Nullable
  
  
   
    _creationDate;
        
        
   
   private 
   
   string _customerID;
        
        
   
   private 
   
   string _fax;
        
        
   
   private Nullable
   
   
    
     _lastEditDate;
        
        
    
    private 
    
    string _phone;
        
        
    
    private 
    
    string _postalCode;
        
        
    
    private 
    
    string _region;
        
        
    
    #region Extensibility Method Definitions

        
    
    /// 
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// 
        
    
    partial 
    
    void OnCreated();
        
    
    partial 
    
    void OnAddressChanging(
    
    string 
    
    value);
        
    
    partial 
    
    void OnAddressChanged();
        
    
    partial 
    
    void OnCityChanging(
    
    string 
    
    value);
        
    
    partial 
    
    void OnCityChanged();
        
    
    partial 
    
    void OnCompanyNameChanging(
    
    string 
    
    value);
        
    
    partial 
    
    void OnCompanyNameChanged();
        
    
    partial 
    
    void OnContactNameChanging(
    
    string 
    
    value);
        
    
    partial 
    
    void OnContactNameChanged();
        
    
    partial 
    
    void OnContactTitleChanging(
    
    string 
    
    value);
        
    
    partial 
    
    void OnContactTitleChanged();
        
    
    partial 
    
    void OnCountryChanging(
    
    string 
    
    value);
        
    
    partial 
    
    void OnCountryChanged();
        
    
    partial 
    
    void OnCreationDateChanging(Nullable
    
     
     
     value);
        
     
     partial 
     
     void OnCreationDateChanged();
        
     
     partial 
     
     void OnCustomerIDChanging(
     
     string 
     
     value);
        
     
     partial 
     
     void OnCustomerIDChanged();
        
     
     partial 
     
     void OnFaxChanging(
     
     string 
     
     value);
        
     
     partial 
     
     void OnFaxChanged();
        
     
     partial 
     
     void OnLastEditDateChanging(Nullable
     
      
      
      value);
        
      
      partial 
      
      void OnLastEditDateChanged();
        
      
      partial 
      
      void OnPhoneChanging(
      
      string 
      
      value);
        
      
      partial 
      
      void OnPhoneChanged();
        
      
      partial 
      
      void OnPostalCodeChanging(
      
      string 
      
      value);
        
      
      partial 
      
      void OnPostalCodeChanged();
        
      
      partial 
      
      void OnRegionChanging(
      
      string 
      
      value);
        
      
      partial 
      
      void OnRegionChanged();

        
      
      #endregion
        
        
        
      
      ///  /// Initializes a new instance of the 
          class. /// 
        
      
      public Customer()
        {
            
      
      this.OnCreated();
        }
        
        
      
      ///  /// Gets or sets the 'Address' value. /// 
        [DataMember()]
        [StringLength(60)]
        
      
      public 
      
      string Address
        {
            get
            {
                
      
      return 
      
      this._address;
            }
            set
            {
                
      
      if ((
      
      this._address != 
      
      value))
                {
                    
      
      this.OnAddressChanging(
      
      value);
                    
      
      this.RaiseDataMemberChanging(
      
      "Address");
                    
      
      this.ValidateProperty(
      
      "Address", 
      
      value);
                    
      
      this._address = 
      
      value;
                    
      
      this.RaiseDataMemberChanged(
      
      "Address");
                    
      
      this.OnAddressChanged();
                }
            }
        }
        
        
      
      ///  /// Gets or sets the 'City' value. /// 
        [DataMember()]
        [StringLength(15)]
        
      
      public 
      
      string City
        {
            get
            {
                
      
      return 
      
      this._city;
            }
            set
            {
                
      
      if ((
      
      this._city != 
      
      value))
                {
                    
      
      this.OnCityChanging(
      
      value);
                    
      
      this.RaiseDataMemberChanging(
      
      "City");
                    
      
      this.ValidateProperty(
      
      "City", 
      
      value);
                    
      
      this._city = 
      
      value;
                    
      
      this.RaiseDataMemberChanged(
      
      "City");
                    
      
      this.OnCityChanged();
                }
            }
        }
        
        
      
      ///  /// Gets or sets the 'CompanyName' value. /// 
        [DataMember()]
        [Required()]
        [StringLength(40)]
        
      
      public 
      
      string CompanyName
        {
            get
            {
                
      
      return 
      
      this._companyName;
            }
            set
            {
                
      
      if ((
      
      this._companyName != 
      
      value))
                {
                    
      
      this.OnCompanyNameChanging(
      
      value);
                    
      
      this.RaiseDataMemberChanging(
      
      "CompanyName");
                    
      
      this.ValidateProperty(
      
      "CompanyName", 
      
      value);
                    
      
      this._companyName = 
      
      value;
                    
      
      this.RaiseDataMemberChanged(
      
      "CompanyName");
                    
      
      this.OnCompanyNameChanged();
                }
            }
        }
        
        
      
      ///  /// Gets or sets the 'ContactName' value. /// 
        [DataMember()]
        [StringLength(30)]
        
      
      public 
      
      string ContactName
        {
            get
            {
                
      
      return 
      
      this._contactName;
            }
            set
            {
                
      
      if ((
      
      this._contactName != 
      
      value))
                {
                    
      
      this.OnContactNameChanging(
      
      value);
                    
      
      this.RaiseDataMemberChanging(
      
      "ContactName");
                    
      
      this.ValidateProperty(
      
      "ContactName", 
      
      value);
                    
      
      this._contactName = 
      
      value;
                    
      
      this.RaiseDataMemberChanged(
      
      "ContactName");
                    
      
      this.OnContactNameChanged();
                }
            }
        }
        
        
      
      ///  /// Gets or sets the 'ContactTitle' value. /// 
        [DataMember()]
        [StringLength(30)]
        
      
      public 
      
      string ContactTitle
        {
            get
            {
                
      
      return 
      
      this._contactTitle;
            }
            set
            {
                
      
      if ((
      
      this._contactTitle != 
      
      value))
                {
                    
      
      this.OnContactTitleChanging(
      
      value);
                    
      
      this.RaiseDataMemberChanging(
      
      "ContactTitle");
                    
      
      this.ValidateProperty(
      
      "ContactTitle", 
      
      value);
                    
      
      this._contactTitle = 
      
      value;
                    
      
      this.RaiseDataMemberChanged(
      
      "ContactTitle");
                    
      
      this.OnContactTitleChanged();
                }
            }
        }
        
        
      
      ///  /// Gets or sets the 'Country' value. /// 
        [DataMember()]
        [StringLength(15)]
        
      
      public 
      
      string Country
        {
            get
            {
                
      
      return 
      
      this._country;
            }
            set
            {
                
      
      if ((
      
      this._country != 
      
      value))
                {
                    
      
      this.OnCountryChanging(
      
      value);
                    
      
      this.RaiseDataMemberChanging(
      
      "Country");
                    
      
      this.ValidateProperty(
      
      "Country", 
      
      value);
                    
      
      this._country = 
      
      value;
                    
      
      this.RaiseDataMemberChanged(
      
      "Country");
                    
      
      this.OnCountryChanged();
                }
            }
        }
        
        
      
      ///  /// Gets or sets the 'CreationDate' value. /// 
        [DataMember()]
        
      
      public Nullable
      
      
        CreationDate { get { 
       return 
       this._creationDate; } set { 
       if (( 
       this._creationDate != 
       value)) { 
       this.OnCreationDateChanging( 
       value); 
       this.RaiseDataMemberChanging( 
       "CreationDate"); 
       this.ValidateProperty( 
       "CreationDate", 
       value); 
       this._creationDate = 
       value; 
       this.RaiseDataMemberChanged( 
       "CreationDate"); 
       this.OnCreationDateChanged(); } } } 
       ///  /// Gets or sets the 'CustomerID' value. ///  [DataMember()] [Editable( 
       false, AllowInitialValue= 
       true)] [Key()] [Required()] [RoundtripOriginal()] [StringLength(5)] 
       public 
       string CustomerID { get { 
       return 
       this._customerID; } set { 
       if (( 
       this._customerID != 
       value)) { 
       this.OnCustomerIDChanging( 
       value); 
       this.ValidateProperty( 
       "CustomerID", 
       value); 
       this._customerID = 
       value; 
       this.RaisePropertyChanged( 
       "CustomerID"); 
       this.OnCustomerIDChanged(); } } } 
       ///  /// Gets or sets the 'Fax' value. ///  [DataMember()] [StringLength(24)] 
       public 
       string Fax { get { 
       return 
       this._fax; } set { 
       if (( 
       this._fax != 
       value)) { 
       this.OnFaxChanging( 
       value); 
       this.RaiseDataMemberChanging( 
       "Fax"); 
       this.ValidateProperty( 
       "Fax", 
       value); 
       this._fax = 
       value; 
       this.RaiseDataMemberChanged( 
       "Fax"); 
       this.OnFaxChanged(); } } } 
       ///  /// Gets or sets the 'LastEditDate' value. ///  [DataMember()] 
       public Nullable 
       
         LastEditDate { get { 
        return 
        this._lastEditDate; } set { 
        if (( 
        this._lastEditDate != 
        value)) { 
        this.OnLastEditDateChanging( 
        value); 
        this.RaiseDataMemberChanging( 
        "LastEditDate"); 
        this.ValidateProperty( 
        "LastEditDate", 
        value); 
        this._lastEditDate = 
        value; 
        this.RaiseDataMemberChanged( 
        "LastEditDate"); 
        this.OnLastEditDateChanged(); } } } 
        ///  /// Gets or sets the 'Phone' value. ///  [DataMember()] [StringLength(24)] 
        public 
        string Phone { get { 
        return 
        this._phone; } set { 
        if (( 
        this._phone != 
        value)) { 
        this.OnPhoneChanging( 
        value); 
        this.RaiseDataMemberChanging( 
        "Phone"); 
        this.ValidateProperty( 
        "Phone", 
        value); 
        this._phone = 
        value; 
        this.RaiseDataMemberChanged( 
        "Phone"); 
        this.OnPhoneChanged(); } } } 
        ///  /// Gets or sets the 'PostalCode' value. ///  [DataMember()] [StringLength(10)] 
        public 
        string PostalCode { get { 
        return 
        this._postalCode; } set { 
        if (( 
        this._postalCode != 
        value)) { 
        this.OnPostalCodeChanging( 
        value); 
        this.RaiseDataMemberChanging( 
        "PostalCode"); 
        this.ValidateProperty( 
        "PostalCode", 
        value); 
        this._postalCode = 
        value; 
        this.RaiseDataMemberChanged( 
        "PostalCode"); 
        this.OnPostalCodeChanged(); } } } 
        ///  /// Gets or sets the 'Region' value. ///  [DataMember()] [StringLength(15)] 
        public 
        string Region { get { 
        return 
        this._region; } set { 
        if (( 
        this._region != 
        value)) { 
        this.OnRegionChanging( 
        value); 
        this.RaiseDataMemberChanging( 
        "Region"); 
        this.ValidateProperty( 
        "Region", 
        value); 
        this._region = 
        value; 
        this.RaiseDataMemberChanged( 
        "Region"); 
        this.OnRegionChanged(); } } } 
        ///  /// Computes a value from the key fields that uniquely identifies this entity instance. ///  
        /// 
         
           An object instance that uniquely identifies this entity instance. 
          
        public 
        override 
        object GetIdentity() { 
        return 
        this._customerID; } } 
        ///  /// The DomainContext corresponding to the 'NorthwindDomainService' DomainService. ///  
        public 
        sealed 
        partial 
        class NorthwindDomainContext : DomainContext { 
        #region Extensibility Method Definitions 
        ///  /// This method is invoked from the constructor once initialization is complete and /// can be used for further object setup. ///  
        partial 
        void OnCreated(); 
        #endregion 
        ///  /// Initializes a new instance of the 
            class. ///  
        public NorthwindDomainContext() : 
        this( 
        new WebDomainClient 
        
          ( 
         new Uri( 
         "SilverlightApplication1-Web-NorthwindDomainService.svc", UriKind.Relative))) { } 
         ///  /// Initializes a new instance of the 
             class with the specified service URI. ///  
         /// The NorthwindDomainService service URI. 
         public NorthwindDomainContext(Uri serviceUri) : 
         this( 
         new WebDomainClient 
         
           (serviceUri)) { } 
          ///  /// Initializes a new instance of the 
              class with the specified 
             . ///  
          /// The DomainClient instance to use for this DomainContext. 
          public NorthwindDomainContext(DomainClient domainClient) : 
          base(domainClient) { 
          this.OnCreated(); } 
          ///  /// Gets the set of 
              entity instances that have been loaded into this 
              instance. ///  
          public EntitySet 
          
            Customers { get { 
           return 
           base.EntityContainer.GetEntitySet 
           
             (); } } 
            ///  /// Gets an EntityQuery instance that can be used to load 
                entity instances using the 'GetCustomers' query. ///  
            /// 
             
               An EntityQuery that can be loaded to retrieve 
               entity instances. 
              
            public EntityQuery 
            
              GetCustomersQuery() { 
             this.ValidateMethod( 
             "GetCustomersQuery", 
             null); 
             return 
             base.CreateQuery 
             
               ( 
              "GetCustomers", 
              null, 
              false, 
              true); } 
              ///  /// Creates a new EntityContainer for this DomainContext's EntitySets. ///  
              /// 
               
                 A new container instance. 
                
              protected 
              override EntityContainer CreateEntityContainer() { 
              return 
              new NorthwindDomainContextEntityContainer(); } 
              ///  /// Service contract for the 'NorthwindDomainService' DomainService. ///  [ServiceContract()] 
              public 
              interface INorthwindDomainServiceContract { 
              ///  /// Asynchronously invokes the 'GetCustomers' operation. ///  
              /// Callback to invoke on completion. 
              /// Optional state object. 
              /// 
               
                 An IAsyncResult that can be used to monitor the request. 
                [FaultContract( 
              typeof(DomainServiceFault), Action= 
              "http://tempuri.org/NorthwindDomainService/GetCustomersDomainServiceFault", Name= 
              "DomainServiceFault", Namespace= 
              "DomainServices")] [OperationContract(AsyncPattern= 
              true, Action= 
              "http://tempuri.org/NorthwindDomainService/GetCustomers", ReplyAction= 
              "http://tempuri.org/NorthwindDomainService/GetCustomersResponse")] [WebGet()] IAsyncResult BeginGetCustomers(AsyncCallback callback, 
              object asyncState); 
              ///  /// Completes the asynchronous operation begun by 'BeginGetCustomers'. ///  
              /// The IAsyncResult returned from 'BeginGetCustomers'. 
              /// 
               
                 The 'QueryResult' returned from the 'GetCustomers' operation. 
                QueryResult 
              
                EndGetCustomers(IAsyncResult result); 
               ///  /// Asynchronously invokes the 'SubmitChanges' operation. ///  
               /// The change-set to submit. 
               /// Callback to invoke on completion. 
               /// Optional state object. 
               /// 
                
                  An IAsyncResult that can be used to monitor the request. 
                 [FaultContract( 
               typeof(DomainServiceFault), Action= 
               "http://tempuri.org/NorthwindDomainService/SubmitChangesDomainServiceFault", Name= 
               "DomainServiceFault", Namespace= 
               "DomainServices")] [OperationContract(AsyncPattern= 
               true, Action= 
               "http://tempuri.org/NorthwindDomainService/SubmitChanges", ReplyAction= 
               "http://tempuri.org/NorthwindDomainService/SubmitChangesResponse")] IAsyncResult BeginSubmitChanges(IEnumerable 
               
                 changeSet, AsyncCallback callback, 
                object asyncState); 
                ///  /// Completes the asynchronous operation begun by 'BeginSubmitChanges'. ///  
                /// The IAsyncResult returned from 'BeginSubmitChanges'. 
                /// 
                 
                   The collection of change-set entry elements returned from 'SubmitChanges'. 
                  IEnumerable 
                
                  EndSubmitChanges(IAsyncResult result); } 
                 internal 
                 sealed 
                 class NorthwindDomainContextEntityContainer : EntityContainer { 
                 public NorthwindDomainContextEntityContainer() { 
                 this.CreateEntitySet 
                 
                   (EntitySetOperations.All); } } } } 
                  
                 
                
               
              
             
            
           
          
         
        
      
     
     
    
    
   
   
  
  

 

我们看到,其实在客户端是有一个WebContext的类型的。下面演示一下,我们具体在Silverlight里面如何使用该服务吧

MainPage.xaml

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White">
        <sdk:DataGrid AutoGenerateColumns="true" Margin="20,26,23,12" Name="dataGrid1" />
    
   
   Grid>

    
    UserControl>

 

MainPage.xaml.cs

using System.Windows;
using System.Windows.Controls;
using SilverlightApplication1.Web;

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            var ctx = new NorthwindDomainContext();
            var op = ctx.Load
  
  
   
   (ctx.GetCustomersQuery());
            dataGrid1.ItemsSource = op.Entities;
        }
    }
}

  
  

【备注】我们都知道Silverlight中访问数据都是异步的(一般编写代码都比较繁琐),RIA Service对此做了优化,但实际上上面这个Load方法也是一个异步方法。

 

image

如果我们需要对数据进行过滤,服务器不需要做任何修改。只要在Silverlight中用下面这样的语法就可以了

using System.Windows;
using System.Windows.Controls;
using SilverlightApplication1.Web;
using System.Linq;
using System.ServiceModel.DomainServices.Client;

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            var ctx = new NorthwindDomainContext();
            var query = from c in ctx.GetCustomersQuery()
                        where c.Country == "USA"
                        select c;

                        
            var op = ctx.Load
  
  
   
   (query);
            dataGrid1.ItemsSource = op.Entities;
        }
    }
}

  
  

image

这是多么方便啊。所以,我有时候也喜欢将RIA解读为:Rapid Internet Application. 它大大地提高了Silverlight应用程序开发的生产力。

 

那么,RIA Service后台到底是如何实现的呢?与前面几种都不同的是,它是通过一个特殊的HttpModule实现的

  <configSections>
    <sectionGroup name="system.serviceModel">
      <section name="domainServices" type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="MachineToApplication" requirePermission="false" />
    
   
   sectionGroup>
  
    
    configSections>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="DomainServiceModule" preCondition="managedHandler"
        type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    
     
     modules>
    <validation validateIntegratedModeConfiguration="false" />
  
      
      system.webServer>
  <system.web>
    <httpModules>
      <add name="DomainServiceModule" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
     
       httpModules> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />  
        assemblies>  
         compilation>  
          system.web>

【备注】RIA Service并没有显式的SVC文件,但会有一个隐含的svc路径可以访问。(请参考下图)

 

那么,RIA Service是使用什么样的Binding呢?根据资料,它应该支持下面三种Binding。默认是第三种,它用二进制的方式发送数据,目的主要是提高传输效率。

  1. For WebHttpBinding: “baseAddress" (REST with JSON Endpoint)
  2. For BasicHttpBinding: “baseAddress” + “/soap” (SOAP with XML Endpoint)
  3. For BinaryHttpBinding: “baseAddress” + “/binary” (SOAP with Binary Endpoint)

image

 

那么,是否可以让RIA Service支持标准的SOAP endpoint呢?

这个问题,之前有一个专门的文章介绍,请参考这里:http://www.cnblogs.com/chenxizhang/archive/2011/06/14/2080887.html

 

总结:RIA Service是一种主要用于Silverlight应用程序的服务。它可以结合ADO.NET Entity Framework数据模型,或者自定义数据模型,极大地方便了Silverlight应用程序进行数据访问的设计。它默认是用二进制的binding,但也可以通过一些配置添加soap endpoint.

 

最后,本文总结分析了五种主要的WCF服务类型,以及他们的一些内在设计原理,下面这个表格可以做一个综合比较

image

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值