25 February 2008
Intermediate
Complex types are one of the most commonly used data structures for exchanging data between Flex clients and Microsoft .NET applications. A complex type represents an entity from the application domain. For example, an order entry system might have these entities represented as classes such as Order, OrderItem, Customer, Invoice, and so on. A well-designed, object-oriented Flex application uses instances of these classes to exchange data between the client and the server. Additionally, the client-side application logic may rely on these business objects. As a result, it is important for the classes to be present in both client and server environments. This article reviews the techniques for establishing mappings between complex types from the client program and the corresponding classes from .NET.
One of my previous articles, Invoking .NET objects using the Flex RemoteObject API, describes how to invoke .NET methods from Flex. I recommended that you follow the steps in that article first, to ensure your development environment is configured to enable invoking .NET methods from Flex.
The examples and solution demonstrated in this article uses WebORB for .NET, a product by Midnight Coders that facilitates connectivity between Flex and .NET. WebORB functions as a gateway between Flex clients and .NET objects. Using WebORB, your Flex applications can use the standard remoting API to communicate with .NET applications.
Flex applications rely on complex types to describe entities from the application domain. An instance of a complex type may be sent to or retrieved from the server. Establishing class mappings significantly simplifies client-server integration. In addition, developers do not have to spend time on writing code for converting mapped data structures received from the server to the corresponding client classes since the task is entirely automated by the Flex runtime. The same process applies to the objects sent by the client – WebORB automatically creates corresponding server-side objects.
The following example code shows a .NET class (defined in C# and VB.NET) and the corresponding ActionScript class:
using System;
namespace ServerSide
{
public class City
{
// The class uses public fields for brevity.
// It could work with private fields
// and public properties too
public String name;
public String country;
public long population;
// Public constructor is required so instances of
// City can be created when they arrive from Flex
public City()
{
}
public City( String name, String country, long population )
{
this.name = name;
this.country = country;
this.population = population;
}
}
}
Imports System
Namespace ServerSide
Public Class City
' The class uses public fields for brevity.
' It could work with private fields
' and public properties too
Public name As String
Public country As String
Public population As Long
' Public constructor is required so instances of
' City can be created when the arrive from Flex
Public Sub New()
End Sub
Public Sub New(ByVal name As String, ByVal country As String, ByVal population As Long)
Me.name = name
Me.country = country
Me.population = population
End Sub
End Class
End Namespace
The ActionScript version of the class has the same structure (the same fields and, if needed, getters/setters). In this example, the package name is different than the namespace in the .NET class. The difference in the package name is not significant; they can be made the same. Additionally, the class uses the RemoteClass metadata tag to establish a mapping between clientside.City and its remote .NET counterpart:
package clientside
{
[RemoteClass( alias="ServerSide.City")]
public class City
{
public var name:String;
public var country:String;
public var population:Number;
}
}
The RemoteClass metadata tag in the ActionScript class definition establishes a mapping with a .NET class. The alias parameter must specify the full type name of the remote class, in this case ServerSide.City. The metadata tag instructs Flex to materialize instances of the ServerSide.City class received from remote method invocations as clientside.City objects.
The following .NET code illustrates how complex types can be used in an application. The code is a class exposed as a remoting service using WebORB:
using System;
using System.Collections.Generic;
using Weborb.Activation;
namespace ServerSide
{
[SessionActivation]
public class CityService
{
private List cities;
public CityService()
{
cities = new List();
cities.Add( new City( "Dallas", "USA", 1248816 ) );
cities.Add( new City( "Chicago", "USA", 2873790 ) );
cities.Add( new City( "Tokyo", "Japan", 12570000 ) );
}
public void addCity( City newCity )
{
foreach( City city in cities )
if( city.name.Equals( newCity.name ) )
throw new Exception( "City with name " + newCity.name + " already exists" );
cities.Insert( 0, newCity );
}
public List getCities()
{
return cities;
}
}
}
Imports System
Imports System.Collections.Generic
Imports Weborb.Activation
Namespace ServerSide
_
Public Class CityService
Private cities As List(Of City)
Public Sub New()
cities = New List(Of City)()
cities.Add(New City("Dallas", "USA", 1248816))
cities.Add(New City("Chicago", "USA", 2873790))
cities.Add(New City("Tokyo", "Japan", 12570000))
End Sub
Public Sub addCity(ByVal NewCity As City)
Dim city As City
For Each city In cities
If city.name.Equals(NewCity.name) Then
Throw New Exception("City with name " + NewCity.name + " already exists")
End If
Next
End Sub
Public Function getCities() As List(Of City)
Return cities
End Function
End Class
End Namespace
The class maintains a collection of City objects. The Flex client can add a new city and get a list of all stored cities. Notice the argument type for the addCity method is the ServerSide.City class. When the Flex client sends an instance of the ActionScript class clientside.City, WebORB automatically converts it to the server-side type. The client-to-server marshalling rules require a public, no-argument constructor and a literal match between field names in the incoming object and the corresponding fields/properties in the target type. The getCities method returns a generic list of City objects. Because the Flex client uses the RemoteObject metadata tag, elements of the returned array are automatically converted to the client-side type, clientside.City in this example.
The following ActionScript code demonstrates remote method invocations of the CityService class:
import clientside.City;
var cityService:RemoteObject;
cityService = new RemoteObject( "GenericDestination" );
cityService.source = "ServerSide.CityService";
cityService.addEventListener( FaultEvent.FAULT, gotFault );
cityService.addCity.addEventListener( ResultEvent.RESULT, cityAdded );
cityService.getCities.addEventListener( ResultEvent.RESULT, gotCities );
private function addCity():void
{
var city:City = new City();
city.name = "London";
city.country = "UK";
city.population = 10000000;
cityService.addCity( city );
}
private function cityAdded( evt:ResultEvent ):void
{
trace( "city added, refreshing cities list" );
getCities();
}
private function getCities():void
{
trace( "getting all cities" );
cityService.getCities();
}
private function gotCities( evt:ResultEvent ):void
{
trace( "received all cities" );
// display in a data grid
cities.dataProvider = evt.result;
}
The invocation sequence begins in the addCity() method. The code creates a new city object and sends it to the server using the cityService.addCity( city ) call. When the server responds, Flex invokes the cityAdded result event listener. The cityAdded method then requests a list of all city objects from the server through the second remote invocation: cityService.getCities(). The return value for getCities() is a collection of server-side City objects. This is where class mappings come into play. The RemoteClass metadata tag instructs Flex to map ServerSide.City instances to clientside.City. As a result, the gotCities( evt ) method receives a collection of clientside.City objects.
Class mapping is a very important and useful feature. It must, however, be well understood before it is put to the appropriate use. The following are the most common mistakes developers make with class mapping:
flash.net.registerClassAlias( aliasName:String, classObject:Class):voidThe API call for the example in this article would be:flash.net.registerClassAlias( “ServerSide.City”, clientside.City )
public void processOrders( ArrayList orders )
{
// …
}
When Flex invokes processOrders and sends a collection of Order objects to the server, WebORB will not know what data type the “orders” ArrayList should contain. To facilitate the mapping between the client-side Order class and the corresponding type from the server, WebORB provides its own class mapping mechanism. To create a class mapping, register the following XML in the product configuration file (weborb.config):
<classMapping>
<clientClass>com.client.Order</clientClass>
<serverClass>com.server.Order</serverClass>
</classMapping>
Here, com.client.Order and com.server.Order are the names of the client and server classes, respectively. The mapping instructs WebORB to materialize objects received from the client as objects of the corresponding server-side types.
Class mapping is a powerful technique; it enforces a better designed client-server data exchange model and saves valuable development time. With class mapping in place, it is easier to trace client-server data flow and debug distributed applications.
For more information on the topic, refer to the following additional resources: