The generated WCF proxy throws an InvalidOperationException due to several types with the same name in the WSDL

I am using Visual Studio 2013 to create a WCF proxy from this WSDL file . However, as soon as I try to call the setSalesItemsV3 method, WCF throws an InvalidOperationException from the depth of System.Xml.dll .

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

This is an internal exception:

Message: The top XML element "start" from the namespace "" refers to the various types WsdlDuplicateName.SalesItemService.hsSimpleDate and System.DateTime. Use XML attributes to specify a different XML name or namespace for the element or types.

I am not an expert on reading WSDL, but I 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> 

But the parts are in completely different messages, so I don’t understand why there should be some kind of confusion. I ran the WSDL file through several online WSDL validators, and they seem to be okay with it.

Below is the only code in the project needed to reproduce the problem (in addition to 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, }; } } 
+7
wsdl proxy-classes wcf
source share
3 answers

To demonstrate the problem, let's take a look at the 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 } 

Note that these elements have the same name ( start ) and the same namespace declared by the MessageBodyMember attribute ( "" , empty namespace). This reason “The top XML element” runs “from the namespace“ refers to various types. ”Serializer exception.

If we have this option:

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

... we can set namespaces for the start , end and return elements (all of which cause problems) manually. I did it myself and set the result here . You can embed it in your Reference.cs and serializer fix.

But it seems that the main reason for your problem is that this service ( http://services.hotschedules.com/api/services/SalesService?wsdl ) is intended to be used through WebServices (and this problem is a kind of incompatibility).

If you added a link to this server as a web link ( AddService Guide ...More ...Add Web Link ... ) and write the same call to the web method, no serialization problems will not arise. In fact, in my case, I got another kind of server exception in my test case, but it will solve your problem with immediate serialization.

A mirror copy of your code, but using the web service directory (and not requiring any changes to the generated files), can be found here .

Hope this helps.

UPDATE . To find the cause of this problem, we need to deeply study the source code of XmlReflectionImporter . First, our WSDL using XSD schemes for defining namespaces: http://www.w3.org/2001/XMLSchema for xsd and http://services.hotschedules.com/api/services/SalesService for tns . XmlReflectionImporter using a NameTable (this is a wrapper for a Hashtable ) to store " accessories ." An accessor is a pair of Namespace and Name .

See the source code that throws the 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". } 

So, the basic logic of matching we cannot have two accessories with the same name, but with different namespaces ! There may be some exceptions for the MembersMapping and ArrayMapping , but this is not our case.

I think this is some kind of mistake. The WSDL is correct and will pass the test, but due to this general implementation of the ReconcileAccessor class from XmlReflectionImporter we got an exception. Not sure if this is the exact problem of the XmlReflectionImporter , or another problem may occur at a higher abstract level. And the source created using the "Web Reference" does not use XmlReflectionImporter .

One more note: the generator sets the Namespace="" value for MessageBodyMemberAttribute, which actually disrupts the negotiation process. Therefore, I believe that there is some inconsistency or incompatibility.

+8
source share

Your problem may be how you use WSDL. Where I work, we have older services that require us to use wsdl.exe to generate class files from WSDL. We also use SoapUI to test our services. Without changing any WSDL or generated code, I can make a request to this system.

Fiddler captures:

Outgoing

 POST http://services.hotschedules.com/api/services/SalesService HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.18444) VsDebuggerCausalityData: uIDPo7vfNRAHy8VFtfrdjickfDQAAAAAVvkpSjtKpEyy02P7sVr8C51Xoz163FNKvwhRT+6uA+wACQAA Content-Type: text/xml; charset=utf-8 SOAPAction: "" Host: services.hotschedules.com Content-Length: 536 Expect: 100-continue Proxy-Connection: Keep-Alive <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><setSalesItemsV3 xmlns="http://services.hotschedules.com/api/services/SalesService"><concept xmlns="">1</concept><storeNum xmlns="">1</storeNum><start xmlns=""><day>1</day><month>1</month><year>1</year></start><end xmlns=""><day>1</day><month>1</month><year>1</year></end></setSalesItemsV3></soap:Body></soap:Envelope> 

Inbox

 HTTP/1.1 500 Internal Server Error Server: Apache-Coyote/1.1 Content-Type: text/xml;charset=UTF-8 Content-Length: 366 Date: Thu, 26 Mar 2015 16:51:22 GMT Proxy-Connection: Keep-Alive Connection: Keep-Alive <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><soap:Fault><faultcode xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">ns1:InvalidSecurityToken</faultcode><faultstring>Error in SecurityHeader: An invalid security token was provided</faultstring></soap:Fault></soap:Body></soap:Envelope> 

I received 500 errors from a security system.

+1
source share

I see several options:

  • use " add web link " instead of "add service link". I confirmed that it works. this will result in a rollback from the classic asp.net proxy, which is not as close as wcf but will do the job.

  • since there are only 6 methods (some seem dummy), you can import wsdl 6 times into 6 different proxies (maybe less). every time wsdl contains only one operation (just remove the otehr operation tags, don't worry about messages / schema).

  • change the parameter names in wsdl (start → start1, start2 ...), and then at runtime create some message inspector that goes back (start1, start2 → start).

  • (not verified). I believe that you can reorganize WSDL in such a way that instead of elements of elements each message contains one part, called a "parameter", which will be redirected to the xsd wrapper type with all the source parts. You will create one wrapper for each message. you can configure wcf to treat this as bare parameters and not emit a dummy shell element, so it looks the same on the wire.

  • Of course, if you are able to change the best server.

Each option has its pros and cons. Some will have a lack of runtime (No. 3), and some will complicate development time. It also depends if you expect this WSDL to change and you will need to reimport it many times.

+1
source share

All Articles