BUG: Web Services & Polymorphism

G

Guest

This is a definite and reproduceable bug in Web Services, Visual Studio 2003.

Here is a simple example. Create a web service...

// this represents an original version of an object
class ObjectV1 { ... } // holds some arbitrary data

// this represents a newer version with extra information
class ObjectV2 : ObjectV1 { ... } // adds some additional information

// this is an 'original' function, which returns the base class original
information
[WebMethod]
public ObjectV1 _GetObject()
{
// this represents the base layer was updated with a new derived
// object with extended information.
return new ObjectV2();
}

// this is a stub function to return the new version explicitly
[WebMethod]
public ObjectV2 _GetObject2()
{
// very simple upcast knowing the base layer is updated
// no need to reimplement everything redundantly for this new type
return _GetObject() as ObjectV2;
}

Create a web service client... Everything WORKS...

But now... following the same extensibility model...

Extend the web service to return ObjectV3...

class ObjectV3 : ObjectV2 { ... } // latest extended version

// this represents the base layer is returning the latest object version
// through the otherwise non-changing public layer
[WebMethod]
public ObjectV1 _GetObject()
{
return new ObjectV3();
}

Now, at this point, the client is still using _GetObject operating on
ObjectV1.

WITHOUT REGENERATING THE WEB SERVICE ON THE CLIENT SIDE...

The client crashes with an error parsing the XML response. Why? ObjectV3 is
polymorphically compatible with ObjectV2. The call signature of _GetObject
hasn't changed. It is still legal usage of polymorphism. There wasn't a
problem with it when it was V2->V1.

You will actually see the problem simply going from V1 to V2, provided you
do not regenerate the WSDL on the client side.

Why not regenerate the client's web service definition?
This represents extending a web service while still supporting previous
versions of the web service clients, who retrieve a polymorphic downcast of a
derived type, i.e. operate on an inherited type from its base class. They are
installed, and need to continue functioning.

Why can't the XML be parsed without regenerating the web client's web
service definition?

You're wrong if you think it SHOULD be this way. Also, don't suggest using
an alternate extensibility model. This model was not my idea, but in itself,
it is very reasonable, and it is probably very common. I already know how to
resolve it by rearchitecturing the web service to promote extensibility...
Boxing and unboxing the parameters and return object with version control.

I am basically looking for a resolution that doesn't require redundant
reimplementation and maintanence of several versions of code.
 
G

Guest

It is still a BUG, but here is an acceptable solution...

You will have to implement copy constructors on all the objects, and change
the _GetObject function to:

return new ObjectV1( base layer's return object );

The copy constructor will ensure that strictly ObjectV1 is returned, and
this essentially disables polymorphism over the web service chasm.

You still need things like _GetObjectV2, _GetObjectV3, simply for the reason
that the web service definition will not recognize ObjectV2, ObjectV3 unless
one of the web service functions returns it.

Also, the caller will need to call the appropriate V2, V3, whereas with the
PROPER way, you would have 1 function, and the caller could recognize the
object type polymorphically. Because of the BUG, the PROPER ways are not
possible.
 
F

Frans Bouma [C# MVP]

Marshal said:
This is a definite and reproduceable bug in Web Services, Visual
Studio 2003.

Here is a simple example. Create a web service...

// this represents an original version of an object
class ObjectV1 { ... } // holds some arbitrary data

// this represents a newer version with extra information
class ObjectV2 : ObjectV1 { ... } // adds some additional
information

// this is an 'original' function, which returns the base class
original information
[WebMethod]
public ObjectV1 _GetObject()
{
// this represents the base layer was updated with a new derived
// object with extended information.
return new ObjectV2();
}

// this is a stub function to return the new version explicitly
[WebMethod]
public ObjectV2 _GetObject2()
{
// very simple upcast knowing the base layer is updated
// no need to reimplement everything redundantly for this new type
return _GetObject() as ObjectV2;
}

Create a web service client... Everything WORKS...

But now... following the same extensibility model...

Extend the web service to return ObjectV3...

class ObjectV3 : ObjectV2 { ... } // latest extended version

// this represents the base layer is returning the latest object
version // through the otherwise non-changing public layer
[WebMethod]
public ObjectV1 _GetObject()
{
return new ObjectV3();
}

Now, at this point, the client is still using _GetObject operating on
ObjectV1.

WITHOUT REGENERATING THE WEB SERVICE ON THE CLIENT SIDE...

The client crashes with an error parsing the XML response. Why?
ObjectV3 is polymorphically compatible with ObjectV2. The call
signature of _GetObject hasn't changed. It is still legal usage of
polymorphism. There wasn't a problem with it when it was V2->V1.

You will actually see the problem simply going from V1 to V2,
provided you do not regenerate the WSDL on the client side.

Why not regenerate the client's web service definition?
This represents extending a web service while still supporting
previous versions of the web service clients, who retrieve a
polymorphic downcast of a derived type, i.e. operate on an inherited
type from its base class. They are installed, and need to continue
functioning.

Why can't the XML be parsed without regenerating the web client's web
service definition?

You're wrong if you think it SHOULD be this way. Also, don't suggest
using an alternate extensibility model. This model was not my idea,
but in itself, it is very reasonable, and it is probably very common.
I already know how to resolve it by rearchitecturing the web service
to promote extensibility... Boxing and unboxing the parameters and
return object with version control.

I am basically looking for a resolution that doesn't require
redundant reimplementation and maintanence of several versions of
code.

this is caused by the fact that the client has a proxy class for the
service, which in fact contains NEW classes for the same types. Because
it gets a type B from a given method M, wsdl.exe simply generates a
class of type B, it doesn't know if B derives from A or not.

In .NET 2.0, this is changed, there you can specify a SchemaImporter
class which tells the wsdl tool to generate proxy classes with the
known types and not generate new types.

FB

--
 
N

Nick Malik [Microsoft]

Bug... perhaps. You are not exactly using the normal mechanisms for
extending a web service. The framework never promised the behavior you are
expecting, so it would be difficult to say that that the test team should
have found this. I'd like to be proven wrong. If you can find somewhere in
the spec for a web service that it says that using derivation, rather than
XML Versioning, for incremental versioning, would be a supported technique,
then I'm happy to eat my words.

You found a technique and jumped to conclusions about what it did for you
and what it could be useful for, without testing your conclusions. A
reasonable part of the responsibility for this situation rests squarely on
your shoulders.

Caveat: I am not a member of the product team or the support team. I have
not seen your situation before. I spend a fair amount of time on these
boards, and I do not know of any other users of VS who attempted to perform
versioning through subclassing. If you want to call customer support, I
encourage you to do so. They have access to a rather large knowledge base
that may have some more detail than I have access to.

My suggestion: change the code so that the original call returns an object
of the original type. The second call returns an object of the second type
and the third call returns an object of the third type. Newest client code
would use the newest call specifically. The underlying logic can all take
place using the newest object, and the oldest service will convert the
results back to the oldest return type.

Good luck
--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
--
Marshal said:
This is a definite and reproduceable bug in Web Services, Visual Studio
2003.

Here is a simple example. Create a web service...

// this represents an original version of an object
class ObjectV1 { ... } // holds some arbitrary data

// this represents a newer version with extra information
class ObjectV2 : ObjectV1 { ... } // adds some additional information

// this is an 'original' function, which returns the base class original
information
[WebMethod]
public ObjectV1 _GetObject()
{
// this represents the base layer was updated with a new derived
// object with extended information.
return new ObjectV2();
}

// this is a stub function to return the new version explicitly
[WebMethod]
public ObjectV2 _GetObject2()
{
// very simple upcast knowing the base layer is updated
// no need to reimplement everything redundantly for this new type
return _GetObject() as ObjectV2;
}

Create a web service client... Everything WORKS...

But now... following the same extensibility model...

Extend the web service to return ObjectV3...

class ObjectV3 : ObjectV2 { ... } // latest extended version

// this represents the base layer is returning the latest object version
// through the otherwise non-changing public layer
[WebMethod]
public ObjectV1 _GetObject()
{
return new ObjectV3();
}

Now, at this point, the client is still using _GetObject operating on
ObjectV1.

WITHOUT REGENERATING THE WEB SERVICE ON THE CLIENT SIDE...

The client crashes with an error parsing the XML response. Why? ObjectV3
is
polymorphically compatible with ObjectV2. The call signature of _GetObject
hasn't changed. It is still legal usage of polymorphism. There wasn't a
problem with it when it was V2->V1.

You will actually see the problem simply going from V1 to V2, provided you
do not regenerate the WSDL on the client side.

Why not regenerate the client's web service definition?
This represents extending a web service while still supporting previous
versions of the web service clients, who retrieve a polymorphic downcast
of a
derived type, i.e. operate on an inherited type from its base class. They
are
installed, and need to continue functioning.

Why can't the XML be parsed without regenerating the web client's web
service definition?

You're wrong if you think it SHOULD be this way. Also, don't suggest using
an alternate extensibility model. This model was not my idea, but in
itself,
it is very reasonable, and it is probably very common. I already know how
to
resolve it by rearchitecturing the web service to promote extensibility...
Boxing and unboxing the parameters and return object with version control.

I am basically looking for a resolution that doesn't require redundant
reimplementation and maintanence of several versions of code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top