Objects and Web Services

I've  been asked this question enough both on-line and in person that I thought a blog might be a better way to clear up a common misconception. Consider the following situation.  Your application has implemented a class as follows:

public class Dog
{
   public string Breed;
   public string Fetch()
   {
      return "stick";
   }
}

As well, the application has a web service method defined like the following:

[WebMethod]
public Dog GetDog()
{
   return new Dog();
}

So let's move to the client side. In a typical .NET application, you would add a web reference pointing to the web service that includes the GetDog method just described.  Under the covers, Visual Studio .NET invokes wsdl.exe to generate the proxy class. Because of the magic of this tool, not only is a proxy for the web service created, but so too is a class that represents the Dog class that is returned from GetDog.  This means that, on the client side, there is no need to deploy the assembly that implements the Dog class on the server-side.  Or does it.

If you open up the .cs file generated by adding the Web Reference (you might have to “show all files” in the Solution Explorer then navigate to below the Reference Map in the Web Reference - the file is called Reference.cs), you will find a class called Dog defined. Does the automatic addition of the Dog class make it easy for developers to work with web services?  Yes. Does it cause some level of confusion?  Definitely.  Which is the reason for this post.

Take a close look at the Dog class in Reference.cs.  What you will see is a class that contains all of the public properties that were exposed by the server-side Dog class.  What you will *not* see is any of the business logic that might have been implemented in the server-side Dog class.  Nor will you see any methods, public or otherwise.  In other words, these two classes are absolutely not the same class, regardless of the similarity in naming. 

What kind of problem does this cause? Say you actually deploy the server-side assembly onto the client and try to pass an object of that type (the server-side Dog class, that is) into the web service method call.  The result is that an apparently inexplicable InvalidCastException error gets thrown.  The reason for the exception is because the server-side Dog class is not the same Dog class defined in the proxy class.  That should make sense now, but it throws a lot of new web service developers for a loop.

There is a solution to the problem.  Once the proxy class has been created, edit the Reference.cs file directly.  Remove the Dog class from that file and change the return type for the GetDog method to match the server-side Dog class.  Now the InvalidCastException will go away.  Keep in mind that, should you decide to refresh the Web Reference, then you will have to make these changes again.  So when I do this kind of manipulation, I would typically use wsdl.exe manually to create the proxy class file and they add it into the project.  This eliminates the potential for wiping out the changes accidentally.

 

Comments

  • bruce October 5, 2004 6:42 AM

    Thanks!
    I squeezed my brains for a few days trying to get around this problem!
    ------------------
    paolo.nunberg@tin.it

  • bruce October 21, 2004 2:59 AM

    What do you exactly meand by "change the return type for the GetDog method to match the server-side Dog class"? I supossed the web method is deployed on the server and that should return an instance of the server class? Am I right? Correct me if not please.

    Thanks. I'm willing for your answer.

  • bruce December 19, 2004 1:24 PM

    Client doe snot compile if i remove
    Remove the Dog class from that file and change the return type for the GetDog method to match the server-side Dog class

  • bruce November 1, 2005 4:19 PM

    Hey Bruce,
    I'm confused. We should NOT expect to see any business logic code in the client-side proxy. The proxy class exists merely to give the client the ability to instantiate and poplulate the custom object to pass to web methods. The class file that defines a type for web method consumption should NEVER have business logic within that class. In other words, the Fetch() method should not be part of the Dog object.

    The client must be able to instantiate a Dog, but it is only on the web service side that the Dog "does" things (i.e. fetches). So, for example, you would pass the Dog from client-side to a web method called Fetch() which would in turn call the business logic.

    This is my understanding based on MCS guy that taught me about web services, but I'm willing to re-think if needed.

  • bruce November 2, 2005 8:19 PM

    Will, you're right that I don't *expect* to see business logic in the generatede client-side proxy. But it doesn't follow that the object used on the client side should *never* have any business logic at all. After all, wouldn't it be convenient if the client side object had validation logic that would duplicate that performed on the server? Logic that is encapsulated in the class that could easily shared between client and server?

    And yet, this desirable situation is not possible without performing the mechanations that I described. At least, not until 2.0. But more on that in an upcoming post.

  • Kevin March 6, 2008 6:24 AM

    Hey Bruce:

        Can you explain a little more on how to use the WSDL.exe tool so that my changes to Reference.cs aren't overwritten every time?

  • Tom May 24, 2008 1:55 PM

    What do you do in VB in the same situation? I have looked at all the files from the Web Service and there is nothing that looks like your description.

    Thnaks

Leave a Comment

(required) 
(optional)
(required) 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS