• WCF 服务中元数据的地址问题


    初用WCF的朋友可能会遇到这样的问题,就是在使用svcutil.exe生成proxy和config的时候,或者利用add service reference添加引用的时候,部署的WCF服务到底它的metadata是什么。或者换句话说,svcutil的URL参数,以及添加服务引用时候的那个Address,到底应该填什么。

    在这里我用两个最常用的Binding方式,WSHttpBinding和NetTcpBinding,分别以实际的例子来进行说明。

    建立服务契约

    在这里就从MSDN上抄一个四则运算的服务来作为我们的素材。建立一个控制台程序,添加System.ServiceModel引用,然后添加下面两个文件:


    ICalculatorService.cs :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    namespace Nocturne.Learning.WcfAddressDemo
    {
        [ServiceContract]
        
    public interface ICalculatorService
        
    {
            [OperationContract]
            
    double Add(double n1, double n2);
            [OperationContract]
            
    double Subtract(double n1, double n2);
            [OperationContract]
            
    double Multiply(double n1, double n2);
            [OperationContract]
            
    double Divide(double n1, double n2);

        }

    }

    CalculatorService.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    namespace Nocturne.Learning.WcfAddressDemo
    {
        
    public class CalculatorService : ICalculatorService
        
    {
            
    public double Add(double n1, double n2)
            
    {
                
    double result = n1 + n2;
                Console.WriteLine(
    "Received Add({0},{1})", n1, n2);
                Console.WriteLine(
    "Return: {0}", result);
                
    return result;
            }


            
    public double Subtract(double n1, double n2)
            
    {
                
    double result = n1 - n2;
                Console.WriteLine(
    "Received Subtract({0},{1})", n1, n2);
                Console.WriteLine(
    "Return: {0}", result);
                
    return result;
            }


            
    public double Multiply(double n1, double n2)
            
    {
                
    double result = n1 * n2;
                Console.WriteLine(
    "Received Multiply({0},{1})", n1, n2);
                Console.WriteLine(
    "Return: {0}", result);
                
    return result;
            }


            
    public double Divide(double n1, double n2)
            
    {
                
    double result = n1 / n2;
                Console.WriteLine(
    "Received Divide({0},{1})", n1, n2);
                Console.WriteLine(
    "Return: {0}", result);
                
    return result;
            }

        }

    }

    下面我们就分别来看看怎样HOST这个服务,怎样获取它的元数据。

    WSHttpBind方式

    这种方式下,元数据可以直接从http地址中获得。先看看下面这段启动服务的代码(注意,我只使用code方式启动,如果存在app.config,请将其删除)。


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

    namespace Nocturne.Learning.WcfAddressDemo
    {
        
    class Program
        
    {
            
    static void Main(string[] args)
            
    {
                
    Uri baseAddress=new Uri("http://localhost:8009/MyService");
                ServiceHost host 
    = new ServiceHost(typeof(CalculatorService), baseAddress);
                
                ServiceMetadataBehavior smb 
    = new ServiceMetadataBehavior()
                
    {
                    HttpGetEnabled 
    = true
                }
    ;
                
                host.Description.Behaviors.Add(smb);

                host.AddServiceEndpoint(
    typeof(ICalculatorService), new WSHttpBinding(), baseAddress);

                host.Open();

                Console.WriteLine(
    "The service is ready.");
                Console.WriteLine(
    "Press <ENTER> to terminate service.");
                Console.WriteLine();
                Console.ReadLine();

            }

        }

    }

    启动该工程,这时候就可以访问这个服务了。注意这里的baseAddress,决定了该host是采用基地址访问,而这个“访问”,仅仅是针对endpoint的位置来说的,理解这个概念非常重要。如果我们在将host.AddServiceEndpoint()的最后一个参数改为一个string,比如"CalculatorService“,影响的只是该服务的endpoint地址,对元数据没有影响。

    这个时候,我们就可以通过svcutil来生成辅助文件了,命令如下:


    D:\Program\temp>svcutil http://localhost:8009/MyService /language:cs /out:Proxy.cs /config:app.config
    Microsoft (R) Service Model Metadata Tool
    [Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
    Copyright (c) Microsoft Corporation.  All rights reserved.

    Attempting to download metadata from 'http://localhost:8009/MyService' using WS-Metadata Exchange or DISCO.
    Generating files...
    D:\Program\temp\Proxy.cs
    D:\Program\temp\app.config


    其中的URL参数,就是代码中写到的baseAddress,在后面添加endpoint的时候不管最后那个参数写的是啥,这个命令都这么写,因为metadata是属于一个host的,并不属于一个endpoint。

    如果是通过给工程添加Service Reference,也是在Address里填入这个baseAddress。如图所示:



    之后该怎么玩就悉听尊便了。

    NetTcpBinding方式

    由于在这种方式下,服务本身是通过NetTcp方式来与客户端应答的,元数据就得另开一个mex的endpoint,来专门提供。看下面的代码:


    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;

    namespace Nocturne.Learning.WcfAddressDemo
    {
        
    class Program
        
    {
            
    static void Main(string[] args)
            
    {
                Uri baseAddress 
    = new Uri("net.tcp://localhost:8009/MyService");
                ServiceHost host 
    = new ServiceHost(typeof(CalculatorService), baseAddress);

                ServiceMetadataBehavior smb 
    = new ServiceMetadataBehavior();

                host.Description.Behaviors.Add(smb);

                host.AddServiceEndpoint(
    typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
                host.AddServiceEndpoint(
    typeof(ICalculatorService), new NetTcpBinding(), baseAddress);

                host.Open();

                Console.WriteLine(
    "The service is ready.");
                Console.WriteLine(
    "Press <ENTER> to terminate service.");
                Console.WriteLine();
                Console.ReadLine();
            }

        }

    }

    与前面建立WSHttpBinding的代码有两个不同的地方。首先是smb里取消了HttpGetEnabled=true的属性设置,这是由于我们的基地址不是HTTP地址,会引发异常,错误信息是:The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address.  Either supply an http base address or set HttpGetUrl to an absolute address。第二点就是最关键的元数据地址,添加了一个MexTcpBinding绑定类型的endpoint,它就承担为整个service对外提供元数据的任务。元数据的地址就是在基址后面加了“mex”的形式,在这里就是net.tcp://localhost:8009/MyService/mex。在实际应用中,这个“mex”可以省略,即只用net.tcp://localhost:8009/MyService,系统会自动去寻找mex,而如果在前面建立endpoint的时候,用的是其它的名称(比如mex1),那就不能省略了。建议使用完整路径。


    D:\Program\temp>svcutil net.tcp://localhost:8009/MyService/mex /language:cs /out:Proxy.cs /config:app.config
    Microsoft (R) Service Model Metadata Tool
    [Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
    Copyright (c) Microsoft Corporation.  All rights reserved.

    Attempting to download metadata from 'net.tcp://localhost:8009/MyService' using WS-Metadata Exchange. This URL does not support DISCO.
    Generating files...
    D:\Program\temp\Proxy.cs
    D:\Program\temp\app.config


    Add Service Reference的操作类同。

    小结

    Address和元数据,是部署WCF服务时最最基本的概念,有必要非常熟练地掌握,笔者也只是对常用的两种绑定方式做了一个讨论,更多的内容还有待发掘。

  • 相关阅读:
    算法-对分查找(二分查找)C++实现
    Android Studio简单设置
    一起talk C栗子吧(第八回:C语言实例--素数)
    Maven生命周期
    java8_api_日期时间
    UITableviewcell的性能问题
    iOS开发25个性能调优技巧
    iOS9新特性
    iOS9 3DTouch、ShortcutItem、Peek And Pop技术一览
    iOS网络访问之使用AFNetworking
  • 原文地址:https://www.cnblogs.com/SpadeQ/p/1286197.html
Copyright © 2020-2023  润新知