Friday, January 22, 2010

Interoperability Gotcha: "These members may not be derived"

@YaronNaveh

The following gotcha applies when the server is written in a Java framework (e.g. Axis) and the client is in .Net. When we "add service reference" in VS to create the client proxy from the wsdl we may get this error:


Custom tool error: Unable to import WebService/Schema. Unable to import binding 'SomeBinding'
from namespace 'http://SomeNamespace'. Unable to import operation 'myOpereration'.
These members may not be derived.


In order to understand what this means we need to take a look at some other (valid wsdl):


<s:element name="EchoString">
 <s:complexType>
  <s:sequence>
   <s:element minOccurs="0" maxOccurs="1" name="s" type="s:string"    />
  </s:sequence>
 </s:complexType>
</s:element>
...
<wsdl:message name="EchoStringSoapIn">
 <wsdl:part name="parameters" element="tns:EchoString" />
</wsdl:message>
...
<wsdl:portType name="SimpleServiceSoap">
 <wsdl:operation name="EchoString">
  <wsdl:input message="tns:EchoStringSoapIn" />
  <wsdl:output message="tns:EchoStringSoapOut" />
 </wsdl:operation>
</wsdl:portType>


This means that the EchoString operation accepts a message that looks like that:


<EchoString xmlns="...">
 <s>some string</s>
</EchoString>


We might expect the proxy class to look something like that:


string EchoString(EchoStringRequest req) {...}

class EchoStringRequest
{
  string s;
}



However the real proxy looks like this:


string EchoString(string s) {...}


Why?
The reason is that most wsdls use the "document/literal/wrapped" pattern. This means that the first element of the message ("EchoString" here) is just some wrapper that the user does not really care about. What our user cares about is the "s" parameter. So the proxy generated the more useful proxy.

But let's take a look at this schema:


<s:element name="EchoString" type="tns:SomeBaseType" />

<xs:complexType name="SomeBaseType">
  <xs:sequence>
   <xs:element name="inParent" type="xs:integer"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="SomeDerivedType">
  <xs:complexContent>
   <xs:extension base="tns:SomeBaseType">
    <xs:sequence>
     <xs:element name="inChild" type="xs:integer"/>
    </xs:sequence>
   </xs:extension>
  </xs:complexContent>
</xs:complexType>


Here the root element "EchoString" is not a dummy wrapper - it can take the form of a few types (since there is derivation). This means the proxy should not omit it.

So how does VS knows which proxy to create?

The answer is in the message definition in the wsdl:


<wsdl:message name="EchoStringSoapIn">
 <wsdl:part name="parameters" element="tns:EchoString" />
</wsdl:message>


Whenever the name of a part is "parameters" .Net assumed doc/lit/wrapped is used and generates the proxy accordingly. If even though the word "parameters" is used the wsdl is not doc/lit/wrapped (as in the last example) .Net may give us some error. Which error? You guessed correctly: "These members may not be derived". Now we can understand what the error means: .Net tries to omit the root element as it thinks doc/lit/wrapped is used. However this element cannot be removed since it is not dummy - it should be actively chosen by the user out of a few derived types.

Fix
The way to fix it is open the wsdl in a text editor and change the part name from "parameters" to "parameters1". Now .Net will know to generate a doc/lit/bare proxy. This means a new wrapper class will appear as the root parameter in the proxy. While this may be a little more tedious api, this will not have any affect on the wire format and the proxy is fully interoperable.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

17 comments:

Evgeniy Podolyak said...

Hello Yaron.

I have similar problem.

We have a lot of request/response messages where we have the same metadata elements. We are going to move these elements declaration into base request/response types. In Java it works OK but in our .NET clients we getting the same error as in your example. But our case a little bit different from your. We don't use base types for request response elements, we are using types derived from base types.



Could you please help me to understand why it doesn't work? And may be there is any workaround without changing "parameters" to something else. I cannot do it because there is big codebase with a lot of usages of types generated for wrapped style.


Thanks!

Evgeniy Podolyak said...

So instead of:
<s:element name="EchoString" type="tns:SomeBaseType" />

We have:

<s:element name="EchoString" type="tns:SomeDerivedType" />

Yaron Naveh said...

Evgeniy

You are basically experiencing the same issue. In your case it might still have made sense for .Net to generate the proxy since no derived types to choose from. I guess maybe .Net does not know how to go to the base type and pick up the derived fields to put them as parameters as well.

First of all to verify the problem change to parameters1 in one of the wsdls and see if that works.

If you have control over all .Net clients development you may ask them to change the wsdl locally before importing it, or even build a dedicated importer interceptor (I believe there is some extension point for that).

If you require this to work for the general public to import these wsdls you have to make them interoperable, either by changing to parameters1 or (preferred) by adding a wrapper element over each operation (as implied by the doc/lit/wrapped notation). The former means changing the wsdls, the latter also means changing the service to handle this.

Evgeniy Podolyak said...

Thanks Yaron!

I didn't have any issues with base type fields in other places in .NET. Inheritance just doesn't work for request/response (it even works for fault contracts).

Changing to "parameters1" helps but it generates additional wrapper type around my request type which will lead to breaking changes for all existent clients.

We are not provide our contract as WSDL we provide it as .NET libraries. So, I have full control over code generation.

Yaron Naveh said...

The problem is only when the inheritance is in the root.

If you have full control over the .Net code you can change the wsdl for one moment while generating the code (even locally on your file system) and return it back later.

Just out of curiosity can you tell me a little about this publishing model (sending the .Net code and not wsdl). What kind of project is that? Who are the clients? Why have you chosen that model? If you prefer you can send it directly to my mail.

Evgeniy Podolyak said...

What do you suggest to change in WSDL on the fly? "parameters" to "parameters1"? It will cause another code generation issue, new wrapper type will generated around each request/response type.

Yaron Naveh said...

This is not an interoperability issue. The outgoing soap is the same either way, the omitting of the wrapper is just for conveniency. You are right that this new proxy is a little less convinient if this is the issue you mentioned.

Evgeniy Podolyak said...

So if with wrapper and without wrapper SOAP is the same then it means that I can omit wrapper types pragmatically after code generation. Seems it is a solution.

Thank you!

Yaron Naveh said...

No you can't. Removing it from the proxy would change the resulting soap. It is identical only when .Net attaches some attributes to it in the code generation. So:

A proxy with wrapper class with attribute X is equivalent to a proxy wihtout the wrapper with attribute Y. .Net cannot generate the second option in your case so it generates the first.

If the resulting soap requires to
have the "xsi:type=" attribute on the derived type I don't see how you can work with a different proxy then generated with "parameters1".

Evgeniy Podolyak said...

I can do it! I can post process code generated by .NET and replace 1st option to 2nd with all necessary attributes.

Abhang Rane said...

Yaron,
I stumbled on your blog while searching for "what is a breaking change in a Java service for a .NET client". I am trying to establish what kind of changes in the wsdl would really break any client which has not rebuilt the proxy. One thing that pops up in mind is a change in signature of an existing method. Any help appreciated.

Yaron Naveh (MVP) said...

Hi Abhang

Breaking changes are not just between Java and .Net application, but also between two .Net parties a change can be breaking.

Some examples of breaking changes are removing a method, adding a new required field or changing soap actions.

See more here:
http://msdn.microsoft.com/en-us/library/ms731138.aspx

Abhang Rane said...

Thanks Yaron. That was helpful.

JP Negri said...

Thank, Yaron!

Saved may day!

Pavel Ĺ avara said...

parameters fix

https://github.com/ServiceStack/ServiceStack/pull/807

Yaron Naveh (MVP) said...

Great work, Pavel!

Macganzyver said...

Thanks!!