WCF服务引用的命名空间与原始命名空间不同

13
我遇到了一个关于服务引用中命名空间的问题。我有许多WCF服务,例如命名空间为MyCompany.Services.MyProduct(实际命名空间更长)。
作为产品的一部分,我还提供了一个示例C#.NET网站。这个Web应用程序使用命名空间MyCompany.MyProduct
在最初的开发过程中,服务被添加为项目引用并直接使用。我使用了一个工厂模式,返回实现MyCompany.Services.MyProduct.IMyService的对象实例。到目前为止,一切都很好。
现在我想改成使用实际的服务引用。在添加引用并在命名空间文本框中键入MyCompany.Services.MyProduct后,它在命名空间中生成类MyCompany.MyProduct.MyCompany.Services.MyProduct。不好!我不想因为使用代理类而不得不更改几个地方的using指令。所以我尝试在命名空间前加上global::,但是不被接受。
请注意,我甚至没有删除原始程序集引用,而且“重用类型”已启用,但显然没有重用。但是,无论如何,我都不想在我的示例网站中保留程序集引用才能使其正常工作。
到目前为止,我想到的唯一解决方案是将我的 Web 应用程序的默认命名空间设置为 MyCompany(因为它不能为空),并将服务引用添加为 Services.MyProduct。假设客户想要使用我的示例网站作为起点,并将默认命名空间更改为 OtherCompany.Whatever,这显然会破坏我的解决方法。这个问题有好的解决方案吗? 总结一下:我想在原始命名空间中生成服务引用代理,而不引用程序集。
注意:我看过这个问题,但没有提供适用于我的用例的解决方案。

编辑:正如John Saunders所建议的那样,我已经向Microsoft提交了一些关于此事的反馈:
在Microsoft Connect上的反馈项目

3个回答

23

我在我的博客中添加了一篇关于这个解决方案的说明。同样的信息,但或许更加完整。

我找到了一种替代使用svcutil.exe实现我想要的目标的方法。它(在我看来)比重新运行该工具更容易更新服务引用。

您应该在ServiceContract和DataContracts上明确指定命名空间URI(请参见下面的注释)。

[ServiceContract(Namespace = "http://company.com/MyCompany.Services.MyProduct")]
public interface IService
{
    [OperationContract]
    CompositeType GetData();
}

[DataContract(Namespace = "http://company.com/MyCompany.Services.MyProduct")]
public class CompositeType
{
    // Whatever
}

命名空间可以是任何东西,但从技术上讲它需要是一个有效的URI,所以我选择了这个方案。后续您可能需要手动构建一些内容才能使其正常工作,所以请确保完成这个步骤。

完成上述步骤后,在解决方案资源管理器中启用显示所有文件选项。展开您之前添加的服务引用。双击Reference.svcmap文件。

您将看到一个<NamespaceMappings />元素,您需要对其进行编辑。继续使用我的示例:

<NamespaceMappings>
    <NamespaceMapping
        TargetNamespace="http://company.com/MyCompany.Services.MyProduct"
        ClrNamespace="MyCompany.Services.MyProduct" />
</NamespaceMappings>

保存文件后,右键点击服务引用并选择更新服务引用

你可以添加所需的任意数量的映射(我实际上需要两个)。效果与svcutil /namespace:方法相同,但无需使用命令行工具本身,使得更新更加容易。

与svcutil的区别

采用这种方法的缺点是需要使用显式的命名空间映射。使用svcutil,你可以选择像这样映射未明确映射的所有内容(John Saunders提到的解决方案):

svcutil /namespace:*,MyCompany.Services.MyProduct ...

你可能会想使用:

<NamespaceMappings>
    <NamespaceMapping
        TargetNamespace="*"
        ClrNamespace="MyCompany.Services.MyProduct" />
</NamespaceMappings>

但这并不起作用,因为Visual Studio已经隐式地添加了此映射,指向我们试图摆脱的生成命名空间名称。上述配置将导致Visual Studio抱怨重复的键。

添加显式名称空间: 当您的代码中没有明确指定名称空间时,.NET似乎会生成形如http://schemas.datacontract.org/2004/07/MyCompany.Services.MyProduct的uri。你可以把它映射成我示例中的显式名称空间一样好,但我不知道是否有任何保证这种行为。因此,采用显式名称空间可能更好。

NB:将两个TargetNamespaces映射到相同的ClrNamespace似乎会破坏代码生成


感谢您提供这么详细的解释。我需要控制生成对象的命名空间映射,以便将它们与部分类(在服务器和Silverlight客户端之间链接的文件)匹配。这样,我就可以在服务器和客户端上拥有带有所有修饰的服务生成对象以及共享类方法和属性。 - Jersey Dude
2
在遇到这个错误之后,我发现在看到 Stack Overflow 上的帖子之前,这就是答案。但我必须说,这绝对是荒谬的,需要这样做。WCF 应该自动完成这项工作,没有任何有效的理由要求每个服务用户都手动编辑他们的客户端文件才能与服务通信。尽管我很喜欢 .NET 这样的东西,但这种情况让我对微软感到非常生气。 - Chris Marisic
@Chris:我不认为这是一个错误。你可以很好地与服务进行交流,而无需进行任何手动编辑。编辑只是为了使事情符合一些个人偏好。尽管如此,这一切仍然相当不方便。 - Thorarin
1
你是什么意思,这不是错误?如果不这样做,项目甚至无法更新其服务引用。 - Chris Marisic
@Thorarin 很抱歉在5年后又提起这个问题。这个解决方法很好用。但是,有一件事情让我感到困扰。在自动生成的代码中,它仍然生成旧的命名空间,其中不包含任何代码。当你写这个解决方法的时候,你是否遇到过这个问题? - LxL

1

你的使用情况是错误的。

你一开始就不应该将服务包含为参考。

我相信svcutil.exe会接受一个指定要使用的完整命名空间的开关。


2
我完全不同意。服务就是服务。它生成程序集的事实并不能成为引用该程序集的借口。即使是为了单元测试,也不应该引用那个程序集。你应该像这个服务是用Java或其他非.NET语言编写的一样行事,否则你永远不会考虑引用该程序集。 - John Saunders
2
引用一个 100% 的 ServiceContract 和 DataContract 程序集有什么问题(当然没有实现)。这有什么区别,特别是如果您拥有连接的两侧。我不会说这是绝对不好的,尤其是在项目开始时进行了大量重构工作时,它可以提高生产力。在这些情况下,我根本不创建服务引用。 - Anderson Imes
1
如果只是合同,我对此没有问题,特别是如果它们只是合同,没有任何行为。我所关注的问题是他似乎在引用整个服务,包括其行为。 - John Saunders
他:http://stackoverflow.com/users/111335/pavel-minaev :) 他在某处发布了他正在使用VS2010(并且在他的个人资料中也这样写着)。 - Thorarin
我喜欢你的方法,John Saunders。点个赞! - Andriy Drozdyuk
显示剩余4条评论

0
在VS2010及更高版本中,有一种设置自定义命名空间的方法。 在解决方案资源管理器中,选择“显示所有文件”,然后在解决方案树中打开“Web引用”,选择服务,选择Reference.map节点,显示属性并设置Custom Tool Namespace属性。
不幸的是,我没有足够的声望来展示截图。

我不知道这个设置,但当我更改它并更新服务引用时,它根本不会更改所使用的命名空间。我有什么遗漏吗? - Thorarin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接