RSS 2.0 Feed
RSS 2.0


Atom 1.0 Feed
Atom 1.0

  Returning Objects from Web Services 


When I work with web services I want things to work the same way as if I were working with a local layer that returns objects, not data. I don't want my code outside of the service to even see the data, just the objects that represent the data. Who doesn't? There are some things to know when it comes to consuming objects returned by a web method and they're not what you might expect on first attempt.

First of all, let's say we have the following Person object. We'll put this in an assembly and use it in the web service.


public class Person
{
    public Person() {}

    public Person(string firstname, string lastname, DateTime dob)
    {
        this._fname = firstname;
        this._lname = lastname;
        this._dob = dob;
    }

    public string FirstName
    {
        set { this._fname = value; }
        get { return this._fname; }
    }

    public string LastName
    {
        set { this._lname = value; }
        get { return this._lname; }
    }

    public DateTime DOB
    {
        set { this._dob = value; }
        get { return this._dob; }
    }

    public int Age
    {
        get
        {
            return DateTime.Now.Year - _dob.Year - 
                (_dob.Month > DateTime.Now.Month ? 1 : 
                    (_dob.Month != DateTime.Now.Month) ? 0 : 
                        (_dob.Day > DateTime.Now.Day) ? 1 : 0);
        }
    }

    public override string ToString()
    {
        return string.Format("{0} {1}", _fname, _lname);
    }

    private string _fname = string.Empty;
    private string _lname = string.Empty;
    private DateTime _dob = DateTime.MinValue;
}


Nothing fancy, some public properties. Some that just set/get private members, others that do something, such as the property for Age and the ToString() override. Now we'll add a simple web method to a service to return an instance of this object.


[WebMethod]
public Person GetPerson()
{
    return new Person("Ryan", "Farley", DateTime.Parse("1/1/1970"));
}


So far so good. Now when you add a web reference for the service and call the GetPerson method things go wrong. There are a couple of typical approaches I see most often when I am asked about this kind of thing. First of all, when you add the web reference, a proxy class is created. You've possibly taken a look at these before. The proxy class allows you to make synchronous or asynchronous calls to the web service without needing to know about any of the SOAP bindings or other stuff that is going on. It allows you to call GetPerson just as if it were a method in a local class in your project. What it also does in our case is create a proxy class for our Person object. However, if you try to use the GetPerson method now things won't go as expected. For example, when we use ToString() it does not return the Person's full name, instead it returns a string indicating the object's type, as if it was not overriden at all. Also, our Age property doesn't even appear to exist at all in the class. Let's take a look at why. If we look at the proxy class created by adding the web reference (by opening the Reference.cs file which is located in the appropriate sub-directory that you'll find in the Web References directory under your project) you'll see not only the proxy class for the web service, but also a proxy class for the Person object. But this Person object is not the same as what we returned from our web service. Here's how the Person proxy class looks.


[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]
public class Person 
{
    /// 
    public string FirstName;
    /// 
    public string LastName;
    /// 
    public System.DateTime DOB;
}


The contract of the object may be the same, but it is definitely not the same object. All of the internal code is missing, nothing more than the public read/write members exist. No good.

So you might think that all we need to do now is add a reference to the same assembly containing the Person object and cast the result to that Person type. Not the case, you'll get an InvalidCastException thrown at you. The web method is returning a Person object as defined in our assembly, so why can't we use it as a Person object referenced from that same assembly? If we think about it, when we looked at the proxy class in the Reference.cs file, it defined the return of the GetPerson method as a Person object as defined in the proxy. So if we try to cast it as our Person object then we are the ones that are using the wrong types according to the proxy class. All makes sense now, right?

It is an easy enough solution to remedy this problem. No reason why we just can't modify the proxy class to return the correct Person object (as defined in our assembly). Open the Reference.cs file again and delete the Person proxy class. Then modify any references to the Person object to reflect the Person object defined in our assembly. The actual reference to the assembly containing the Person object already exsits in the project, so we can simply add the appropriate namespace as a using directive and drive on. Recompile and all is well. You are successfully consuming a Person object from your web service. Cool. Keep in mind that if you update the web reference then your changes to the Reference.cs file will be lost and you'll have to make the changes again. One thing you can do is manually create the proxy class file using the command-line wsdl.exe tool, make the changes to that class, and then add it to the project. Then you're safe from loosing those changes.

Edit

One thing I failed to mention. Even though you are returning a Person object from your web service, and using it as a Person in the client, you are only going to get the values for any public members that the Person class exposes. For example, if you have private members in your class and these private members don't have public read/write accessors, then you won't get those values when you consume the service and use the returned Person object. The reason why this is becomes very apparent when you look at the actual XML return for the GetPerson method in our service. Let's say we add a private member to store the person's Social Security number and we add a writable only setter for this member (let's just say the number is not directly accessed by the client but instead the private memeber is used elsewhere by other public members). When you look at the XML returned by the GetPerson method it will still look like this:

<?xml version="1.0" encoding="utf-8" ?> 
<Person xmlns:xsd="http://www.w3.org/2001/XMLSchema" ...>
    <FirstName>Ryan</FirstName> 
    <LastName>Farley</LastName> 
    <DOB>1970-01-01T00:00:00.0000000-07:00</DOB> 
</Person>

No sign of the value for Social Security number anywhere, so be sure that it will not make it to the client (obviously). If you think about what is happening behind the scenes it all makes sense. The Person object is serialized when returned from the GetPerson method. So you're only going to get the publically accessible values returned. So make sure you keep this in mind when designing your objects so you don't hose yourself when you try to access a value that never made it from the web service.




                   



Leave a comment below.

Comments

  1. Gabriel Kohen 9/10/2004 7:17 AM
    Gravatar
    Regarding the first problem:
    You can always extend the generated class in the Reference.cs and override whatever functionality you like
  2. HI 12/19/2004 9:13 AM
    Gravatar
    Hi,
    I did not understand
    Open the Reference.cs file again and delete the Person proxy class. Then modify any references to the Person object to reflect the Person object defined in our assembly. The actual reference to the assembly containing the Person object already exsits in the project, so we can simply add the appropriate namespace as a using directive and drive on. Recompile and all is well. You are successfully consuming a Person object from your web service. Cool. Keep in mind that if you update the web reference then your changes to the Reference.cs file will be lost and you'll have to make the changes again. One thing you can do is manually create the proxy class file using the command-line wsdl.exe tool, make the changes to that class, and then add it to the project.

    Pls explain

    bye
    tom
  3. hari babu 3/14/2005 3:00 AM
    Gravatar
    hi
    it's really helped me a lot
    my problem is littlebit extensive for this
    i want to return "IList" collection from a web service member.
    cn you please help me in that way

    Thanks & Regards,
    Hari Babu Chevuturi
    mail ID:haribabu_chevuturi@yahoo.com
  4. Ana 6/23/2005 3:17 PM
    Gravatar
    Hi.
    Your code and explanation helped me to solution the same problem.
    I search a lot trying to understand why my program didn´t work and nobody found the problem until i found and read your post.

    Thank you.

    Bye.
  5. Yalaguresh Jorapur 7/21/2005 12:41 PM
    Gravatar
    Very good article. It saved my time.
    Thanks Ryan.

    Yalaguresh Jorapur
    mail: yalaguresh@yahoo.com
  6. Eugen 8/15/2005 6:47 PM
    Gravatar
    Could you please elaberate on the step 'Then modify any references to the Person object to reflect the Person object defined in our assembly'. I get compile errors after removing the proxy class.
  7. Ryan Farley 8/15/2005 7:39 PM
    Gravatar
    Eugen,

    Well, when you add the web reference to the web service in your app, it creates a proxy class. So in the example, we have the Person class in the proxy, but also the Person class in our assembly. We need to first add a reference to the assembly containing our Person class, but then also modify the code in the proxy to use the Person class in the assembly, by adding the appropriate namespace or using the fully-qualified name for the Person class in the assembly. Basically, anywhere it says Person, you need to modify that to say YourAssemblyNamespace.Person. If the assembly namespace matches the namespace of the Person proxy class then you don't need to change anything more than removing the Person class defined in the proxy and add a reference to the assembly containing the Person class.

    Does that make more sense?

    -Ryan
  8. Andy 11/29/2005 3:09 PM
    Gravatar
    Thanks Ryan,

    That last comment sorted it for me, I just had to remove the class that was automatically defined in the proxy by Visual Studio, and add a using reference to the assembly that contains my class.

    Cheers, you saved me a lot of time.

    Andy.
  9. Rebecca 12/13/2005 8:55 AM
    Gravatar
    Hi Ryan,

    I want to return "ArrayList" collection from a web service member.
    When I call the method from the client, I get a compilation error that says 'Cannot implicitly convert type 'object[]' to 'System.Collections.ArrayList'

    I added using System.Collections in the proxy class and replace all the object type with ArrayList but I still get error message regarding the conversion.

    Can you please help.
  10. Jandost Khoso 1/8/2006 7:49 AM
    Gravatar
    Thanks a lot.
    your solution really held me! but i wanted to ask it this ultimate solution? i am asking this because for every change in Service we need to open Reference.cs and edit it. it there any other way to avoid this job?

    Regards,
  11. Fred 4/6/2006 4:28 AM
    Gravatar
    Nice article. We have the *exact* same situation at work and came up with the same conclusion (to modify reference.cs to point to the location with the real classes) -- but we did it for different reasons. In our case, we have a library loaded on a web server, and the same library loaded locally to communicate with a database on the users machine (its a resource framework library) ... so to make this successful, we interfaced - out the data access to the WS or DB, but the objects going into and coming out of the data access interface needed to be the same regardless of whether it was ultimately implemented via a WS or DB call.
  12. { public virtual blog; } 5/25/2006 2:43 PM
    Gravatar
    I started this blog in August of 2003, almost 3 years ago. I've made 176 posts in those 3 years. I don't post too often to my blog because I'm not all that big on posting stories about my kids, wife, dog, etc - although those do come in every now and then. Anyway, even when I have lulls where I am not posting as much, my traffic seems to stay pretty consistent. I'm actually amazed at how much traffic I get, especially when I consider how often I get around to posting (big thanks to all the visit
  13. Arash 3/5/2007 8:25 AM
    Gravatar
    Hi :)

    Thank you. Very useful. I just wonder why Microsoft didn't set it up this way, itself! Of course, Microsoft makes me wonder from time to time; in fact, this one's not been a really big surprise :D

    Cheers,
    Arash
  14. kevin Mocha 8/13/2007 5:11 PM
    Gravatar
  15. John F. 8/23/2007 8:27 AM
    Gravatar
    Can this be done from a web app? There is no Reference.cs, just wsdl, disco and discomap.
  16. John 8/23/2007 9:06 AM
    Gravatar

    I did the following....

    1. Generate web ref as usual.
    2. Run wsdl.exe against the wsdl created by step 1.
    3. Add the resulting .cs file to app_code folder.
    4. Edit that .cs file as described in Ryans excellent blog.

    Does anyone see a problem with this?
  17. Eddie 10/30/2007 1:43 PM
    Gravatar
    I followed exactly what it is in the article, the program compiles sucessfully with no errors and I can pass an array of that object to the web service. However, the value of objects contained in the array that I pass in are all default value (i.e. null for string and 0 for int). The array itself does contains some objects as shown by the length attribute, but no matter how hard I try, I still can't send the arrary of object with value. Does anyone have any idea?
  18. Erez Lotan 11/26/2007 10:01 PM
    Gravatar
    Thanks!, your article proved to be helpful.

  19. Ryan Bales 1/23/2008 11:13 AM
    Gravatar
    Ryan,

    Great example...

    Another question

    Do you know how to override the display name of objects returned from web services?

    Thanks,
    Ryan
  20. nobita 4/18/2008 5:24 PM
    Gravatar
    I defined a class MyClass in WebService Project, and now in my client project, I want to get its type and use it. Example, in webservice i define class MyClass, and now in my client project I use Type.GetType("MyClass") or Type.GetType("localhost.MyClass") is null. How can I get its type?
    Thanks so much.
  21. DClonch 5/24/2008 8:30 PM
    Gravatar
    AWESOME! I searched for hours on how to do this. You saved me a huge headache!
  22. Brandon 6/7/2008 9:41 AM
    Gravatar
    can you post your codes to show us for example?
  23. DJ 7/10/2008 6:15 AM
    Gravatar
    Hi,
    Result of your sample is

    <?xml version="1.0" encoding="utf-8" ?>
    <Person xmlns:xsd="http://www.w3.org/2001/XMLSchema" ...>
    <FirstName>Ryan</FirstName>
    <LastName>Farley</LastName>
    <DOB>1970-01-01T00:00:00.0000000-07:00</DOB>
    </Person>

    Can i have this result in my code. Actually i want to have XML generated for list objects say persons in method itself. But i am not able to find any direct method. and dont wat to process it.

    Regards,
    DJ
  24. Mel 10/21/2008 4:53 AM
    Gravatar
    Rebecca already asked what I am looking for. Is it possible to return an ArrayList from a web service? I guess no, found no solution on the web, only open questions.
  25. ikllano 4/23/2009 2:26 AM
    Gravatar
    I followed exactly what it is in the article. I have a class similar to the one in the example. But when the object arrives to the client, all the values are default.

    How can I solve this?
  26. Rich 9/7/2009 2:51 AM
    Gravatar
    Ryan you are a God among men!

    I cannot tell you the hours that I have spent fighting with this problem and the number of things that I have tried to correct it.

    Having deleted the proxy reference as you suggest everything is now working perfectly! Thank you so much. Luckily I found your post whilst I still had hair left.

  27. Arvydas 9/27/2009 8:26 AM
    Gravatar
    Hi, Thanks for solution, I used this solution too, just i am developing big webservice and client program too, then after any webservice interface update i all the time need to change Reference.cs.

    I am unsure, but maybe possible to solve this in another way? maybe with some System.Xml.Serialization headers or editing webservice project? Just Microsoft namespaces with used build in .net datatypes are added automatically after web reference is added, then maybe exist way to do same with own datatypes, i still will search for it...
  28. Rupesh Tiwari 9/28/2009 3:55 AM
    Gravatar
    Hi,
    I have the same issue. I want to pass typed object to webservices.
    I have a webapplication and when i am puting the webreference of a webservices then i am only getting the disco, .discomap, .wsdl file i am not getting any reference file how do i proceed.
    kindly suggest me asap.

    Thanks in advance.
  29. Roi 12/6/2009 8:38 AM
    Gravatar
    That was really helpfull, thank you.

    What shall i do when creating a web application, there is no reference.cs...
    what is wdsl.exe, where can i find it?
  30. Roi 12/6/2009 8:39 AM
    Gravatar
    That was really helpfull, thank you.

    What shall i do when creating a web application, there is no reference.cs...
    what is wsdl.exe, where can i find it?
  31. mr_cis 12/14/2009 6:48 AM
    Gravatar
    Hi all,

    I created an Windows application to call java web service. this web service return a list of obejct
    It worked fine.

    I created a Pocket PC application to call that web service.
    I run that PPC application from my Pocket PC which is in the same network as my desktop computer in which Web service is running.
    I got an a list of object ,but this list is empty nothing on it .

    am using vs 2005
    Any help is much appreciated.
  32. lerchg 12/17/2009 4:12 AM
    Gravatar
    Hi
    I followed your suggestions and editing the reference.cs file was easy. However, when I check the returned object, I have only "null" values. If I debug the webservice, my retruned object is created properly and contains the values I expect but once back on the client layer there are no values.
    I realized that just after the return for the web service, the empty contrructor of my class is been called, I just dont understand why.

    Help appreciated.

    Thanks
    Guido
  33. 12/18/2009 11:40 PM
    Gravatar
    How to return a user defined Object from webservice &amp;laquo; Arrao4u
  34. Kannan 3/5/2010 12:32 AM
    Gravatar
    How to Return Object in webservice using in .net.
  35. Hung 4/1/2010 4:44 AM
    Gravatar
    I found some people ask about return object have "null" value. If you use WebReferences, let check wsdl file to be sure targetNamespace and name space are same.

    Hope this help!
  36. 12/17/2013 5:33 PM
    Gravatar
    How to return a list of objects from soap webservice with rails | Debugger
  37. 9/11/2014 5:06 PM
    Gravatar
    Transferring files from Linux server to Windows accessory automatically [closed] - Ziglar Gyan
  38. 12/5/2014 4:17 AM
    Gravatar
    How to return a list of objects from soap webservice with rails | Zody Answers
  39. 12/8/2014 8:49 AM
    Gravatar
    Returning Business Object from Web Service | Zabielski Answers
  40. 12/10/2014 11:29 AM
    Gravatar
    Cannot resolve the return value from a client object WebService | Zaniboni Answers
  41. 12/11/2015 5:52 AM
    Gravatar
    Returning Business Object from Web Service | Questions
Comments have been closed on this topic.



 

News


Also see my CRM Developer blog

Connect:              


Sponsor

Sections