• WCF基础系列1 创建一个简单的WCF应用


    之前做项目的时候没怎么用到WCF,对它也是一知半解,只知道要想学好.net,掌握面向服务,分布式开发,WCF是很重要的一部分。现在进入到一家新的公司,现在的项目里都用WCF来做前端与服务端通信,虽然还只是简单的应用,但是借此机会,我觉得该好好学习WCF了,我也从最基础的开始学起。今天要总结的是如何创建一个简单的WCF应用(一个web版计算器),虽然这只是一个非常简单的例子,但麻雀虽小,五脏俱全。它还是很好地展现了创建WCF程序的各个方面。我准备从以下几点来总结它。

    1,项目搭建
    2,创建服务契约
    3,创建服务
    4,服务寄宿
    5,客户端调用服务

    1,项目搭建

    构建一个完整的WCF应用一般需要四个工程,如图所示。

    wcf01

    下面来详细介绍一下每个工程的作用。

    1) WCFDemo.Contracts,一个类库项目,定义了服务契约(Service Contract),需要引用System.ServiceModel程序集,因为WCF的绝大部分实现和API定义在该程序集中。
    2) WCFDemo.Services,一个类库项目,提供对WCF服务的实现。该工程中所有的WCF服务实现了定义在WCFDemo.Contracts中的契约。所以需要添加对WCFDemo.Contracts的引用。
    3) WCFDemo.Host,一个Web Application项目,实现对定义在WCFDemo.Services项目中的服务的寄宿。因为我这里采用的是IIS服务寄宿的方式,所以创建了一个web application项目。该项目需要添加对WCFDemo.Contracts,WCFDemo.Services和System.ServiceModel的引用。
    4) WCFDemo.WebClient,一个Web Application项目,模拟服务调用的客户端。需要添加对System.ServiceModel的引用。

    项目总体上搭建好了,下面我们开始创建服务契约

    2,创建服务契约

    一般地,我们通过定义接口的形式来创建服务契约。下面我们将一个接口ICalculatorContract定义成服务契约,通过在接口上应用特性ServiceContract将其定义为服务契约,在其方法成员上应用特性OperationContract而将其定义为服务操作。具体代码如下。

    namespace WCFDemo.Contracts
    {
        [ServiceContract]//通过ServiceContractAttribute特性将这个接口定义为服务契约
        public interface ICalculatorContract
        {
            [OperationContract]//通过OperationContractAttribute特性将方法定义为服务操作
            double Add(double x,double y);
    
            [OperationContract]
            double Subtract(double x,double y);
    
            [OperationContract]
            double Multiply(double x,double y);
    
            [OperationContract]
            double Divide(double x,double y);
        }
    }

    还有,接口的名称最好以Contract结尾,这样可读性更好一点,当然这只是我的个人习惯而已,没有强制要求。服务契约创建好了以后,下面我们就要创建服务来实现契约中定义的接口。

    3,创建服务

    我们通过实现服务契约来创建具体的WCF服务。因此我们创建一个CalculatorService类来实现ICalculatorContract这个契约接口,并且提供接口的所有实现。代码如下。

    namespace WCFDemo.Services
    {
        public class CalculatorService:ICalculatorContract//实现了契约接口
        {
            public double Add(double x, double y)
            {
                return x + y;
            }
    
            public double Subtract(double x, double y)
            {
                return x - y;
            }
    
            public double Multiply(double x, double y)
            {
                return x * y;
            }
    
            public double Divide(double x, double y)
            {
                return x / y;
            }
        }
    }

    WCF服务创建好了以后,我们就要进行服务的寄宿了。

    4,服务寄宿

    因为WCF服务必须依附着一个进程(或称为宿主)才可以运行。当然宿主可以是一个控制台程序,IIS,或者是Windows服务。这里我们选择IIS实现服务寄宿,下面一篇我会总结WCF服务的各种寄宿方式和优缺点。实现IIS寄宿一般有三个步骤,创建一个web application,为WCF服务创建.svc文件,和创建配置文件(主要是定义终结点和服务行为的定义)。这里web application已经创建好了(WCFDemo.Host),下面我们在这个web项目中创建一个名为CalculatorService的svc文件。

    .svc文件的内容很简单,仅仅包含一个%@ServiceHost%指令,该指令具有一个必需的Service属性和一系列可选的属性。CalculatorService.svc文件内容如下。

    注意,这里Service属性被指定为服务的全名(命名空间+类型名称),所以这里应该为WCFDemo.Services.CalculatorService。

    接下来就要通过配置的方式来定义服务寄宿的终结点endpoint和用于元数据发布的服务行为ServiceMetadataBehavior,因此我们需要在WCFDemo.Host项止的web.config文件中添加这些信息。配置文件的创建我们一般采用的是vs自带的WCF服务配置编辑器,可以单击“工具”菜单,选择“WCF服务配置编辑器(WCF service Configuration Editor)”菜单项来打开WCF服务配置编辑器,如图所示。

    wcf02

    创建好的配置文件如下XML所示。

    <system.serviceModel>
        <!--定义寄宿服务的终结点-->
        <services>
          <service behaviorConfiguration="metadataBehavior" name="WCFDemo.Services.CalculatorService">
            <endpoint binding="basicHttpBinding" bindingConfiguration="" contract="WCFDemo.Contracts.ICalculatorContract" />
          </service>
        </services>
        <!--定义服务行为-->
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <serviceMetadata httpGetEnabled="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>

    注意,这里无需给endpoint指定地址address,因为svc文件所在的地址就是服务地址,我们可以通过在.svc文件的地址加上?wsdl查询字符串就可以访问WCF服务的元数据wsdl文件。

    5,客户端调用服务

    客户端调用WCF服务有两种方法,一种是通过添加服务引用并利用生成的服务代理类来调用WCF服务,第二种是通过System.ServiceModel.ChannelFactory<TChannel>直接创建服务代理对象。

    方法一:添加服务引用

    通过添加服务引用后,vs会为我们自动生成用于服务调用的类和配置。在生成的一系列类中,有一个继承自ClientBase<T>的服务代理类,我们可以通过实例化它来调用WCF服务。

    生成的配置文件如下。

    <system.serviceModel>
            <bindings>
                <basicHttpBinding>
                    <binding name="BasicHttpBinding_ICalculatorContract" 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>
            <!--vs自动生成的endpoint-->
            <client>
                <endpoint address="http://localhost:3721/CalculatorService.svc"
                    binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ICalculatorContract"
                    contract="wcf_CalculatorService.ICalculatorContract" name="BasicHttpBinding_ICalculatorContract" />
            </client>
        </system.serviceModel>

    生成的代理类如下。

    wcf03

    我们创建CalculatorContractClient对象来调用WCF服务,代码如下:

    private void btnCalculate_ServerClick(object sender, EventArgs e)
            {
                //实例化服务代理类
                CalculatorContractClient proxy = new CalculatorContractClient();
    
                var op = this.opSelect.Value;
                try
                {
                    switch (op)
                    {
                        case "+":
                            this.txtResults.Value = proxy.Add(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();//通过代理类对象调用WCF服务
                            break;
                        case "-":
                            this.txtResults.Value = proxy.Subtract(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();
                            break;
                        case "*":
                            this.txtResults.Value = proxy.Multiply(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();
                            break;
                        case "/":
                            this.txtResults.Value = proxy.Divide(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();
                            break;
                    }
                }
                catch(Exception ex)
                { }
            }

    方法二:通过System.ServiceModel.ChannelFactory<TChannel>直接创建服务代理对象

    vs在添加服务引用的过程中,会在客户端创建一个与服务端等效的服务契约接口,如下图。由于服务端与客户端都在同一个解决方案中,因此完全可以让服务端和客户端引用相同的契约。所以我们先将之前添加的服务引用移除,然后为WCFDemo.WebClient项目添加对WCFDemo.Contracts的引用。

    为ChannelFactory创建的配置,指定了endpoint的地址,绑定和契约,并为endpoint添加了一个name配置名称。基本同服务端一样,除了添加了address地址。

    <system.serviceModel>
          <client>
            <!--创建endpoint,指定了name供ChannelFactory使用,和abc三要素-->
            <endpoint name="calculatorservice"
                      address="http://localhost:3721/CalculatorService.svc" 
                      binding="basicHttpBinding" 
                      contract="WCFDemo.Contracts.ICalculatorContract" />
          </client>
        </system.serviceModel>

    客户端调用代码:

    private void btnCalculate_ServerClick(object sender, EventArgs e)
            {
                //创建ChannelFactory对象
                using (ChannelFactory<ICalculatorContract> channelFactory = new ChannelFactory<ICalculatorContract>("calculatorservice"))
                {
                    //创建接口对象
                    ICalculatorContract proxy= channelFactory.CreateChannel();
    
                    var op = this.opSelect.Value;
                    try
                    {
                        switch (op)
                        {
                            case "+":
                                this.txtResults.Value = proxy.Add(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();//通过代理类对象调用WCF服务
                                break;
                            case "-":
                                this.txtResults.Value = proxy.Subtract(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();
                                break;
                            case "*":
                                this.txtResults.Value = proxy.Multiply(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();
                                break;
                            case "/":
                                this.txtResults.Value = proxy.Divide(double.Parse(txt1.Value), double.Parse(txt2.Value)).ToString();
                                break;
                        }
                    }
                    catch (Exception ex)
                    { }
                }
            }
  • 相关阅读:
    UML 基础知识
    制作嵌入式根文件系统
    oracle 学习之 PL/SQL
    Password for '(null)' GNOME keyring:
    oracle 学习笔记
    lua关于编译后无法使用
    android,wince,windows,ios mms 网络电台收音机,小巧,兼容性好,性能高
    lua批量编译目前支持5.2,5.1
    lua关于编译后无法使用
    dlna support windows
  • 原文地址:https://www.cnblogs.com/mcgrady/p/3137496.html
Copyright © 2020-2023  润新知