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);
            }

        }

    }
Advertisements




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




Flex Remoting: channels, destinations and SSL

26 08 2008

If you’re like me, then you’ve gotten well into flash remoting as a solution for Flex Data Messaging. You’re using AMF to allow the creation of remote objects between actionscript on the client and some server language (PHP, Java, .NET, etc).

I’ve been running into troubles with WebORB (v3.5) and setting channels that work.

Essentially, for whatever reason, my compiled app didn’t register the changes in my services-config.xml file that resides in the WEB-INF folder of whatever software you’re using to interface with the server side language. And believe me, I tried it a thousand different ways.

The easiest way to override this faulty behaviour is to set the channels and endpoints inside your flex app when you’re building the remote object.

You can see below a solution in weborb for .NET (hence the weborb.aspx endpoint)

Service.as

————————————————————————–

protected static function init(serviceName:String, isSecure:Boolean = false):void
{

var destination:String;

var remoteObject:RemoteObject = null;

var channelSet:ChannelSet = new ChannelSet();

var channel:Channel;

if (isSecure)
{
destination = “MySecureDestination”;

channel = new SecureAMFChannel(null,”weborb.aspx”);
}
else
{
destination = “MyDestination”;

channel = new AMFChannel(null,”weborb.aspx”);
}

channelSet.addChannel(channel);

remoteObject  = new RemoteObject(destination);

//30s timeout on requests
remoteObject.requestTimeout = 30;

remoteObject.showBusyCursor = true;

remoteObject.source = “My.Name.Space.” + serviceName;

remoteObject.channelSet = channelSet;

}

————————————————————————–

And in my remoting-config.xml file:

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

<destination id=”MyDestination”>
<properties>
<source>*</source>
</properties>
</destination>

<destination id=”MySecureDestination”>
<channels>
<channel ref=”my-secure-amf” />
</channels>
<properties>
<source>*</source>
</properties>
</destination>

———————————————————–

As you first start using the SSL option, you may find you start receiving an error about cross domain security issues between HTTP and HTTPS. The deal is that if you are loading your SWF from HTTP, you require a crossdomain.xml policy file in the root of your website to allow HTTPS access.

From Adobe Flex 3 Help: crossdomain.xml

<cross-domain-policy>

<allow-access-from domain=”*.mydomain.com” secure=”false”/>

</cross-domain-policy>

As the Help mentions, you don’t require the crossdomain policy if you’re calling HTTP from a HTTPS loaded swf.





Flex 3 : FluorineFx : .NET

25 01 2008

Right, I might be sold on this setup I’ve got here.

So, I’m running Flex 3 Beta 3, the new open source remoting tool from the Silent Group called FluorineFx and .NET 2.0 (under Visual Studio 2008). Now, I’d been reading through online docs, and was struggling with persistent remote objects. Basically, I had assumed that after one call to some method in a remote object, the subsequent call would be to the same object, thereby any changes made in the first call would be noticed in the second.

HOWEVER, it turns out (thanku Zoli from the Fluorine mailing list) that by default, services have a “request” scope. If you want to change this, you must modify the remoting config file in the Fluorine .NET gateway.

The code below works as intended when you change the above remote file to include “<scope>session</scope>” under the destination tag of id “fluorine”… interesting…

Note: the code below is a modification of an example by Mark Piller from the Adobe docs on Flex and .NET (pg. 6).

C# Code

City.cs

using System;

 

namespace FlexInterop

{

    public class City

    {

        // The class uses public fields for brevity.

        // It could work with the public properties too

        public String name;

        public String country;

        public long population;

 

        // Public constructor is required so instances of

        // City can be created when the arrive from Flex

        public City()

        {

        }

 

        public City(String name, String country, long population)

        {

            this.name = name;

            this.country = country;

            this.population = population;

        }

    }

}

 

 

CityService.cs

 

using System;

using System.Collections.Generic;

using FluorineFx;

 

namespace FlexInterop

{

  

    [RemotingService(“Fluorine city service.”)]

    public class CityService

    {

        public List<City> cities;

 

        public CityService()

        {

            cities = new List<City>();

            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.Add(newCity);

           

        }

 

        public List<City> getCities()

        {

            return cities;

        }

    }

}

 

 

ActionScript 3.0 Code

City.as

package com.interop

{

 [RemoteClass( alias=“FlexInterop.City”)]

 public class City

 {

  public var name:String;

  public var country:String;

  public var population:Number;

 }

}

 

 

Application.mxml

 

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application creationComplete=”init()” xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute>

      <mx:Script>

            <![CDATA[

                 

                  import com.interop.City;

                  import mx.rpc.events.FaultEvent;

                  import mx.rpc.events.ResultEvent;

                  import mx.controls.Alert;

                  import mx.rpc.remoting.RemoteObject;

                       

                       

                 

                  private var cityService:RemoteObject;

                 

                  public function init():void

                  {

                        cityService = new RemoteObject( “fluorine” );

                        cityService.source = “FlexInterop.CityService”;

                        cityService.addEventListener( FaultEvent.FAULT, gotFault );

                        cityService.addCity.addEventListener( ResultEvent.RESULT, cityAdded );

                        cityService.getCities.addEventListener( ResultEvent.RESULT, gotCities );

                       

                       

                        addCity();

                       

                       

                  }

                 

                  private function gotFault( evt:FaultEvent ):void

                  {

                   Alert.show( “Server reported an error – “ + evt.fault.faultString + evt.fault.faultDetail );

                  }

                 

                  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;

                   }

            ]]>

      </mx:Script>

     

      <mx:DataGrid id=”cities/>

</mx:Application>





Trawling through the Flex Remoting world

24 01 2008

Well, I’ve been a-reading, powering through the offerings available on the communication between .NET and Flex.

If you don’t know why, the reason is that Flex is almost completely a client-side application, and requires a server-side application to handle everything from databasing to file uploading.

Basically, everyone seems to agree on the remoting option rather than HTTP services or WebServices and fair enough. However, a lot of the documentation seems to focus around WebORB which is a pretty fantastic tool from The Midnight Coders. It is a remoting gateway that sits on IIS (requiring version 6, and thereby Windows Server 2003, I later learned) and interfaces with classes you make in .NET. There are open-source options, the main one I have been testing is FluorineFx which seems to be the newer release of Fluorine. However, it doesn’t have all the bells and whistles of WebORB.  But – I can develop on my own machine (running XP), which is a plus as you can imagine.

Essentially, you setup a virtual directory on IIS, running .NET 2.0 and then tell Flex to run from within that .NET application. On the .NET side, you ensure your classes use the right remoting compiler directives, and on the Flex side, ensure your actionscript objects reflect their .NET counterparts. Then in Flex, use the RemoteObject control along with a source property (which is the namespace and classname of your class) and call your methods via the RemoteObject.

Phew. It’s a bit messy comparing the different implementations of Flex remoting though, and that’s what I’m currently struggling with, not to mention, using the still-under-testing Flex 3 Beta 3.

I’ll post more, with examples, when I understand more.