65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF 代理生成选项

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.57/5 (5投票s)

2009年2月11日

CPOL

7分钟阅读

viewsIcon

132271

downloadIcon

3667

概述了多种生成 WCF 服务代理的方法。

引言

本项目旨在演示客户端通过代理使用 WCF 服务的三种不同方式。即:

  • VS 生成的代理
  • svcutil 代理
  • 手动代理

我假设您已经设置了至少一个测试 WCF 服务,并且您了解其基本原理。

背景

在我工作的公司,我们使用 WCF 已经一年多了,我们发现其中一件事是系统生成的代理代码经常非常糟糕,有时无法构建,而且它生成的配置文件也非常糟糕。我们最终采取了类似于我下面介绍的第三种方案。

基本项目

为了创建一个简单易用的 WCF 服务来演示生成代理的不同方式,我使用了 VS 2008。在 VS 2008 中,有一个选项可以创建类型为“WCF 服务库”的新项目,它几乎生成了我需要的一切。如果不是使用 VS 2008,只需将以下代码复制到一个新项目中并解决引用,您就拥有了一个(非常)基本的 WCF 服务。删除大部分无关内容,只保留一个方法 HelloWorld(),我们得到以下服务和配置文件:

服务实现

public class ProxyServ : IProxyServ
{
    public void HelloWorld() { }
}

服务合同

[ServiceContract]
public interface IProxyServ
{
    [OperationContract]
    void HelloWorld();
}

配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="ProxyGeneration.ProxyServ" 
            behaviorConfiguration="ProxyGeneration.Service1Behavior">
        <endpoint address ="" binding="wsHttpBinding" 
            contract="ProxyGeneration.IProxyServ"/>
        <endpoint address="mex" binding="mexHttpBinding" 
            contract="ProxyGeneration.IProxyServ"/>
        <host>
          <baseAddresses>
            <add baseAddress="https://:8080/ProxyServ"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ProxyGeneration.Service1Behavior">
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

您可以在 VS 2008 中直接运行它,它会即时为您生成一个主机并允许您使用它,但我更喜欢自己创建主机。

宿主

我在解决方案中添加了一个控制台项目,并将以下代码片段粘贴到其中。我总是使用相同的代码片段,并对其进行调整以使其适用于每个解决方案。

static void Main(string[] args)
{
    ServiceProc();
}

private static void ServiceProc()
{
    ServiceHost host = new ServiceHost(typeof(ProxyServ));
    try
    {
        Console.WriteLine("WCF Service: Starting Up");
        host.Opened += new EventHandler(host_Opened);
        host.Open();
        Console.WriteLine("Please press any key to close...");
        Console.ReadLine();
    }
    finally
    {
        if (host.State != System.ServiceModel.CommunicationState.Closed)
        {
            host.Close();
        }
    }
}

private static void host_Opened(object sender, EventArgs e)
{
    Console.WriteLine("WCF Service: Running");

    System.ServiceModel.ServiceHost host = (System.ServiceModel.ServiceHost)sender;
    StringBuilder detailBuilder = new StringBuilder();
    foreach (Uri baseAddress in host.BaseAddresses)
    {
        detailBuilder.AppendFormat("\r\nBase Address: {0}", baseAddress.ToString());
    }

    foreach (ServiceEndpoint endPoint in host.Description.Endpoints)
    {
        string address = endPoint.Address.ToString();
        string bindingName = endPoint.Binding.Name;
        detailBuilder.AppendFormat("\r\nEndpoint({0}): {1}", bindingName, address);
    }

    Console.WriteLine(detailBuilder.ToString());
}

解决引用,然后将 app.config 从服务移动到主机。现在,我们可以运行控制台主机,它将为我们托管 WCF 服务,这意味着我们不再需要从 VS 2008 运行服务,我们只需运行控制台应用程序的 exe 文件。

客户端

我将为每种类型的代理创建一个客户端项目。我正在使用 Windows Forms 客户端。您实际上可以使用任何您想要的东西。此外,您会注意到 HelloWorld() 方法不返回任何内容,这没关系。只要它不抛出错误,我们就知道操作成功了。

生成代理

添加服务引用

在 VS 2005 中,您可以运行服务主机,并且仍然可以将服务引用添加到解决方案中的另一个项目。令人恼火的是,我的 VS 2008 版本似乎不起作用。因此,您必须转到控制台主机的 bin 目录并从那里运行它。一旦它运行起来,您就可以在客户端中添加服务引用。VS 2008 比 VS 2005 具有一个更漂亮的表单,它允许您预览服务及其方法,并为其命名。

ServRef.JPG

我**总是**尝试将代理命名为与服务不同的名称,这有助于我在脑海中将两者分开。无论如何,单击“确定”,您将得到一个名为“*服务引用*”的文件夹和一个小小的万维网图标。您可以通过 Windows 资源管理器深入查看 VS 2008 隐藏的实际代码文件,您会找到一个名为“*Reference.cs*”的文件。*Reference.cs* 是您刚刚创建的实际代理,但如果您只想让服务正常工作,请不要担心,这并不重要。使用它非常简单。这些代码行运行得很好:

private void btnTest_Click(object sender, EventArgs e)
{
    ProxyServClient client = new ProxyServClient("WSHttpBinding_IProxyServ");
    client.HelloWorld();
}

据我所知,通过 VS 2008 添加服务引用所做的只是将 svcutil 的命令行功能包装在一个设置各种参数的表单后面。它很漂亮,但在 app.config 和代理中生成了大量的额外内容(svcutil 也是如此)。

svcutil

我只将此方法包含在内以求完整性,VS 2005 有扩展允许您添加服务引用,并且它随 VS 2008 本身提供。

编辑:好的,Chris Richner 在评论中指出,并非所有人都能访问 VS 的专业版,并且会使用 VS 的 Express 版本。这些版本几乎保证您会使用 svcutil

svcutil 本身用起来真是噩梦。好吧,其实也不是,但如果你喜欢 GUI 并且讨厌命令行,那就跳到下一节。但是,如果你真的想知道如何使用它,请继续阅读。

我已将客户端项目重命名为 ReferenceClient,并创建了一个名为 svcutilClient 的类似项目。要访问 svcutil,请打开 VS 命令提示符,键入“svcutil”,然后按 Enter 键,您将看到一页又一页的开关和参数。它们非常多,我不会一一介绍。请查看 MSDN 文章此处,假设您能成功加载该网站。运行以下命令(在运行主机之后):

svcutil.exe "https://:8080/ProxyServ"
/out:"C:\Projects\Testing\ProxyGeneration\svcutilClient\ProxySVC"
/config:"C:\Projects\Testing\ProxyGeneration\svcutilClient\app.config"

我告诉工具要查看哪个服务(您也可以使用主机 exe 和配置来生成此服务,但这种方式键入更少),我希望生成的代理放在哪里,以及如何命名它,配置也是如此。(奇怪的是:默认的配置名称是“output.config”,这很糟糕,因为为您生成的代理显然只能“看到”名为“app.config”的配置。或者至少,我遇到了这种情况。)

您应该会看到一些输出,类似于这样:

Microsoft (R) Service Model Metadata Tool

[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.648]

Copyright (c) Microsoft Corporation. All rights reserved.



Attempting to download metadata from 'https://:8080/ProxyServ' using WS-

Metadata Exchange or DISCO.

Generating files...

C:\Projects\Testing\ProxyGeneration\svcutilClient\ProxySVC.cs

C:\Projects\Testing\ProxyGeneration\svcutilClient\app.config

如果您看到这个,那太棒了!现在,我们转到 VS,为生成的 CS 文件和配置“添加现有项”。和之前在我们漂亮的表单中一样,使用相同的测试代码。

private void btnTest_Click(object sender, EventArgs e)
{
    ProxyServClient client = new ProxyServClient("WSHttpBinding_IProxyServ");
    client.HelloWorld();
}

构建它,我们就完成了!

手工代理

好的,现在是选项 3。手动完成。

是的,它确实比使用服务引用甚至 svcutil 方法花费的时间要长得多;但是,您最终得到的代码更简洁,并且配置**大大**更清晰易读,您很快就会看到。这部分比它本来可以的要长一些,我添加了一个额外的项目,名为 Proxy,它编译成一个 DLL,然后我从另一个名为 ProxyClient 的新类中引用它。这对于这个项目来说并非严格必要,但它展示了这种方法的扩展性。

这个想法是展示在构建 WCF 服务时,您可以将代理作为一个项目构建在服务中(将代理保留在服务中有助于良好的维护),然后将代理作为 DLL 引用到使用它的项目中。配置也经过了大量修改,只包含我需要运行它的内容,这使得它看起来更容易理解。

所以,首先,我创建了一个名为 Proxy 的项目。它有一个接口和一个类。接口 IBob 是服务接口中定义的方法的子集。这允许您拥有一个**只**包含您将要使用的方法的代理,而不是全部,无论您是否需要它们。如果您的服务非常大,那么多个代理允许您分发所需的功能。然后,类 ProxyServiceClient 继承自 ClientBase<IProxyServ> 和我们的自定义接口。我们最终得到一个如下所示的接口(我将其命名为 IBob 以便*真正*清楚哪个是哪个接口):

public interface IBob
{
    void HelloWorld();
}

代理本身看起来像这样:

public class ProxyServiceClient : ClientBase<IProxyServ>, IBob
{
    public ProxyServiceClient()
        : base()
    { }

    public ProxyServiceClient(string endpoint)
        : base(endpoint)
    { }

    public void HelloWorld()
    {
        try
        {
            base.Channel.HelloWorld();
        }
        catch (Exception exc)
        {
            //FANTASTIC error handling here. No, really. It must be awesome.
            throw new ApplicationException(exc.Message, exc);
        }
    }
}

如果您查看了生成的代理,您会注意到它非常相似,只是稍微精简了一些。我没有使用任何属性,并且只使用了四个可能的构造函数中的两个。

配置文件在客户端,如下所示:

<configuration>
    <system.serviceModel>
      <client>
            <endpoint address="https://:8080/ProxyServ" 
                binding="wsHttpBinding" 
                contract="ProxyGeneration.IProxyServ"
                name="ProxyServHTTP"/>
        </client>
    </system.serviceModel>
</configuration>

您会注意到**只有** ABC,没有其他内容。一切都是默认值,所以我们不需要明确提及它。然后我们添加对代理的引用,并像上面一样创建一个测试按钮。

就是这样。祝您编码愉快,易于维护!

© . All rights reserved.