Lesson with ServiceContractAttribute.ConfigurationName in the proxy class

使用SvcUtil工具可以生成一个cs文件,用以提供访问WCF服务要用到的代理类和接口。

By utilizing SvcUtil tool, one can get the .cs file in which proxy classes/interfaces are presented to consume WCF Services.

如果这一cs文件无须经过人工编辑后续处理,那么客户端将来要用的.config配置也无须特殊处理——否则,情况将不一样。

If no manual modification is required to this .cs file, people need not to pay special attention to the .config at the client side -- but if manual modification is required, things get different.

最近,我被要求更新代理类的.cs文件,以反映出服务端的最新变化。然而,我遇到了问题。

I have a trouble recently when I am asked to update the proxy .cs file to reflect the latest change at the service side.

之前的代理类的.cs文件,既包括了SvcUtil工具的生成代码,也包含了手动编辑部分。比较明显的证据是,同样一个命名空间声明语句,重复了很多次。我肯定之前的编码者是先生成了好几段代码再把它们拼接到了一起。

The original one is the mixture of the result of executing SvcUtil tool and manual modification: the same namespace statement repeats here and there -- I am sure someone generated several files then simply placed the content together into this one.

除了这一“拼接”工作,编码者还进行了其他手动编辑——我非常肯定这一点,等会相关证据会展示给大家——这一手动编辑,正是我后来遇到的问题的根源。

Moreover, that guy did some manual modification -- believe me, I will show you the evidance. And that DOES lead to my problem!

之前的代理类代码文件,数据契约类位于统一的一个命名空间内,服务契约类/接口则没有自己的命名空间,直接位于最上层。

Let‘s go back to the original .cs file, we can see the data contract classes are given an uniform namespace, while the service contract classes/interfaces do not have any namespace but are exposed to the root.

为了最小化工作量,我决定维持这一层次结构安排。为了避免将来维护时不必要的手工劳动,我写了一个批处理,通过一次调用SvcUtil来生成最终的代理文件。

To minimize the possible change, I decided to keep such a structure. And, to avoid unnecessary manual work in the future maintainance, I wrote a .bat file in which a single call to SvcUtil is performed to generate the final proxy .cs file.

幸运的是,服务端发布的数据契约和服务契约,各自位于不同的命名空间下。所以我可以在SvcUtil中利用多个/namespace选项[1]来生成如此层次结构要求的代码:

Fortunately, the data contracts and the service contracts are published with different namespaces, so I can use multiple /namespace options in executing SvcUtil to present exactly the same structure[1]:
        SvcUtil ... /namespace:*,DataContractNameSpaceAtClientSide /namespace:ServiceContractNameSpaceAtServiceSide, ...

我执行了此批处理并提交了新的代理文件。

I excuted the .bat file and committed the new proxy file.

似乎一切正常。然而测试人员告诉我,和代理相关的功能彻底不能用了。错误消息说:

Every thing is OK, right? But NOT -- the tester told me that the function involved with the proxy crashed, with error message saying
    InvalidOperationException - Could not find default endpoint element that references contract ‘IService‘ in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element. ...

我的变更有什么问题?我记得完全没有改过config文件啊。

What‘s wrong with my change? I did not do any modification towards the .config file.

我不得不回头仔细比较之前的代码和我生成的新代理文件,最后发现主要的不同点位于服务契约接口上的System.ServiceModel.ServiceContract属性的ConfigurationName值。

I have to turn back to compare the original proxy file and the new one, at last the main difference is addressed at the value of System.ServiceModel.ServiceContractAttribute.ConfigurationName declared for the service contract interface.

原始文件给的是

The original one is like

[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "ClientProxies.IService")]
public interface IService
{
   // Something declared here.
}

而我的是

while mine is like

[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IService")]
public interface IService
{
    // Something declared here.
}

根据[2]的讨论,SvcUtil工具在生成代码时,System.ServiceModel.ServiceContract属性的ConfigurationName值永远等于其所装饰的类/接口的完整类名。在服务契约接口没有上层命名空间的情况下,我的代码是SvcUtil工具的原生结果,相反地,原始代码的方式明显是人工干预的结果。

According to the discussion in [2], when utilizing SvcUtil tool, the service contracts in the proxy file, will always take the classes/interfaces full type name as the value of System.ServiceModel.ServiceContractAttribute.ConfigurationName. As the final proxy interface does not have any super namespace, mine code is the nature result of utlizing SvcUtil tool -- and the original one must have been experienced certain manual modification else its ConfigurationName cannot look like that.

这一差异导致了相关功能崩溃的问题吗?

But may such a difference lead to my trouble?

答案是"是"。

The answer is YES.

在config文件中有如下声明:

Let‘s see what is configured in the .config file:

<system.serviceModel>
  <client>
    <endpoint contract="ClientProxies.IService" name="IService" />
  </client>
</system.serviceModel>

根据[3]

According to [3]

…这个endpoint的contract属性声明了它所服务的契约对象。该值与契约对象的ServiceContract属性的ConfigurationName值相对应——若此ConfigurationName

未提供,则默认取契约对象的完整类名。…

... The contract attribute specifies which contract the endpoint is exposing. This value maps to the ConfigurationName of the ServiceContractAttribute.
   The default value is the full type name of the class that implements the service. ...

所以即使没有所谓的ClientProxies.IService存在,系统仍然能够通过将契约对象的ServiceContract属性的ConfigurationName设置成该值来实现定位。这就是为什么原来的代理能工作而我的不能的原因。
although there is no such a ClientProxies.IService type but only IService declared, the application will also be able to make use of it as long as it is decorated with the System.ServiceModel.ServiceContractAttribute of ConfigurationName = "ClientProxies.IService". This is why the original proxy worked.

真惭愧,和WCF打交道差不多十年了,却到今天才了解这点。

Shame me, after nearly ten years working with WCF, only till today I know the connection between the system.serviceModel\client\[email protected] and System.ServiceModel.ServiceContractAttribute.ConfigurationName!

[1] http://stackoverflow.com/questions/1103686/use-svcutil-to-map-multiple-namespaces-for-generating-wcf-service-proxies
[2] https://social.msdn.microsoft.com/Forums/vstudio/en-US/14ff3b6e-22a9-4a4a-b12f-1cffc8ddf6da/svcutilexe-issue-with-configuration-name?forum=wcf
[3] https://msdn.microsoft.com/en-us/library/ms731745(v=vs.110).aspx

时间: 2024-10-14 13:53:55

Lesson with ServiceContractAttribute.ConfigurationName in the proxy class的相关文章

跟我一起学WCF(6)——深入解析服务契约[下篇]

一.引言 在上一篇博文中,我们分析了如何在WCF中实现操作重载,其主要实现要点是服务端通过ServiceContract的Name属性来为操作定义一个别名来使操作名不一样,而在客户端是通过重写客户端代理类的方式来实现的.在这篇博文中将分享契约继承的实现. 二.WCF服务契约继承实现的限制 首先,介绍下WCF中传统实现契约继承的一个方式,下面通过一个简单的WCF应用来看看不做任何修改的情况下是如何实现契约继承的.我们还是按照之前的步骤来实现下这个WCF应用程序. 步骤一:实现WCF服务 在这里,我

WCF初探-10:WCF客户端调用服务

创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: 服务和客户端使用托管属性.接口和方法对协定进行建模. 若要连接客户端应用程序中的服务,则需要获取该服务协定的类型信息.通常,我们使用Svcutil.exe(ServiceModel Metadata Utility Tool)来完成,也可以直接在客户端项目上引用服务地址完成.它们会从服务中下载元数据

WCF初探-11:WCF客户端异步调用服务

前言: 在上一篇WCF初探-10:WCF客户端调用服务 中,我详细介绍了WCF客户端调用服务的方法,但是,这些操作都是同步进行的.有时我们需要长时间处理应用程序并得到返回结果,但又不想影响程序后面代码部分的执行,这时我们就需要考虑使用异步的方式来调用服务.注意这里的异步是完全针对客户端而言的,与WCF服务契约的方法是否异步无关,也就是在不改变操作契约的情况下,我们可以用同步或者异步的方式调用WCF服务. WCF客户端异步调用服务方式: 通过代理类异步调用服务.就需要通过使用事件驱动的异步调用模型

WCF技术剖析之十一:异步操作在WCF中的应用(下篇)

原文:WCF技术剖析之十一:异步操作在WCF中的应用(下篇) 说完了客户端的异步服务调用(参阅WCF技术剖析之十一:异步操作在WCF中的应用(上篇)),我们在来谈谈服务端如何通过异步的方式为服务提供实现.在定义服务契约的时候,相信大家已经注意到了OperationContractAttribute特性具有一个bool类型的AsynPattern.该属性可以将一个服务操作定义成异步实现模式,接下来的内容主要是着眼于介绍异步操作的定义和实现原理. 一.异步操作的定义和实现原理 实现WCF异步服务操作

WCF系列之双工通信

WCF双工通信允许客户端调用服务器端,也允许通过回调,实现服务器端调用客户端,并不是所有的协议都支持双工通信,比如HTTP协议是不支持双工通信的. 我们来看一下契约的定义,其中在ServiceContract指定了CallbackContract,定义了ICalculateCallback,回调契约不需要指定为ServiceContract,但是方法要标记为OperationContract,可以看到服务契约和回调的方法均指定为IsOneWay=true,返回值都是void. 下一步,再来看一下

WCF系列之承载(IIS-HTTP)

IIS承载 部署到IIS的话,需要激活WCF Http Activation功能组件. 下面我们来看一个简单的例子,我们先来看一下项目的目录结构 该例子实现传入两个整型的参数,返回它们的和.(截图是Surface pro4截出来的,可能字体有点大) 定义契约: 实现服务: 再来看一下服务的配置: 到目前为止,那么服务的定义已经实现了,下面再来看一下怎么在IIS中承载. 首先,我们把项目编译,拷贝bin的目录和svc和配置文件到一个文件夹下. 在IIS中创建一个网站,目录映射到上面的目录中,启动网

WCF简介-01

WCF Windows Communication Foundation 1.1 新建一个"空白解决方案" 1.2 在解决方案中添加类库IBLL 1.2.1 添加接口IUserInfoService using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; na

[老老实实学WCF] 第四篇 初探通信--ChannelFactory

原文:[老老实实学WCF] 第四篇 初探通信--ChannelFactory 老老实实学WCF 第四篇 初探通信--ChannelFactory 通过前几篇的学习,我们简单了解了WCF的服务端-客户端模型,可以建立一个简单的WCF通信程序,并且可以把我们的服务寄宿在IIS中了.我们不禁感叹WCF模型的简单,寥寥数行代码和配置,就可以把通信建立起来.然而,仔细品味一下,这里面仍有许多疑点:服务器是如何建起服务的?我们在客户端调用一个操作后发生了什么?元数据到底是什么东西?等等.我们现在对WCF的理

WCF初探-16:WCF数据协定之基础知识

数据协定概念 “数据协定”是在服务与客户端之间达成的正式协议,用于以抽象方式描述要交换的数据. 也就是说,为了进行通信,客户端和服务不必共享相同的类型,而只需共享相同的数据协定. 数据协定为每一个做数据交换时需要被序列化的参数或者返回值做了精确定义. 数据协定特点 默认情况下, WCF使用称为数据协定序列化程序的序列化引擎对数据进行序列化和反序列化(与 XML 进行相互转换). 所有 .NET Framework 基元类型(如整型和字符串型)以及某些被视为基元的类型(如 DateTime 和 X