Ajax Service Methods: Returning Strong types or lists.

In all the examples we've seen so far, we've returned simple strings, integers, or a standard type / value.  That's all fine and good, until we have something a little more complex to return, or need to return a list or array of something, or a list of type.  In the following example I'll demonstrate through code how to accomplish just that.

So lets get started at our webservice first.  I'll create a webservice in my project named WebService1.asmx.  Inside it will be a class called WebService1.  The first thing we need to do is add the class attribute that lets us call this through our JavaScript proxy.

<System.Web.Script.Services.ScriptService()> _

I'm going to go ahead and fire up my service to check it and test the hello world example, once I'm done with that, we can move on!

<?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://tempuri.org/">Hello World</string>
 

Ok, I hope everyone checked their service, now...to create the complex type that we'll be returning, we need are going to add a new class file to our project, and call it widget.

I'm going to keep the example simple, so I've just wired up two properties, with their private members, and a default constructor with one overload so that I can new it in one line.

Public Class widget
 
 Private _Name As String
 Public Property Name() As String
 Get
 Return _Name
 End Get
 Set(ByVal value As String)
 _Name = value
 End Set
 End Property
 
 Private _Price As Decimal
 Public Property Price() As Decimal
 Get
 Return _Price
 End Get
 Set(ByVal value As Decimal)
 _Price = value
 End Set
 End Property
 Public Sub New()
 
 End Sub
 Public Sub New(ByVal Name As String, ByVal Price As Decimal)
 _Price = Price
 _Name = Name
 End Sub
End Class

Ok, so back to our webservice to do something with our widget!  I'm going to mock up 5 widgets for this example, normally you'd connect to your datasource or collection object to get real life data.

<WebMethod()> _
Public Function GetWidgets() As List(Of widget)
 Dim tempWidgets As New List(Of widget)
 For I As Integer = 1 To 5
 Dim tempWidget As New widget("Name:" & I, CDec(I))
 tempWidgets.Add(tempWidget)
 Next
 Return tempWidgets
End Function

I can verify this is working by running the service again and getting this result:

<?xml version="1.0" encoding="utf-8" ?> 
- <ArrayOfWidget xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
- <widget>
 <Name>Name:1</Name> 
 <Price>1</Price> 
 </widget>
- <widget>
 <Name>Name:2</Name> 
 <Price>2</Price> 
 </widget>
- <widget>
 <Name>Name:3</Name> 
 <Price>3</Price> 
 </widget>
- <widget>
 <Name>Name:4</Name> 
 <Price>4</Price> 
 </widget>
- <widget>
 <Name>Name:5</Name> 
 <Price>5</Price> 
 </widget>
 </ArrayOfWidget>

Ok, now for the fun part, lets work with this data coming back - lets get into the page we'd like to call the webservice, and wire it up.  The first thing we'll need is a container to hold this data.  I'm just loading up a div in this case, dont forget to set the ID so you can find it. 

<div id="returnData">
 
</div>

In this case, I'm going to have a button fire the event that calls the webservice, so we'll add that too:

<asp:Button ID="button1" runat="server" OnClientClick="doWork" Text="Do Work" />

The last thing we need before we write our JavaScript is our service reference inside our script manager on the page.

<asp:ScriptManager ID="scriptmanager1" runat="server">
 <Services>
 <asp:ServiceReference Path="~/WebService1.asmx" />
 </Services>
</asp:ScriptManager>

Ok, on to our Javascript!

I'm going to post the complete example in here - and let you guys parse through it.  Please feel free to post comments or questions:

<script type="text/javascript" language="javascript">
 function pageLoad(){
 ret = WebService1.GetWidgets(onComplete, onError, onTimeout);
 }
 function onComplete(result){
 var retData = new Array(5);
 retData = result;
 var strDiv = ""
 var x = 0;
 for (x=0; x<5; x++)
 {
 strDiv = strDiv + retData[x].Name + " " + retData[x].Price + "<br />";
 }
 document.getElementById("returnData").innerHTML = strDiv;
 }
 function onError(){}
 function onTimeout(){}
</script>

As always, Happy Coding and good luck!

Bryan Sampica

4 Comments

  • I believe in VS 2008 you even get intellisense for this. Looks great.

  • But why ' var retData = new Array(5);'?
    In the next line you are replacing it with the reference to a completely different object.. Just use the object passed to the function.

    Also, its a bad habbit using empty handlers..
    Just leave them out at the framework will use its default handlers..

    You also need to reference the System.Collections.Generic namespace to be able to use List(Of t).

  • Thanks for the comments Sean

    You are correct on all cases. Generally in a Blog post though, I tend to be verbose on the side of err so that the point isnt lost. It's comments like this though that help keep things straight. :)

  • Well, yeah - that's something really.
    But the title is a bit deceiving - the types returned are not strongly typed.

    The "widget" client-side class has no "back bone". It's got no actual class structure. There's no way of knowing what it contains until it's being run. And all logic found on the server-side entity doesn't exist on the client-side entity.

    Let's say we get a "widget" class with a "Price" property (getter & setter) and a "PriceAfterDiscount" property that only has a getter which takes Price and gives 60% off ("get { return this.Price * 0.6; }".

    Using the client-side entity does not provide any "type-safety" so if we change the property name from "Price" to "PriceBeforeDiscount" there's no way of knowing what code just broke (during compilation that is).

    Additionally, If we create a client-side "widget" class with a Price of 100, the PriceAfterDiscount client-side property will show 0 (uninitialized) as the client-side data class doesn't have any of the server-side logic.

    The correct solution is there for to "translate" the C# Business entities to their Counter-part Javascript prototypes. One option is using Script# and add it a "LoadDataEntity(entity)" function. Therefore other approaches, but that one is a good start.

    Regarding List, AJAX web methods cannot receive Strongly-typed List, Only return those. That's because Javascript has no correlating type to a strongly-typed collection.

Comments have been disabled for this content.