• WCF学习之三, 寄宿方式 代码,配置文件


    可以通过代码或者配置文件寄宿WCF服务,在使用过程中的一些心得,记录一下,方便后续查阅。

    预备知识,几个地址的作用

    1、 behavior.HttpGetUrl  定义元数据的地址,如果不定义基地址,则必须定义HttpGetUrl ,是The absolute or relative location of the metadata.

    2、baseAddresses 服务的地址,因为元数据(metadata)是属于一个host的,并不属于一个endpoint。此基地址就是客户端添加服务引用的地址,

         所以baseAddress解决了Where to locate the WCF Service?

         如果定义endpoint daaress="",则endpoint daaress就等于baseAddresses

    3、终结点endpoint address ,用于和客户端通信,服务端与客户端endpoint的address必须严格对应。

    4、在这三个地址中如果没有定义基地址则另外两个地址HttpGetUrl,endpoint address必须都不能为空

    5、如果定义了baseaddress,则另外两个地址均可为空。此时endpoint address就是baseaddress定义的url

    如服务端终结点配置为:

    <endpoint address="http://127.0.0.1:8882/DBServer" binding="wsHttpBinding"  contract="IContract.IContract" />

    即服务端定义address为"http://127.0.0.1:8882/DBServer" ,那么客户端的终结点address也必须为"http://127.0.0.1:8882/DBServer" 才可以建立通讯。

    当我们Host一个WCF Service的时候,我们必须给他定义一个或多个Endpoint,然后service通过这个定义的Endpoint进行监听来自Client端的请求。当我们的Application需要调用这个Service的时候,因为Client 和Service是通过Endpoint的进行通信的, 所以我们必须为我们的Application定义Client端的Endpoint。只有当Client的Endpoint和Service端某个Endpoint相互匹配(Service端可以为一个Service定义多个Endpoint),Client端的请求才能被Service端监听到。也就是说,我们只有在Client具有一个与Service端完全匹配的Endpoint,我们才能调用这个Service。而这种匹配是比较严格的,比如从匹配Address方面,Client端和Service端的Endpoint Address不仅仅在URI上要完全匹配Service, 他们的Headers也需要相互匹配。对于Binding, 一般地,Client需要有一个与Service端完全一样的Binding,他们之间才能通信。

    4、如果定义了基地址,则endpoint address可以设为空,这样所有endpoint的address就是基地址所定义的url

    以下是一个建立wcf服务及调用的例子

    1、新建一个控制台项目,添加以下程序集引用

    using System.ServiceModel;
    using System.ServiceModel.Web;

    2、添加类IContract,只是为了演示,Contract和Service都建在一个项目里面了

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace IContract //注意:如果命名空间为WCFHost,则在采用配置文件寄宿服务的时候会不认配置文件,不知道为什么
    {
        [ServiceContract]
        public interface IContract
        {
            [OperationContract]
            [WebInvoke(UriTemplate = "Add/{x}/{y}")]
            double Add(double x, double y);
    
        }
       
    }
    

     3、添加类DBService

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace DBService  //注意:如果命名空间为WCFHost,则在采用配置文件寄宿服务的时候会不认配置文件,不知道为什么
    {
        public class DBService:IContract.IContract
        {
            public double Add(double x, double y)
            {
                return x+y;
            }
        }
    }
    

    4、使用代码方式配置服务并开启服务,三个地址都进行了定义

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace WCFHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    //Uri uri = new Uri("http://127.0.0.1:8881/DBServer"); //和下面一句等价
                    Uri uri = new Uri("http://localhost:8881/DBServer");
    
                    using (ServiceHost host = new ServiceHost(typeof(DBService.DBService), uri))
                    {
                        //定义元数据发布方式,此处  通过在服务所在的URL后加“?wsdl”的方式公布WSDL,可直接通过HTTP访问得到。
                        System.ServiceModel.Description.ServiceMetadataBehavior behavior = new System.ServiceModel.Description.ServiceMetadataBehavior();
                        behavior.HttpGetEnabled = true;
                        //此句也可不要,HttpGetUrl定义元数据的url
                        behavior.HttpGetUrl = new Uri("http://localhost:8882/DBService");
                        host.Description.Behaviors.Add(behavior);
    
                        Uri endpointAddress =new Uri("http://127.0.0.1:8883/DBServer");
    
                        //添加终结点
                        host.AddServiceEndpoint(typeof(IContract.IContract), new WSHttpBinding(), endpointAddress);
                       
                        ////定义终结点地址(如果定义了baseadress,终结点地址也可为空)
                        //host.AddServiceEndpoint(typeof(IContract.IContract), new WSHttpBinding(), "");
                      
                        host.Opened += host_Opened;
                        host.Open();
                        Console.ReadLine();
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
    
            static void host_Opened(object sender, EventArgs e)
            {
                Console.WriteLine("DBService opened successful");
            }
        }
    } 

    5、使用配置文件配置服务并开启服务,作用同4

    5.1 添加配置文件,配置文件中定义了三个address,基地址(服务地址),元数据地址、与客户端的通讯地址

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8881/DBServer"></serviceMetadata>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <!--注意此处name必须与第三步服务的命名空间一致-->
          <service behaviorConfiguration="metadataBehavior" name="DBService.DBService">
            <endpoint address="http://127.0.0.1:8882/DBServer" binding="wsHttpBinding"  contract="IContract.IContract" />
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:8883/DBServer"/>
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    

      5.2 开启服务

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace WCFHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    ServiceHost host1 = new ServiceHost(typeof(DBService.DBService));
                    host1.Opened += host_Opened;
                    host1.Open();
                    Console.ReadLine();
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
    
            static void host_Opened(object sender, EventArgs e)
            {
                Console.WriteLine("DBService opened successful");
            }
        }
    }

    6、在浏览器中打开服务地址:http://127.0.0.1:8883/DBServer,如下图

    打开元数据地址:http://127.0.0.1:8881/DBServer,如下图

    客户端添加服务,使用服务地址或者元数据地址均可找到服务。

    而终结点地址:http://127.0.0.1:8882/DBServer,无法打开,

    7、在客户端调用服务时,定义终结点address必须服务定义的终结点address匹配,如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace WCFClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                WSHttpBinding binding = new WSHttpBinding();
                EndpointAddress address = new EndpointAddress(new Uri("http://127.0.0.1:8882/DBServer"));
                ServiceReference1.ContractClient client = new ServiceReference1.ContractClient(binding, address);
                double i = client.Add(1, 2);
                Console.WriteLine(i);
                Console.ReadLine();
            }
        }
    }

     8、只定义baseAddress,不定义其他两个address

    8.1 配置文件定义

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <serviceMetadata httpGetEnabled="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <!--注意此处name必须与第三步服务的命名空间一致-->
          <service behaviorConfiguration="metadataBehavior" name="DBService.DBService">
            <endpoint address="" binding="wsHttpBinding"  contract="IContract.IContract" />
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:8883/DBServer"/>
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    

      开启服务

                    ServiceHost host1 = new ServiceHost(typeof(DBService.DBService));
                    host1.Opened += host_Opened;
                    host1.Open();
                    Console.ReadLine();
    

      

    8.2 代码定义并开启服务

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace WCFHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    //Uri uri = new Uri("http://127.0.0.1:8881/DBServer"); //和下面一句等价
                    Uri uri = new Uri("http://localhost:8881/DBServer");
    
                    using (ServiceHost host = new ServiceHost(typeof(DBService.DBService), uri))
                    {
                        //定义元数据发布方式,此处  通过在服务所在的URL后加“?wsdl”的方式公布WSDL,可直接通过HTTP访问得到。
                        System.ServiceModel.Description.ServiceMetadataBehavior behavior = new System.ServiceModel.Description.ServiceMetadataBehavior();
                        behavior.HttpGetEnabled = true;
                        host.Description.Behaviors.Add(behavior);
    
                        host.AddServiceEndpoint(typeof(IContract.IContract), new WSHttpBinding(), "");
                      
                        host.Opened += host_Opened;
                        host.Open();
                        Console.ReadLine();
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
    
            static void host_Opened(object sender, EventArgs e)
            {
                Console.WriteLine("DBService opened successful");
            }
        }
    }
    

      8.3 客户端,直接添加服务引用会自动生成配置文件,然后调用

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <system.serviceModel>
            <bindings>
                <wsHttpBinding>
                    <binding name="WSHttpBinding_IContract" />
                </wsHttpBinding>
            </bindings>
            <client>
                <endpoint address="http://localhost:8881/DBServer" binding="wsHttpBinding"
                    bindingConfiguration="WSHttpBinding_IContract" contract="ServiceReference1.IContract"
                    name="WSHttpBinding_IContract" />
            </client>
        </system.serviceModel>
    </configuration>
    

      

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace WCFClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                ServiceReference1.ContractClient client = new ServiceReference1.ContractClient();
    
    
                double i = client.Add(1, 2);
                Console.WriteLine(i);
                Console.ReadLine();
    
            }
        }
    }
    

      8.4 也可不用配置文件,通过代码直接调用

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    
    namespace WCFClient
    {
        class Program
        {
            static void Main(string[] args)
            {
               
                WSHttpBinding binding = new WSHttpBinding();
                EndpointAddress address = new EndpointAddress(new Uri("http://127.0.0.1:8881/DBServer"));
                ServiceReference1.ContractClient client = new ServiceReference1.ContractClient(binding, address);
                client.Endpoint.Binding = binding;
    
                double i = client.Add(1, 2);
                Console.WriteLine(i);
                Console.ReadLine();
    
            }
        }
    }
    

      9、比较WCF发布元数据的两种方式,不管哪种方式ServiceMetadata节点必须存在。

              元数据描述服务的细节,导出以及发布元数据,都是由一个服务中的ServiceMetadataBehavior实现的。

             必须为服务配置ServiceMetadata行为,即配置节点,才能为发布元数据,才能再使用httpGetEnabled=true或者httpGetEnabled=false发布元数据。

    <serviceBehaviors>

       <behavior name="metadataBehavior">
          <serviceMetadata  />
       </behavior>
    </serviceBehaviors>

    9.1 WSDL,此方式通过在服务的URL后加“?wsdl”的方式公布WSDL,可直接通过HTTP访问。

       设置httpGetEnabled="true" 发布元数据(图片见上面步骤6),设置为false不发布元数据。以下为设置为false的情况:
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <!--<serviceMetadata httpGetEnabled="true" />-->    <!--设置为true公布元数据,服务可以开启,客户端也可以访问服务-->
              <serviceMetadata httpGetEnabled="false" />  <!--设置为false不公布元数据,服务可以开启,但是客户端无法访问服务-->
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <!--注意此处name必须与第三步服务的命名空间一致-->
          <service behaviorConfiguration="metadataBehavior" name="DBService.DBService">
            <endpoint address="" binding="wsHttpBinding"  contract="IContract.IContract" />
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:8883/DBServer"/>
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    

    设置为false的情况:

      

    9.2 MEX:此方式以一般的终结点方式公布,支持各种协议:http、tcp、NamedPipe

          注意:以终结点的方式公开的元数据,无法通过浏览器查看元数据内容

          添加mex终结点发布元数据,此时httpGetEnabled设置为true和false均可访问服务,只是元数据发布方式不同。

    有mex终结点,httpGetEnabled="true",此时服务可以开启,客户端也可以访问服务,元数据以wsdl方式发布。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <serviceMetadata httpGetEnabled="true" />  <!--设置true,则以wsdl方式发布-->
              <!--<serviceMetadata httpGetEnabled="false" />-->  <!--设置false,则以mex方式发布-->
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <!--注意此处name必须与第三步服务的命名空间一致-->
          <service behaviorConfiguration="metadataBehavior" name="DBService.DBService">
            <endpoint address="" binding="wsHttpBinding"  contract="IContract.IContract" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:8883/DBServer"/>
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    

      

    有mex终结点,httpGetEnabled="false",此时服务可以开启,客户端也可以访问服务,元数据以mex方式发布。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <!--<serviceMetadata httpGetEnabled="true" />-->  <!--设置true,则以wsdl方式发布-->
              <serviceMetadata httpGetEnabled="false" />  <!--设置false,则以mex方式发布-->
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <!--注意此处name必须与第三步服务的命名空间一致-->
          <service behaviorConfiguration="metadataBehavior" name="DBService.DBService">
            <endpoint address="" binding="wsHttpBinding"  contract="IContract.IContract" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:8883/DBServer"/>
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    

      

     例子的源代码

     参考:

    我的WCF之旅(2):Endpoint Overview

    WCF公开服务元数据方式

    整理一下思路,探讨WCF(二)

    WCF元数据发布的2种方式:httpGetEnabled与mex

    WCF 第五章 导出并发布元数据(服务行为)

    如何:使用代码发布服务的元数据

    《我的WCF之旅》博文系列汇总

  • 相关阅读:
    HIVE 技巧积累之合并重叠日期
    记一次hive版本升级
    【转】elasticsearch中字段类型默认显示{ "foo": { "type": "text", "fields": { "keyword": {"type": "keyword", "ignore_above": 256} }
    【转】深入理解Linux修改hostname
    VMware中 CentOS7挂载windows共享文件夹
    Apache版hadoop编译
    No route info of this topic
    java遍历文件夹及所有子文件
    关闭spring整合kafka时,消费者一直打印kafka日志
    (转)详解shell中>/dev/null 2>&1到底是什么
  • 原文地址:https://www.cnblogs.com/xiaochun126/p/5147052.html
Copyright © 2020-2023  润新知