Communicating using web services is in many ways similar to the method from the previous section. Being another XML-based communication method, SOAP-based web services boast a convenient advantage over simple HTTP services, which has built-in support for types. The user can make use of the type information provided by the WSDL descriptor. Common data types can be converted seamlessly between SOAP, .NET, and ActionScript, thus simplifying the data exchange. To the programmer, this means that no XML parsing has to be done on either the Flex or.NET side. As we will see though in later sections, it comes at the price of additional overhead, and ultimately translates into the poorest performance of all three methods on large data sets. At the same time, if the size of the transmitted data is not too big, it should not be considered a big performance drawback. Setup and data exchange are also easy with web services, and you may especially want to consider web services if you would like to interface with a pre-existing web service as often such a service may already be available and tested.
Type support by web services ensures type consistency across both platforms. Some of the supported common types are listed in the table below:
| .NET | SOAP | ActionScript |
|---|---|---|
String |
xsd:string |
String |
Int |
xsd:int |
Int |
Float |
xsd:float |
Number |
Bool |
xsd:boolean |
Boolean |
Array |
e.g.
xsd:string[] |
mx.collections.ArrayCollection |
Table 2. Some of the supported common types
Custom type support is not currently possible with Flex 2
but will be possible using the class called SchemaTypeRegistry, which is currently in the
Flex 3 beta. Sending complex .NET objects though such as DataTable to Flex is possible today with
some hacks described in the next section.
As mentioned, one advantage of web services is the fact that they are widely available, and are very easy to create and deploy. Web services are more popular than HTTP services or Remoting, so the likelihood of having access to a public web service (which happens to be written in .NET) is greater. In .NET, most of the code is generated automatically, including the proxy and the necessary function tags. The communication part is being taken care of by .NET (see Figure 2).

Figure 2. Web services communication model
In this section, we will show how to set up the connection between a .NET web service and the client Flex code. All the code was tested in Flex 2 unless specified otherwise. Among other new features of the Flex Builder 3 beta is a new WSDL import wizard which generates all the data objects as well as the proxy and auxiliary classes. This simplifies calling .NET web services and provides strongly-typed parameters and results.
On the client side, SOAP messages are handled by the WebService component which is itself written in ActionScript. As usual, you can work with the WebService component either in MXML or ActionScript. No matter which way you choose, consuming a web service from Flex is very straightforward. We will start with MXML. The first step is declaration:
<mx:WebService id="webService"
wsdl="http://localhost/WebService/RosterWebService.asmx?wsdl">
<mx:operation name="GetRoster" result="webServiceResultHandler(event)">
<mx:request>
<numberOfStudents>
{studentStepper.value}
</numberOfStudents>
</mx:request>
</mx:operation>
</mx:WebService>
The wsdl field of the WebService object should point at the WSDL definition of the web
service that we are going to consume. The <mx:operation> tag specifies a method
supported by the web service as well as a handler function, which will be
invoked automatically when the response is received. Since the GetRoster() method
takes the number of students as a parameter, the nested <mx:request> tag holds a
parameter of type integer, named numberOfStudents. In this case, instead of using a
literal constant, the parameter value is wired to a numeric stepper component
using databinding. It is important to note that the parameter name in the
nested tag matches the parameter name in the web service; otherwise the method
call won't succeed.
Invoking a method in ActionScript can be done by calling
either serviceName.methodName.send() or serviceName.methodName(),
which do the same thing most of the time. The only exception when you must use
the send() function is when the parameters are explicitly defined in MXML (like above). In
that case, the send() function will take no parameters.
In our example, the method is being called from a function
named callWebService(),
and the result is being handled in a function named webServiceResultHandler(). Here is the
code for the function that calls the web service and the corresponding result
handler:
private function callWebServiceClassData():void
{
webService.GetRoster.send();
}
private function webServiceResultHandler(event:ResultEvent):void
{
var result:ClassData = new ClassData();
result.ClassName = event.result.ClassName;
result.TeacherName = event.result.TeacherName;
result.TeacherID = event.result.TeacherID; var students:* =
event.result.Students;
result.Students = students as ArrayCollection;
if (result.Students == null)
{
result.Students = new ArrayCollection([students]);
}
updateUI(result);
}
To recreate the ClassData object, we have to copy the fields one by one.
Fortunately, we do not need to do XML parsing because all of those fields are
strongly typed. There is a special case that needs separate handling, though.
In Flex 2, ResultEvent objects represent ArrayCollections of size one as an ObjectProxy and not as an ArrayCollection with one element. Since an ObjectProxy object cannot be converted to an ArrayCollection with
one element automatically, that object has to be added to a new ArrayCollection manually.
In our case, you have to check that event.result.Students can be converted to an ArrayCollection, and
if the result of the conversion is null, you should treat event.result.Students as an ObjectProxy.
This issue should be fixed in the final version of Flex 3, as it is already fixed in its
current beta release, where ArrayCollections with one element remain ArrayCollections.
In Flex 3 beta, we could avoid the extra checks by simply doing:
result.Students = event.result.Students;
Casting event.result to type ClassData would be convenient, but is not possible in Flex 2, since SOAP does not support
custom data types. As mentioned before, the currently available beta release of
Flex 3 adds a new class, SchemaTypeRegistry,
which allows to convert the return values to custom types without manually
copying the fields like we did. The aforementioned WSDL import tool uses
SchemaTypeRegistry in the generated classes. Since it is still in beta
and does not always work correctly yet, we will not cover it in the article.
Finally, the last line, updateUI(result), populates the list of students and updates the labels in
the user interface. Alternatively, the result variable could have been declared as an instance variable and used data binding.
Even though it is possible to have the networking code scattered among several files, it is usually a better idea to encapsulate it in a custom communication class, thus making the programmer's task of making changes easier. For the sake of simplicity, this example is written without creating such a class.
On the .NET side, creating and
returning a ClassData object is similar to
the previous IHttpHandler implementation:
[WebMethod]
public ClassData GetRoster(int numberOfStudents)
{
ClassData sampleClass = new ClassData();
sampleClass.ClassName = "Science";
sampleClass.TeacherName = "Smith";
sampleClass.TeacherID = 10;
sampleClass.Students = new List<StudentData>();
for (int i = 0; i < numberOfStudents; i++)
{
StudentData studentOne = new StudentData();
studentOne.StudentID = i + 1;
student.StudentName = (i%2 == 0 ? "Denis" : "Alexey");
sampleClass.Students.Add(studentOne);
}
return sampleClass;
}
After creating a ClassData object, its fields are populated with data, and
the student list is generated on the fly in a similar manner. Depending on
whether the current index is odd or even, the name field is set to Denis or Alexey. In contrast
with HTTP services, both the parameter and the return value are strongly typed
here and no XML handling has to be done. The parameter is an int, and the ClassData object is
returned to the client in a simple return statement. With HTTP services, it was necessary to
explicitly parse the parameter as an integer and serialize the return value as
a ClassData object. As you can see, you don't have to worry
that with web services. Another difference that should be pointed out is that a
single .asmx page can contain multiple methods, whereas the HTTP service
mechanism only allows one method per page.
As we said before, DataTable objects can be sent from .NET 2.0 web services
to Flex relatively easily if we resort to some hacks. To explore that, we will slightly
alter the example. Here, neither ClassData nor StudentData will be required. We will also add a second
method to the web service definition called GetDataTable:
<mx:operation name="GetDataTable" result="dataTableResultHandler(event)">
<mx:request>
<numberOfRows>
{studentStepper.value}
</numberOfRows>
</mx:request>
</mx:operation>
The remote method GetDataTable has a single paramater which is the number
of rows to be included in the resulting DataTable. The return value will
contain student names and their ID numbers, represented as strings and
integers, respectively, and stored as table rows. The UI function callWebServiceDataTable() invokes the remote method:
private function callWebServiceDataTable():void
{
webService.GetDataTable.send();
}
Besides that, a new handler function is necessary:
private function dataTableResultHandler(event:ResultEvent):void
{
var dataArr:ArrayCollection;
if (event.result.diffgram == "")
{
dataArr = new ArrayCollection();
}
else
{
var table:* = event.result.diffgram.DocumentElement.ClassRoster;
dataArr = table as ArrayCollection;
if (dataArr == null)
{
dataArr = new ArrayCollection([table]);
}
}
//update the UI
studentGrid.dataProvider = dataArr;
}
That is arguably the trickiest part of the process. The
resulting data is nested inside a structure called a diffgram that .NET uses to represent DataTable objects in
SOAP. In Flex, the data representation differs depending on how many rows there
are in the table: zero, one, or more than one. If there are no rows in the DataTable, the value
of the structure will have to be compared to the empty string to make sure you
don't get a null reference exception. If there is one or
more rows, the table can be obtained by calling event.result.diffgram.DocumentElement.ClassRoster, where ClassRoster is
the value of the TableName field of the remote table. It is not the name of the remote DataTable object,
which happens to be table and not ClassRoster, as you will see below. If there is more than one row, you
are fine, as the result will be an ArrayCollection, and if there is exactly
one, it will be an Object,
meaning that the table has to be created accordingly: new ArrayCollection([table]).
The additional .NET method is placed in the same class as
the GetRoster() method:
[WebMethod]
public DataTable GetDataTable(int numberOfRows)
{
DataTable table = new DataTable();
table.TableName = "ClassRoster";
DataColumn column;
column = new DataColumn();
column.DataType = typeof(int);
column.ColumnName = "StudentID";
table.Columns.Add(column);
column = new DataColumn();
column.DataType = typeof(string);
column.ColumnName = "StudentName";
table.Columns.Add(column);
for (int i = 0; i < numberOfRows; i++)
{
DataRow row = table.NewRow();
row["StudentID"] = i;
if (i % 2 == 0)
row["StudentName"] = "Denis";
else
row["StudentName"] = "Alexey";
table.Rows.Add(row);
}
return table;
}
In the function above, a table with two columns is being
created, and rows are generated depending on the value of the passed parameter numberOfRows. It is
crucial to specify the table name using the TableName property, because otherwise the
table will not be serializable.
All in all, web services are a convenient and easy-to-use tool,
though sometimes limited by its relative performance to the other communication
methods. The type support provided by the SOAP protocol and its popularity can
desirable, but the protocol heaviness caused by the extra type information
included in the packet makes web services inappropriate for some applications.
Web services seem to be very convenient for calling functions whose return
value and parameters are of simple types such as int, String, boolean, and Array. Given the
similarity between HTTP and web services, the choice between them primarily
depends on which service type is already available.