Rich clients (Flex/AIR/Silverlight) & LINQ : data across the pipe

19 03 2009

I’ve blogged before about Flex & LINQ, but I’m always learning new things, and I wanted to share some of my most recent findings.

Note: this article focuses on Flex & LINQ, but it could as easily be applied to Silverlight & LINQ as well. Silverlight 3 is set to merge client & server with .NET 4.0, but I believe the issue of how much and when to send data between client & server will continue to be relevant.

First off, why LINQ?

  1. With LINQ on your server layer, you can avoid having to write your server value objects – LINQ to SQL will do that for you from your DB schema – allows you to quickly updates these objects if your schema ever changes (and let’s face it, it will). NOTE: unfortunately, there is no free & easy way to sync your LINQ to SQL context to your schema in VS2008).
  2. You can put all your SQL as strongly-typed code in your DAL rather than having to call the DB to do it via stored procs, which decouples your DB from your application, allowing you to switch databases if you ever wanted to, and keeps all your server code in one place. 
  3. Cleaner and shorter code – easier for you, easier for other developers to understand (as long as they understand LINQ 🙂 )

So, if you’ve decided to take the LINQ option and your a Flex or AIR developer, then there is good news. 

Products such as WebORB.NET and FluorineFx both support .NET 3.5 and will allow your Flex application to send Remote Objects back and forth between the two. Yes you could use Web Services to do communicate with the server, but I’m assuming you’ve read up on the performance benefits of remoting and AMF vs Web Services, and you’re opting for speed.

The question is however, how do you do it?

The LINQ to SQL tool (I’m not going to mention the ADO.NET Entities tool, which is more feature rich, but a touch more complicated) creates you a data context as you know. This remembers the actions you perform on your objects and will propagate the changes back to the DB when you “SubmitChanges()”. However, when you’re working on the Flex/.NET model, you’re now using LINQ on an middle-tier (or even an n-tier) application, and that changes things.

What changes is that the “context” will be a new instance between each request (unless you use session access to the RemoteObjects, which I won’t get into here). LINQ won’t know that you changed something outside this context, and so to Update an object in the database you will need to “Attach()” it back to the current context to save it, along with it’s original but from a new  context. (See examples below).

Now, there are plenty of articles out there on LINQ in the middle tier and LINQ with n-tier, so I won’t reinvent the wheel. What I will do is talk about strategies on how to leverage LINQ in your Flex app, and how to synchronise data.

Let’s take a think about possible methodologies for sending/receiving related object data from server to client:

  • Safety method: take a little save a little
  • Hungry method: take a little save lots
  • Greedy method: take everything save lots
  • Thrifty method: take everything save a little

I’ll use the following example to illustrate the different methods:

Example Schema

 

Safety Method

The safety method is a simple-to-follow, clear 1:1 system  – load an object when you need it, and load the associated objects as you need them. When you save data back, call the server with either one or a collection of single unrelated objects. You rely on your foreign keys to do all the loading.

All associations within the LINQ to SQL designer (DBML) need to have INTERNAL access, to prevent them being sent to the client, and so that you can still use them within the server code. 

For example: Your client wants the list of projects? It loads them simply via LINQ. Client wants the project’s list of Companies? Load them via the ProjectID. Need to save a Company, send the server the Company object with an associated ProjectID. 

 

Hungry Method

The hungry method involves retrieving just the data objects you require at the time and building up the object model on the client side, eventually sending back an object with related objects. This means you don’t have an excessive number of save calls back to the server, and you don’t rely on the foreign key, you rely on the attached object.

For example:   You may want to load all Projects within the system via LINQ, but don’t want the associated Companies, Employees or Suburbs. You then want to create a new Project and create Companies, Employees. You then call one Save() method to save the new Project, and let LINQ create the entire collection of Company and Employee records.

Now, by default, LINQ will try to send you all the related Companies, Employees and related Suburbs when you ask for the list of Projects.  You have two main options to avoid this:

  1. Change the ACCESS of the association(s) between parent and child within the DBML (LINQ to SQL) editor to INTERNAL. This means only inside the DLL could you access this property.
  2. In your server DLL, NULL any related properties before returning the data to the client. (Literally: project.Companies = null).    

Do you see the problem? Option 1 is the obvious choice but then it means you couldn’t actually send the data back to .NET because if you tried to send a Project back with Companies attached, .NET would say that Project doesn’t have the Companies property as the association as it is internal (and the exception happens during unmarshalling on the server, handled by either the WebORB or FluorineFx DLL).

The Hungry method starts to become a real issue with visibility, and unfortunately, the easiest is to follow option 2 above, which is not a good solution in my opinion.

NOTE: When saving back to the server, do NOT send back both an associated object and an associated foreign key. This will cause .NET to except. EG: If you were to save a Company with an attached Project, you couldn’t also send back Company’s ProjectID (the FK), .NET wouldn’t know which association to use. 

So, in your client code, either remove the Foreign Keys completely from the value object, or use a property getter only to get the foreign key from the association object.

 

Greedy Method

The greedy method is expensive. You are always sending and receiving all (or a lot) of the data in a single call. The advantage is, less trips between the client and server, and minimal client & server code (one Save method to do everything!).

As you can imagine, debugging the Save method can be a headache – as debugging LINQ usually is 🙂 

Now, to Update an object under the greedy method is tricky yet fun. You need to Attach() this new object to the context, along with it’s original from yet another context. 

For example: You want to load all Employees and their Suburb plus their Company and it’s Project. If you wanted to save it back, you’d need to attach it back in. 

Employee SaveEmployee(Employee e) 
{
  DataClassesDataContext context = new DataClassesDataContext();

  //if is Update
  if (e.EmployeeID > 0) 
  {
     //need to attach Employee with a NEW INSTANCE of the DataContext
     Employee oldEmployee = new DataClassesDataContext().Employees.Single( p => p.EmployeeID == e.EmployeeID);

     //now attach it
     context.Attach(e, oldEmployee);
  }
  else
  {
     context.Employees.InsertOnSubmit(e);
  }
  context.SubmitChanges();
  return e;
}

 

This can get even tricker when you’re attaching objects with children which already exist. It can require going through the children and testing their Primary Keys using the method above.

 

Thrify Method

The thrifty method involves receiving all the required data, and just sending back the minimal amount of objects back to the server for a successful save. 

To prevent data being sent back to the server once it is already in the Flex client, requires the use of the Flex metatag: [Transient]. To use Thrifty, we need to rely on Foreign Key associations as LINQ will use the FK on the insert  (just like saving in the Safety method). The advantage is that you have complete control over anything being saved into your database, which can be a real advantage in a intricate & lengthy project. 

Generally using this method, you would need to recall all of your data again, as some associations have been modified. You can try an sychronise these on the client, but it can get VERY tricky. At the very least, you’ll need to return your saved object to get it’s ID if it was just inserted.

For example: You want to load all Projects from the system, but when you save a Project, you don’t t send back it’s Companies, this property is Transient on the client and doesn’t get send back to the server. If you wanted to save a Company, you’d send back a Company object without the associated Project object, but WITH the ProjectID Foreign Key.

 

I personally use a combination of the Thrifty and the Greedy method, deciding when I want to send all the data back, and when I just want to send back an individual object. It still means however, that I need to reload ALL of my data again after a successful save of some object. 

The most important things to remember is attaching to a new context, and not sending both a foreign key and a related foreign object back to the server simultaneously – this upsets LINQ. 

More examples and further investigation to come…





Flex: Dynamic, bindable value objects

10 10 2008

What a mouthful. Dynamic, bindable value objects.

Well, practically these puppies are pretty useful.

Lemme give you a scenario:

You are writing an Employee database that you wish to use for various Companies. Say that each Company wants custom fields for employees (on top of the essential fields). Sure, you could add another column to the Employee table “CustomFields” which is XML data and put the fields in there. However, my preferred method is to have two extra tables – one a list of available fields for the company, and the other an assignment of values for these custom fields to an employee record.

eg:

Example Database Schema

At this point I will clarify that I use .NET 3.5 and LINQ to pull out this data. That means that my Employee object will automatically be given the Company property and the EmployeeCustomValues[] property. Please see my other articles on Flex and .NET 3.5.

Now, to get this into Flex.

I’m using FluorineFx’s most recent revision (r33) posted on Sep 30 in their Google SVN (at time of writing, the most recent version for DL on the FluorineFx website is v1.0.0.13, which won’t handle dynamic objects for our purposes).

I also tried with WebORB 3.5 but was getting exceptions in sending this data back to .NET and still haven’t heard back from them regarding dynamic value objects.

Getting this data into the Employee.as file isn’t that hard. You write a normal value object, but you make it dynamic and you will need to extend Proxy to make it bindable. See this solution here. (I’ve also read this solution in the Flex 3 Cookbook).

Now the fun part, in Employee.as, I now need to also implement some more methods of Proxy, because I want to use these value objects inside Datagrids. First off I need to get all the properties within this object for enumeration. This means, in the Employee constructor, I’ll need to get all the strong properties and add them to my list. I do this using the describe type utility.

[Bindable(event=“propertyChange”)]

[RemoteClass(alias=“JustinJMoses.Examples.DynamicBindableVO.Employee”)]

dynamic public class Employee extends Proxy implements IEventDispatcher

{

private var _evtDispatcher:EventDispatcher;

private var _properties:Array;

public function Employee():void

{

_evtDispatcher = new EventDispatcher(this);

_properties = new Array();

var classInfo:XML = flash.utils.describeType(this);

for each (var a:XML in classInfo..accessor)

{

addProperty(a.@name.toString());

}

}

….

Then, as I add dynamic properties in the getProperty() and setProperty() I will also append them to my _properties private array. This means that enumerating my object will now yield both my strong properties and my dynamic ones:

//because this class override Proxy, it must implement the iteration over this object’s properties

override flash_proxy function nextNameIndex (index:int):int {

if (index < _properties.length) {

return index + 1;

} else {

return 0;

}

}

//as above

override flash_proxy function nextName(index:int):String {

return _properties[index – 1];

}

override flash_proxy function nextValue(index:int):*

{

return this[_properties[index – 1]];

}

But there’s still a bit more. The callProperty() method is also needed for all these toString() calls that happen around the shop. My implmentation ins’t great I confess, but I found that without it, the rows in my Datagrids weren’t selectable as the UID created for each row is a toString() of the datarow’s item.

//this implementation of toString is required as a datagrid’s list items require it

override flash_proxy function callProperty(name:*, … rest):*

{

if (name.localName == “toString”)

{

var str:String = “”;

for each (var field:String in this)

{

if (field != null)

str += field.toString();

}

return str;

}

}

 

Now, as I mentioned, this dynamic value object – Employee – will only get sent back to .NET successfully in the most recent revision of FluorineFx, but this should be remedied soon.

Remember, in the implementation of get and setProperty() in Employee, you still have to manage the storage of your values into the array collections sent from .NET.

I’ve written out an abstract DynamicProperties class and my Employee solution below.

DynamicProperties.as

[Bindable(event="propertyChange")]
    dynamic public class DynamicProperties extends Proxy implements IEventDispatcher
    {

        private var _evtDispatcher:EventDispatcher;

        private var _properties:Array;

        public function DynamicProperties():void
        {
            _evtDispatcher = new EventDispatcher(this);

            _properties = new Array();

            var classInfo:XML = flash.utils.describeType(this);

             for each (var a:XML in classInfo..accessor)
             {
                addProperty(a.@name.toString());
            } 

            //if you want to listen to property changes
            //this.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onChanged,false,0,true);
        }

        override flash_proxy function getProperty(name:*):*
        {
            return getDynamicProperty(name);

        }

        public function addProperty(name:String):void
        {
            _properties.push(name);
        }

        protected function getDynamicProperty(name:*):*
        {
            //abstract
            throw new Error("Abstract");
        }

        //this override allows us to dispatch a binding event when change
        override flash_proxy function setProperty(name:*, value:*):void
        {
            var oldVal:* = setDynamicProperty(name, value);

            var evt:Event = PropertyChangeEvent.createUpdateEvent( this, name, oldVal, value );

            dispatchEvent( evt );
        }

        /**
        * Abstract.
        *
        * This function will set the property of a dynamic
        */
        protected function setDynamicProperty(name:*, value:*):*
        {
            throw new Error("Abstract");
        }

        //because this class override Proxy, it must implement the iteration over this object's properties
        override flash_proxy function nextNameIndex (index:int):int {

             if (index < _properties.length) {
                 return index + 1;
             } else {
                 return 0;
             }
         }

         //as above
         override flash_proxy function nextName(index:int):String {
             return _properties[index - 1];
         }

         override flash_proxy function nextValue(index:int):*
         {
             return this[_properties[index - 1]];
         }

         //this implementation of toString is required as a datagrid's list items require it
         override flash_proxy function callProperty(name:*, ... rest):*
         {
             if (name.localName == "toString")
             {
                 var str:String = "";

                 for each (var field:String in this)
                 {
                     if (field != null)
                         str += field.toString();
                 }

                 return str;
             }
         }

        // IEventDispatcher implementation.
        public function addEventListener( type:String,
                                        listener:Function,
                                        useCapture:Boolean = false,
                                        priority:int = 0,
                                        useWeakReference:Boolean = false):void
        {
            _evtDispatcher.addEventListener( type, listener, useCapture,
                                               priority, useWeakReference );
        }
        // IEventDispatcher implementation.
        public function removeEventListener( type:String,
                                            listener:Function,
                                            useCapture:Boolean = false ):void
        {

           _evtDispatcher.removeEventListener( type, listener, useCapture );
        }
        // IEventDispatcher implementation.
        public function dispatchEvent( evt:Event ):Boolean
        {
            return _evtDispatcher.dispatchEvent( evt );
        }
        // IEventDispatcher implementation.
        public function hasEventListener( type:String ):Boolean
        {
            return _evtDispatcher.hasEventListener( type );
        }
        // IEventDispatcher implementation.
        public function willTrigger( type:String ):Boolean
        {
            return _evtDispatcher.willTrigger( type );
        }

    }

And my final Employee.as file

[Bindable]
    [RemoteClass(alias="JustinJMoses.Examples.DynamicBindableVO.Employee")]
    dynamic public class Employee extends DynamicProperties implements IValueObject
    {

        public var EmployeeID:int = 0;

        public var CompanyID:int = 0;

        /**
         * These are the two implementations of the dynamic properties class.
         *
         * They allow dynamic properties to be getted/setted via some implementation.
         */

        override protected function getDynamicProperty(name:*):*
        {
            for each (var value:EmployeeCustomValue in this.EmployeeCustomValues)
            {
                if (value.EmployeeCustomField.FieldName == name.localName)
                {
                    return value.FieldValue;
                }
            }

            return null;
        }

        override protected function setDynamicProperty(name:*, value:*):*
        {
            var oldVal:*; 

            for each (var fieldValue:EmployeeCustomValue in this.EmployeeCustomValues)
            {
                if (fieldValue.EmployeeCustomField.FieldName == name.localName)
                {
                    oldVal = fieldValue.FieldValue;
                    fieldValue.FieldValue = value;

                    return oldVal;
                }
            }

            //can't find this value for the object, so find the field object and create a new associated value
            var allFields:ICollectionView = MyModelLocator.getInstance().dynamicEmployeeFields;

            if (allFields.length < 1)
            {
                throw new MyError(MyError.EMPLOYEE_CUSTOM_FIELDS_NOT_LOADED);
            }

            //search through available field names
            for each (var field:EmployeeCustomField in allFields)
            {
                if (field.FieldName == name.localName)
                {
                    var employeeCustomValue:EmployeeCustomValue = new  EmployeeCustomValue();
                    employeeCustomValue.Employee = this;
                    employeeCustomValue.EmployeeCustomField = field;
                    employeeCustomValue.FieldValue = value;
                    this.EmployeeCustomValues.addItem(employeeCustomValue);
                    return null;
                }

            }

            throw new MyError(MyError.NO_EMPLOYEE_FIELD);

        }

        public function Employee():void
        {
            EmployeeCustomValues = new ArrayCollection();
        }

        private var _employeeCustomValues:ArrayCollection;

        public function get EmployeeCustomValues():ArrayCollection
        {
            return _employeeCustomValues;
        }

        public function set EmployeeCustomValues(val:ArrayCollection):void
        {
            _employeeCustomValues = val;

            for each (var field:EmployeeCustomValue in EmployeeCustomValues)
            {
                //set the property (so it shows up)
                this[field.EmployeeCustomField.FieldName] = field.FieldValue;

                //add the property to the internal list
                this.addProperty(field.EmployeeCustomField.FieldName);
            }

        }

    }




Flex, .NET 3.5 with LINQ to SQL

23 09 2008

March 2009: For more information see the latest article on FLEX & LINQ:

https://justinjmoses.wordpress.com/2009/03/19/rich-clients-flex-air-silverlight-linq-data-across-the-pipe/

——————————————————————————-

I had certainly delayed looking at LINQ for too long.

After seeing a screencast on some of the new features in ASP.NET I noticed the very handy LINQ to SQL item.

Well.

Let’s just say I’m not using strongly typed datasets anymore.

LINQ provides strongly typed access to your database (tables, views, sprocs and function) while also allowing you to query those results.

Whilst many still advocate the use of stored procs for larger and more advanced or intensive queries, at least some of the simple overhead can be reduced. Furthermore, updating your C# classes to reflect your DB is as simple as updating the LINQ to SQL item.

But the major advantage from a Flex remoting perspective is how easy it is to have all this returned data in your client.

Say I have three tables: (already linked with relationships via foreign keys)

Employee, Company & Position

I add the LINQ to SQL item to my solution. Inside, I connect the LINQ object up to the db and drag in my database objects. Voila, I have the strongly typed classes: “Employee”, “Company” and “Position”. None of this DataRow, DataTable, DataAdapter business.

Using LINQ syntax, I can do simple queries on the spot, like:

DataClassesDataContext db = new DataClassesDataContext();

var data = from e in db.Employees where e.CompanyID == someCompanyID select p;

List<Employee> employees = data.ToList<Employee>();

Now, I can return this list to either FluorineFx or WebORB and the ArrayCollection that the List is serialised into will contain an array of Employee Value Objects (my Employee.as file). Nothing special there.

BUT, the “Company” property is also provided. This is the Company property that is automatically generated in LINQ to SQL that provides the foreign key reference to the company.

This holds for all your foreign keys.

So if you want to reduce your overhead from a C# (writing classes for your db objects) and DB perspective (writing sprocs for every data call), then take a gander at .NET 3.5.

NOTE:

  • If you weren’t aware, .NET 3.5 framework is actually an addition to .NET 2.0. This means that once you install the framework on a machine, no manual IIS settings are required (as opposed to the 1.1 to 2.0 switch) to activate 3.5 on a website or virtual directory.
  • Both FluorineFx and WebORB support .NET 3.5




FluorineFx and WebORB coding differences part1

3 09 2008

I thought I’d compile a list of differences between coding an Flex .NET remoting solution in FluorineFx and WebORB.

First off, the versioning:

FluorineFx: 1.0.0.13

WebORB (.NET): 3.5

Now, for various features/actions. These are typically in no order:

1. Registering a DLL to work in remoting

both: deploy the DLL in the “bin” folder (alongside “FluorineFx.dll” and “weborb.dll” respectively)

2. Destinations/Channels (in the WEB-INF/flex folder)

WebORB: Uses a “GenericDestination” that is exposed to all classes in all DLLs alongside the

<destination id="GenericDestination">
<properties>
<source>*</source>
</properties>
</destination>

Uses a set of channels including the default AMF channel

<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="weborb.aspx" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>

FluorineFx: Uses the “fluorine” destination that is open to all classes exposed as remoting (see point 3 below).

<destination id="fluorine">
<properties>
<source>*</source>
</properties>
</destination>

Uses a set of channels including the default AMF channel

<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/Gateway.aspx" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
</properties>
</channel-definition>

NOTE: Due to a bug in Flex Builder 2 & 3, you must “Clean” your project to register and changes in the WEB-INF xml files.

NOTE: You can see that the “ContextRoot” setting is important in FluorineFx. If you aren’t running off the root domain, but in some other folder / virtual directory, you need to set up the Context Root to match that folder structure. ie. If you’re testing in “http://localhost/TestFolder&#8221; then you must set your Context Root in Flex Builder (Project Properties) to be “TestFolder”.

3. Exposing classses/methods from a DLL to Flex.

WebORB: WebORB will expose all namespaces/classes/methods from a destination by default.

This destination, when combined with the “source” property on a remote object, will expose everything inside your DLLs to the web. This can be toggled in the weborb.config file (under security->deploymentMode) to “closed”.

FluorineFx: Only those classes compiled with the [RemotingService] Attribute in C# will be exposed via FluorineFx, and even then only their public methods. NOTE: that class must be using the “FluorineFx” namespace.

4. Using SSL remoting

WebORB: The SSL channel is there by default “my-secure-amf”. If you ran your app from HTTPS, it would work by default as both the normal and the HTTPS channels are defaults. Alternatively (and for best practises), you can set up a destination to use it:

<destination id="MyDestination">
<channels>
<channel ref="my-secure-amf" />
</channels>
<properties>
<source>My.Name.Space.MyServiceClass</source>
</properties>
</destination>

FluorineFx: Doesn’t come bundled with a secure channel, but can do it easily. You just need to add the following to your services-config.xml file

<channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
<endpoint uri="https://{server.name}:{server.port}/{context.root}/Gateway.aspx" class="flex.messaging.endpoints.SecureAMFEndpoint"/>
</channel-definition>

Then you can add a destination in remoting-config.xml that uses “my-secure-amf” (see the WebORB solution above).

5. Manage security

WebORB: The WebORB console is fully featured to allow you to setup which services can be exposed to which users. You can limit a namespace, class or method by role, hostname, ip range or single ip (allowing masks). Essentially this just adds security configuration data to weborb.config but it’s handy.

To implement custom authentication and authorisation, you must implement Weborb.Security.IAuthenticationHandler and Weborb.Security.IAuthorizationHandler respectively (and add a reference to this classes in your weborb.config file under the security setting). Then you should be able to use the setCredentials() method on your remote object.

FluorineFx: Uses a similar approach to security, but it’s all done in the remoting-config.xml file instead. You need to manually add the constraints and apply them to destinations.

<destination id="MyService">
    <channels>
      <channel ref="my-amf" />
    </channels>
    <properties>
      <source>My.Name.Space.MyServiceClass</source>
    </properties>
    <security>
      <security-constraint ref="privileged-users"/>
    </security>   
  </destination>

and then in your services-config.xml file

  <security>
    <security-constraint>
      <auth-method>Custom</auth-method>
      <roles>
        <role>admin</role>
        <role>privilegeduser</role>
      </roles>
    </security-constraint>
    <login-command class="My.Name.Space.MyLoginCommand" server="asp.net"/>
  </security>

Like WebORB, you’ll need to define your own authenticator/authoriser. You must implement the FluorineFx.Security.ILoginCommand interface. You then insert it’s name aboce, in the “login-command” setting above.


6. Data Type Mapping

Both: use the [RemoteClass(alias=”…”)] metadata syntax above your Value Objects in ActionScript to ensure the remoting gateway will return your objects as the correct type.

WebORB: Returns a DataTable as an array of objects (associative arrays representing a row). Returns DataSets as an object (associated array of arrays of associated arrays). Can optionally use Attribute [ReturnType(“namespace.actionscriptClassName”)] before your C# method to tell WebORB how to serialise the data in the DataTable (I can’t get this to work though).

FluorineFx: Must be told directly which type of data the DataTable/DataSet will be returning. Otherwise will return DataTables and DataSets in flat array without column associations.

You’ll need to use Attributes above methods in C#: [DataTableType(string remoteClass)] & [DataSetType(string remoteClass).





WebORB vs FluorineFx

10 03 2008

Updated: September 30, 2008

So, it starts to get interesting here.

I’m noticing some of the sames and some of the stark differences between WebORB, the commercial yet free .NET Flash Remoting tool and FluorineFx, the open source alternative.

To summarise thus far:

Both:

  • Have a Console feature which allows you to see all the classes you are exposing to the remoting client and invoke them.
  • Work with VS2008 (I have only tested the .NET 2.0 framework) and Flex 3.
  • Have a Windows Service feature that allow you to deploy it alongside your AIR application for better client-side functionality (though by nature that must limit them to Windows deployments, no?)

WebORB: (v3.5 for .NET)

  • Is a free product, but commercially owned and closed source.
  • The licensing allows the software to be used in every scenario except redistribution.
  • Has a very comprehensive Console app that handles security on all namespace/class/method levels very nicely.
  • Has very useful codegen tools within the Console to get you going – both for AS and C#/VB.NET.
  • Has reasonable documentation to go along with it (it is still quite lacking in content and organisation)
  • Has a database tool in the Console (WDML) that essentially takes your DB structure and creates a C# or VB.NET Solution interfacing to all tables/views/stored procs. Also creates AS classes to interface with this data.
  • Exposes all of the DLL to the user. However, there is the option in .config file to turn on “closed” method which means you need to turn each service/method you want.
  • Uses a yahoo mail group to communicate between the developers and the users. The support is helpful to a point… they don’t answer every single question on there, but do answer the crux of them generally.

FluorineFx: (v.1.0.0.13 for .NET)

  • Has a Console app also, but with much less functionality than WebORB
  • Has significant multi-framework (Cairngorm, PureMVC, etc) style actionscript codegen.
  • Has documentation but is seriously lacking in examples. Not easy for new users to understand where to go once it’s working.
  • Has a specific implementation of DataSets and DataTables that is not as intuitive as WebORB. A DataTable for example has the “serverInfo” property which when expanded reveals a cursor, the list of columns and the list of data.
  • Has a nice project setup wizard in Visual Studio.
  • Security support for services must all be done manually in the configuration files.
  • Uses C# Attributes to expose classes as Services. (use [RemotingService(“Fluorine sample service”)] above the class definition.
  • Uses a mailing list for communication, and generally everyone is very helpful.
  • Can and will provide quick additions to the code and SVN releases of new versions based on requests/issues in the mailing list.
  • It’s Open Source, so you can actually learn the ins and outs of AMF and remoting. And, if you’re not happy, get in there and modify the code.

I appreciate both products but feel there needs to be a serious overview of documentation, as I’ve read through both and still find myself not 100% sure about the whole system.

Personally, I think Adobe have something to answer for here. I know they are pushing Blaze and I understand that Java-based Blaze is cross-platform and complies to Adobe’s needs… But seriously, C# and Visual Studio are an amazing partnership in developing software, and I won’t give up developing server-side code in them until something as good as it comes out (fingers crossed that Mono keeps plugging away).

I would like to see Adobe step up into this market and help everyone out, because, let’s face it, Flex and AIR don’t meet their potential until they are hooked up into a server-side component.