由于WSDL中具有相同名称的多个类型,WCF生成的代理会抛出InvalidOperationException

Ste*_*ngs 8 wcf wsdl proxy-classes svcutil.exe

我正在使用Visual Studio 2013从此WSDL文件生成WCF服务代理.但是,只要我尝试调用该setSalesItemsV3方法,WCF就会InvalidOperationException从中深入抛出System.Xml.dll.

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

这是内在的例外:

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

我不是读WSDL的专家,但是我已经看了它,并且引用名称"start"的唯一部分是以下几个<wsdl:part>元素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>
Run Code Online (Sandbox Code Playgroud)

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

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

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,
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

kny*_*nyu 9

为了演示这个问题,我们来看一下生成的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
}
Run Code Online (Sandbox Code Playgroud)

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

如果我们有这个选项:

(b)我可以对生成的代理进行的更改,以使序列化程序满意

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

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

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

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

希望这会有所帮助.

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

让我们看看抛出异常的源代码:

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".
}
Run Code Online (Sandbox Code Playgroud)

因此,主要的协调逻辑是我们不能拥有两个具有相同名称但名称空间不同的访问者!可能有一些例外 MembersMappingArrayMapping类型,但它不是我们的情况.

我相信这是某种错误.该WSDL是正确的,并通过验证,但由于该通用实现的ReconcileAccessorXmlReflectionImporter课堂上,我们得到了一个例外.不确定这是否是确切的问题XmlReflectionImporter,或者可能是更高的抽象层上存在另一个问题.并且,"Web引用"生成的源未使用XmlReflectionImporter.

另外值得一提的是:generator Namespace=""为MessageBodyMemberAttribute 设置了一个值,这有效地打破了对帐过程.所以,我认为存在一些不一致或不兼容的问题.

  • 你好,从 2019 年开始......这个问题仍然存在,对于我们这些无法控制我们必须使用的 WSDL 的人来说,msft 现在应该已经解决了。显然,有一些常用的工具可以在 php 中生成 WSDL,它喜欢通用的“返回”属性名称和反映该 XML 的 .net 扼流圈。精彩的。 (3认同)