【WCF系列】之二:构建WCF的服务架构

 在前面我们简单的介绍了WCF的由来及在SOA系统中所处的重要地位。但需要向大家说明的是,SOA绝对不光只是WCF,SOA是一个复杂庞大的系统架构。而WCF只是它的一个技术开发平台。企业部署SOA重在通过一定的投入进行组织的深刻变革以获得最大的利益。而并不关心是否用WCF来开发。真正需要注重它的则是技术人员。但肯定的是脱离了企业应用的技术实现是没有任何意义的。

  闲话不多说,还是让我们回到代码上来吧。这里我们完成一个简单的案例,在这个系统中,使用WCF来发布一个服务,用来完成摄氏温度的转换。为了实现这个架构,我们首先需要创建一个新的Soluting。

  这里我们使用VS2008,要创建WCF项目有多种方法,例如,可以在“新建项目”中选择“WCF服务库”。

点击“确定”,IDE将为你完成一个基础的WCF架构模板,其架构如下:

     其中IService1为一个接口层,它定义服务端和客户端共同继承的接口,并通过Attribute将其转换为契约(契约的概念后面会详细讲到)。Service1为服务层,通过继承接口层的契约向外界提供服务。这样创建WCF项目非常简便,但有一问题,它创建的架构是在同一个项目中使用Class文件进行分层。这不适合企业级应用的需要。因此,我们选择自己创建WCF架构。

     1)新建一个“类库”解决方案,方案的名称改为MyWCF,项目名称为MyWCF.Contract,类名称改为IContract

     2)首先为改项目添加System.ServiceModel的引用。

     3)在IContract.cs文件中,首先引用System.ServiceModel的命名空间。将主class改为interface。代码入下:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace MyWCF.Contract
{
    [ServiceContract]
    public interface IContract
    {
        /// <summary>
        /// 温度转换
        /// </summary>
        /// <param name="celsius">摄氏温度</param>
        /// <returns>转换结果,华氏温度</returns>
        [OperationContract]
        float GetFahrenheit(float celsius);
    }
}

  其中ServiceContract Attribute为接口服务契约,表示此接口能被作为WCF的契约,OperationContract Attribute为方法契约。使用Attribute申明的方式,使得契约的设计更为方便,结构也更加清晰。

  这里我们还需要对契约的概念做一个简单说明,以利于大家理解前面讲的概念。在SOA架构中,契约是一个非常重要的概念。SOA的核心是服务,既然是服务就要牵涉到两方,服务方和被服务方。就好比电信局向大家提供电话通讯服务,为了保障服务的顺利和安全,电信局(服务方)需要大家签订一个服务条款,约定电信局提供服务的形式,内容等信息,也约定了被服务方的权利和义务。签订了此协议后,服务双方都需要遵守这份合同条款。这个服务条款,就可以被理解成契约。被服务方要想接受服务方提供的服务,就必须和服务方遵守相同的契约。当然,WCF的契约远不只这么简单,在后面我们还会进行详细的介绍。

  4)理解了契约的概念,我们再回头看刚才的程序。在上面的程序中,契约被表现为了一个接口和相关的方法,并使用不同的Attribute将其指定为契约。有了这个契约后,我们就可以在其基础上来创建服务了。因此接下来需要一个服务层,我们在解决方案中再添加一个类库项目,取名为MyWCF.Service。并在项目中添加System.ServiceModel的引用(具体添加方法见上面的操作)和对MyWCF.Contract项目的引用,如下图。

      

     5)有了对契约的引用后,服务就可以继承契约的接口了,代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using MyWCF.Contract;
namespace MyWCF.Service
{
    /// <summary>
    /// 继承契约的服务
    /// </summary>
    public class TemperatureService : IContract
    {
        /// <summary>
        /// 稳定转换的方法
        /// </summary>
        /// <param name="celsius">摄氏温度</param>
        /// <returns>返回华氏温度</returns>
        public float GetFahrenheit(float celsius)
        {
            //将摄氏度转换为华氏度的公式“F=1.8(C)+32”
            celsius = celsius * 1.8F; //执行乘法
            return celsius + 32; //执行加法
        }
    }
}

     6)契约已签署,服务已设计完成,接下来需要一个发布服务的平台,这里我们建立一个ConsoleApplication作为服务的载体,在WCF中,称为对服务的“寄存”。

     这里我们顺便要提到一些新的概念,以便你对寄存过程的理解。前面我们讲到过,寄存实际上是用来发布服务。有了服务后,用户就可以通过这个服务进行通讯了。但你要和服务端通讯,就必须有一个通讯的渠道,好比你与电信签署了通讯协议,但最后通讯过程完全还是由电信局来控制。而这个通讯过程对于用户来说则是完全封闭的,用户也不需要去关系通讯的细节,反正只要拿起电话,拨通号码,能和对方通话就行了。但对于开发人员来说,就必须考虑到如何通讯,于是WCF为你提供了这样一个对象Endpoint,它负责服务端和客户端通讯的细节。

     既然涉及到通讯的细节,就需要考虑到几个东西,首先是地址Address,就好比我们的拨号号码。在Endpoint中,地址用uri对象表示。比如Uri uri = new Uri(http://localhost:8080/MyWCF); 表示地址在本机的8080端口上的MyWCF项目中。

     其次是Binding,这个概念在WCF中十分重要。因为除地址外,通讯还需要重要考虑的就是通讯所使用的协议、编码方式,已经安全性等内容。对于其它开发架构来说,要设置好这些内容是一件非常麻烦的事情,要考虑的东西很多。但对于WCF来说就再简单不过了,这些所有的东西只需要用一个已设置好的对象来指明就行了,因为WCF已为我们提供了一整套用于应付各种通讯的对象,这个在后面我们还会单独介绍。这里我们用BasicHttpBinding对象,这个对象表示使用HTTP协议,没有任何安全设置,消息都以明文传送,对客户端也不进行验证。

     最后一个是契约Contract,前面我们已经提到过,它用来描述服务提供的各种操作。看到这里,细心的朋友已经可以看出一个小技巧。就是Endpoint中的三要素取其每项的第一个字母,合起来就是ABC,呵呵,不知是凑巧还是故意这样设置,不过也倒方便我们记忆了。

     设置好Endpoint,实际上已能够进行通讯了,但要发布服务,还差一个东西,就是行为Behavior。行为的作用是把服务通过WSDL对外暴露对服务的Metadata描述。这句话很绕口,还是举个例子,我们在打10086充电话费时,首先会听到一个语音提示,说听到提示音后请按键选择服务项目,1为普通话,2为英语。按1进入后又会说为手机充值请按1,查询本手机花费请按2...... OK,这下你清楚了吧。行为好比提供一个服务清单,供你选择,如果没有它,即使服务发布出来了,你也无法知道该选择什么。当然,行为还有一些其它功能,这里我们不详述,还是留到后面去深入理解。

     7)好了,理解了这些,我们又可以继续项目的开发了,我们把上面刚添加的控制台项目的名称定为MyWCF.Hosting,在该项目中,需引用前面的两个项目,其代码如下:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using MyWCF.Service;
using MyWCF.Contract;

namespace MyWCF.Hosting
{
    class Program
    {
        static void Main(string[] args)
        {
            CreateHosting();
        }

        static void CreateHosting()
        {
            //创建服务地址信息,用来发布WCF服务的位置
            Uri uri = new Uri("
http://localhost:8080/MyWCF");
            //创建服务寄存的对象
            using(ServiceHost host=new ServiceHost(typeof(TemperatureService),uri))
            {
                //为服务添加Endpoint,用于和客户端通讯
                host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),string.Empty);
                //创建服务的行为Behavior
               ServiceMetadataBehavior behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
                if (behavior == null)
                {
                    //创建Behavior对象
                    behavior = new ServiceMetadataBehavior();
                    //允许通过HTTP的get方法获取sevice的WSDL元数据
                    behavior.HttpGetEnabled = true;
                    //指示发布服务的WSDL的Url地址
                    behavior.HttpGetUrl = uri;
                    //将新建立的behavior添加到host中
                    host.Description.Behaviors.Add(behavior);
                }
                else
                {
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = uri;
                }
                //建立服务监听的事件,当服务启动时给出提示信息
                host.Opened += new EventHandler(host_Opened);
                //启动服务
                host.Open();
                Console.ReadLine();
            }
        }

        static void host_Opened(object sender, EventArgs e)
        {
            Console.WriteLine("服务已启动,按回车键停止!");
        }
    }
}

     8)全部代码写完后,一个WCF的服务端实际上已经被建立起来了,这时候已经可以发布项目了。我们把MyWCF.Hosting项目定为启动项目,编译运行后得到如下画面。

     这表面服务已经在运行过程中了。我们可以测试这个服务,还记得前面设置的Uri吗,现在就可以通过这个Uri来访问服务页面。打开你的浏览器,将Uri的地址输入到里面并回车,会出现下面的页面。

     因为图太大,这里我们只截取了一部分。可以看到,我们在MyWCF.Service中定义的服务此时已被显示出来。并且可以通过svcutil.exe工具来创建客户端所需的信息。

     对于创建WCF服务,我们需要重点理解架构为何如此安排,再来看看VS 2008中项目的架构:

                           

     这个架构中,首先是把服务与契约分离。因为契约是服务端和客户端都需要遵循的,分离出来有助于降低耦合度。而服务端与提供服务的寄存方也单独分离开,这样,寄存方就不局限于单一的某种平台,可以是ConsoleApplication,也可以是WinForm,或是WebForm,当服务发生变化时,寄存方也不会受到耦合的影响。另外,在WCF的架构中,要求服务是自治的,每一个服务之间不应受到牵连,也是采用这种架构的主要原因。

     配置Endpoint和Behavior是一件较为繁琐的工作,特别是当服务器的Uri发生变化时(在网络环境下,这会较为非常频繁),难免要涉及到代码的修改。而WCF正是为了解决这些配置问题而生,利用.NET自身的优势,微软将这件工作变得非常简单。所有的工作都可以在配置文件中完成,这时候,我们只需要在MyWCF.Hosting项目中添加一个App.config文件,代码如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="MyWCF.Service.TemperatureService" behaviorConfiguration="behavior">
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:8080/MyWCF"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="basicHttpBinding" contract="MyWCF.Contract.IContract"></endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="behavior">
          <serviceMetadata httpGetEnabled="true" httpGetUrl=""/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

     可以看到,在配置文件中,Uri、Endpoint和Behavior都被配置好了,这时候MyWCF.Hosting的代码变得异常简单:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using MyWCF.Service;
using MyWCF.Contract;

namespace MyWCF.Hosting
{
    class Program
    {
        static void Main(string[] args)
        {
            CreateHosting();
        }

        static void CreateHosting()
        {
            using (ServiceHost host = new ServiceHost(typeof(TemperatureService)))
            {
                host.Opened += new EventHandler(host_Opened);
                //启动服务
                host.Open();
                Console.ReadLine();
            }
        }

        static void host_Opened(object sender, EventArgs e)
        {
            Console.WriteLine("服务已启动,按回车键停止!");
        }

    }
}

     配置好后,当服务启动时,自动到config文件中读取相应的配置。程序的结构被进一步简化,服务被彻底的从应用层分离开,完全具备了自治的特性。当你再看到这里时,是不是觉得整体系如此简单,清晰,这就是WCF来的深度体验。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值