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服务时最最基本的概念,有必要非常熟练地掌握,笔者也只是对常用的两种绑定方式做了一个讨论,更多的内容还有待发掘。

posted @ 2008-09-07 13:03  Jacky Zhou  阅读(2214)  评论(3编辑  收藏  举报