由于 WSDL 中有多个同名类型,WCF 生成的代理抛出 InvalidOperationException [英] WCF generated proxy throws InvalidOperationException due to multiple types with same name in WSDL

查看:22
本文介绍了由于 WSDL 中有多个同名类型,WCF 生成的代理抛出 InvalidOperationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Visual Studio 2013 从这个 WSDL 文件生成 WCF 服务代理.但是,一旦我尝试调用 setSalesItemsV3 方法,WCF 就会从 System.Xml.dll 深处抛出一个 InvalidOperationException.

I'm using Visual Studio 2013 to generate a WCF service proxy from this WSDL file. However, as soon as I try to call the setSalesItemsV3 method, WCF throws an InvalidOperationException from deep in System.Xml.dll.

这个示例项目演示了这个问题:https://github.com/jennings/WsdlDuplicateNameProblem

This sample project demonstrates the problem: https://github.com/jennings/WsdlDuplicateNameProblem

这是内部异常:

消息: 来自命名空间 '' 的顶部 XML 元素 'start' 引用了不同的类型 WsdlDuplicateName.SalesItemService.hsSimpleDate 和 System.DateTime.使用 XML 属性为元素或类型指定另一个 XML 名称或命名空间.

Message: The top XML element 'start' from namespace '' references distinct types WsdlDuplicateName.SalesItemService.hsSimpleDate and System.DateTime. Use XML attributes to specify another XML name or namespace for the element or types.

我不是阅读 WSDL 的专家,但我看过它,唯一引用名称start"的部分是一些带有 元素的 >name="开始":

I'm no expert at reading WSDL, but I've looked at it and the only sections that reference the name "start" are a few <wsdl:part> elements with name="start":

<wsdl:message name="setSalesItems">
  <wsdl:part name="start" type="xsd:dateTime"></wsdl:part>
</wsdl:message>

<wsdl:message name="setSalesItemsV3">
  <wsdl:part name="start" type="tns:hsSimpleDate"></wsdl:part>
</wsdl:message>

但是,这些部分在完全不同的消息中,所以我不明白为什么应该有任何混淆.我已经通过几个在线 WSDL 验证器运行了 WSDL 文件,他们似乎对此没问题.

But, the parts are in completely different messages, so I don't see why there should be any confusion. I've run the WSDL file through several online WSDL validators and they seem to be okay with it.

以下是项目中重现问题所需的唯一代码(除了生成的代理).

Below is the only code in the project necessary to reproduce the problem (besides the generated proxy).

class Program
{
    static void Main(string[] args)
    {
        SalesServiceClient client = new SalesServiceClient();
        var date = ToSimpleDate(new DateTime());

        // throws InvalidOperationException
        // Message == "There was an error reflecting 'start'."
        client.setSalesItemsV3(1, 1, null, date, date);
    }

    static hsSimpleDate ToSimpleDate(DateTime time)
    {
        return new hsSimpleDate
        {
            year = time.Year,
            month = time.Month,
            day = time.Day,
        };
    }
}

推荐答案

为了演示问题,让我们看一下生成的 Reference.cs:

To demonstrate the problem let’s take a look into generated Reference.cs:

public partial class getSalesItemsV3 {
  // skipped
  [System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=2)]
  public WsdlDuplicateName.SalesItemService.hsSimpleDate start;  
  // skipped
}

public partial class setSalesItems {
  // skipped
  [System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=3)]
  public System.DateTime start;
  // skipped
}

请注意,这些元素具有相同的名称 (start) 和相同的命名空间,由 MessageBodyMember 属性声明("",空命名空间).这会导致来自命名空间 '' 的顶级 XML 元素 'start' 引用了不同的类型"序列化程序异常.

Please note that these elements have the same name (start) and the same namespace declared by the MessageBodyMember attribute ("", empty namespace). This cause "The top XML element 'start' from namespace '' references distinct types" serializer exception.

如果我们有这个选项:

(b) 我可以对生成的代理进行更改,以使序列化器快乐

(b) the changes I can make to the generated proxies to make the serializer happy

...我们可以手动为元素startendreturn(它们都会引起麻烦)设置命名空间.我自己做了,把结果放在 这里.您可以将其粘贴到您的 Reference.cs 中,序列化程序异常将消失.

... we can set namespaces for elements start, end and return (they all cause troubles) manually. I did it by myself and put the result here. You can paste it into your Reference.cs and serializer exception will gone.

但您的问题的根本原因似乎是该服务 (http://services.hotschedules.com/api/services/SalesService?wsdl) 旨在通过 WebServices 使用(这个问题是某种不兼容性).

But it seems that the root cause of your issue is that this service (http://services.hotschedules.com/api/services/SalesService?wsdl) is intended to be used through WebServices (and this problem is some kind of incompatibilities).

如果您添加对此服务器的引用作为 Web 引用(添加 -> 服务引用... -> 高级...-> 添加 Web 引用...) 并编写相同的 Web 方法调用,不会出现序列化问题.实际上,就我而言,我在我的测试示例中收到了另一种服务器异常,但它将解决您的直接序列化问题.

If you add reference to this server as a Web Reference (Add -> Service Reference... -> Advanced... -> Add Web Reference...) and write the same web method call, no problems with serialization will occur. Actually, in my case I received another kind of server exceptions in my test example, but it will solve your immediate serialization problem.

您的代码的镜像副本,但使用 Web 服务参考(并且不需要对生成的文件进行任何更改)可以在 此处.

The mirror copy of your code, but using Web Service Reference (and not requires any changes in generated files) can be found here.

希望这会有所帮助.

UPDATE:要找到导致此问题的实际原因,我们需要深入研究 XmlReflectionImporter 源代码.首先,我们的 WSDL 使用 XSD 模式来定义名称空间:http://www.w3.org/2001/XMLSchema 用于 xsdhttp://services.hotschedules.com/api/services/SalesService 用于 tns.XmlReflectionImporter 使用 NameTable(这是 Hashtable 的包装器)来存储访问器".Accessor 是一对NamespaceName.

UPDATE: To found what is actually cause this problem we need to deep delve in XmlReflectionImporter source code. First, our WSDL using XSD schemas to define namespaces: http://www.w3.org/2001/XMLSchema for xsd and http://services.hotschedules.com/api/services/SalesService for tns. XmlReflectionImporter using NameTable (this is a wrapper for Hashtable) to store "accessors". Accessor is a pair of Namespace and Name.

让我们看看引发异常的源代码:

Let's see source code that throws exception:

private Accessor ReconcileAccessor(Accessor accessor, NameTable accessors)
{
   // initial check skipped
   // look for accessor by name and namespace, add to accessors hash if not found and return
   Accessor accessor1 = (Accessor) accessors[accessor.Name, accessor.Namespace];
   if (accessor1 == null)
   {
        accessor.IsTopLevelInSchema = true;
        accessors.Add(accessor.Name, accessor.Namespace, (object) accessor);
        return accessor;
   }

   // accessor ("start" in our case) found!

   // check if mappings is the same and return accessor. This is not our case, we have two accessors with the same name but different mappings (despite that this mappings is have the same type)!
   if (accessor1.Mapping == accessor.Mapping)
     return accessor1;

    // next I skipped some  reconciliations for MembersMapping and ArrayMapping. Please note that it performed by types, for example:
    // if (accessor.Mapping is ArrayMapping) { /* some logic */}

   // Our mapping is not MembersMapping or ArrayMapping and we finally got there:      
   throw new InvalidOperationException(Res.GetString("XmlCannotReconcileAccessor", (object) accessor.Name, (object) accessor.Namespace, (object) XmlReflectionImporter.GetMappingName((Mapping) accessor1.Mapping), (object) XmlReflectionImporter.GetMappingName((Mapping) accessor.Mapping)));

   // Resource definition is: XmlCannotReconcileAccessor=The top XML element '{0}' from namespace '{1}' references distinct types {2} and {3}. Use XML attributes to specify another XML name or namespace for the element or types.
    // using this resource template you can see that string representations of mappings are "WsdlDuplicateName.SalesItemService.hsSimpleDate" and "System.DateTime".
}

所以,主要的协调逻辑是我们不能有两个名称相同但命名空间不同的访问器MembersMappingArrayMapping 类型可能有一些例外,但我们的情况并非如此.

So, the main reconciliation logic is we can't have two accessors with the same name but different namespaces! There're may be some exceptions for MembersMapping and ArrayMapping types, but it is not our case.

我相信这是某种错误.WSDL 是正确的并且会通过验证,但是由于来自 XmlReflectionImporter 类的 ReconcileAccessor 的这种通用实现,我们得到了一个异常.不确定这是否是 XmlReflectionImporter 的确切问题,或者可能是更高抽象层上的另一个问题.并且,Web Reference"生成的源代码没有使用 XmlReflectionImporter.

I believe that this is some kind of a bug. The WSDL is correct and will pass validation, but due to this generic implementation of ReconcileAccessor from XmlReflectionImporter class we got an exception. Not sure if this is exact problem of XmlReflectionImporter, or may be there's another problem on a higher abstract layer. And, source generated by "Web Reference" is not using XmlReflectionImporter.

还有一件事值得一提:生成器为 MessageBodyMemberAttribute 放置了一个 Namespace="" 值,这有效地中断了协调过程.所以,我认为存在一些不一致或不兼容的问题.

Another thing is worth to mention: generator puts a Namespace="" value for MessageBodyMemberAttribute, what is effectively break the reconciliation process. So, I believe there's some inconsistency or incompatibility.

这篇关于由于 WSDL 中有多个同名类型,WCF 生成的代理抛出 InvalidOperationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆