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 part 2 (synchronising from Flex to LINQ)

29 09 2008

March 2009: This post is outdated, please see the latest article on FLEX & LINQ:

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

 

Right.

So, getting your objects from your .NET 3.5 Linq to SQL setup to Flex is fairly easy. Getting the changed data back and synchronised to your DB is a different story.

The great thing about LINQ is that the data you retrieve and modify in .NET will automatically sync back to the DB by calling the SubmitChanges() method on your context (Linq to SQL) object.

The problem is of course when this data comes back in from Flex, the system doesn’t know what exactly has changed.

Now, the first thing to realise is that your Value Objects in Flex should NOT have the foreign key IDs in them. Why? Because say in the Company/Employee example, if you submit a Company object back to the system, with each Employee in the list having both a CompanyID AND a Company object reference, how will the LINQ system know which is the correct reference?

These foreign IDs will try to get serialised on the remoting level and be ignored. Good. We’re dealing with object references now, and foreign key ids are useless. If I want the foreign key of some employee, I would say: EmployeeObj.Company.CompanyID

Now, in .NET, I have a “Save()” method that I want to essentially insert/update the object and all objects within in.

Using the example before, say I have a company object, and that has a list of Employees. Then if I have a Save(Company c) method, I want the company object to insert/update, and the list of employees to automatically synchronise.

You can try deleting the old object and then inserting the new

So,

SaveCompany(Company newCompany)
{

DataClassesDataContext db = new DataClassesDataContext();
if (newCompany.CompanyID > 0)
{

Company oldCompany = db.Companies.Single<Company>(c => c.CompanyID = newCompany.CompanyID);
db.Companies.DeleteOnSubmit(oldCompany);
}
db.Companies.InsertOnSubmit(newCompany);
db.SubmitChanges();
}

This will nicely handle an insert/update of the company object, but it won’t deal with your Employee objects that well. Especially when each Employee references anthor table that essentially depicts there type./

Say each Employee has a foreign key reference to EmployeeType. Employee type is a static naming table that has a set of 5 records: “Manager”, “Sales Coordinator”, “Receptionist”, “Consultant” and “Helpdesk”. Then my Employee object will have an EmployeeType property (loaded via LINQ). Now, when this goes through LINQ, I believe during the serialisation, the creation of a “new” object EmployeeType will mean that the code above will create a new EmployeeType record for the Employee. Ouch.

So even though you may have sent all the employees through LINQ to Flex via a Company object, when it comes back, even if unchanged, all these employees will have references to new EmployeeType objects.

The way to solve this is fairly easy:

foreach (Employee e in newCompany.Employees)
{
   EmployeeType type = db.EmployeeType.Single<EmployeeType>(t => t.TypeID == e.EmployeeType.TypeID);
   e.EmployeeType = type;
}

oldCompany.Employees = newCompany.Employees;

Unfortunately, it’s not an easy process to learn how exactly to handle each scenario in synching LINQ and Flex, but one thing’s for sure, it certainly makes code lighter, and a lot easier to update.





Flex, .NET 3.5 with LINQ to SQL

23 09 2008

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

http://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







Follow

Get every new post delivered to your Inbox.