Mark Piller

Created

25 February 2008

Requirements
Prerequisite knowledge
User Level
Intermediate
Required products
Flex Builder 3 (Download trial)

 
Additional Requirements

 
Microsoft Internet Information Services Server (version 5.x or later)
 
Microsoft .NET version 2.0 or later (installed on the system and integrated into IIS)
 
Visual Studio 2003 or 2005
 
WebORB for .NET (version 3.3 or later)
 
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.
 

 
Class mapping overview

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.
 
Class mapping between Flex client and the server.
Figure 1.Class mapping between Flex client and the server.
 
The following example code shows a .NET class (defined in C# and VB.NET) and the corresponding ActionScript class:
 
C# 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; } } }
 
The same class in VB.NET:


 
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:
 
C# example:


 
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; } } }
 
VB.NET example:


 
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.

 
Caveats and gotchas

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:
  • While the RemoteClass metadata tag establishes a class mapping, if the client-side class is not explicitly referenced in the main code execution path, the Flex compiler entirely ignores the class. As a result, not only will the class mapping not work for the given class, but the class itself will be excluded from the compilation. In this case, you can use the “–include-classes” compiler argument to force the class to be compiled in or use the following API to register class mapping:
     
    flash.net.registerClassAlias( aliasName:String, classObject:Class):void
    The API call for the example in this article would be:flash.net.registerClassAlias( “ServerSide.City”, clientside.City )
  • When the Flex client invokes a method and passes a complex type as an argument, WebORB uses reflection to determine what type the incoming object should be materialized as. In some cases, the reflection may not yield enough information about the server-side type. For example, consider the following method:
 
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.

 
Conclusion

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.

 
Where to go from here

For more information on the topic, refer to the following additional resources: