Real life complexity with ECOII. Part 1.

By: Hans Karlsen

Abstract: Article shows how to build a project planner application with the ECOII Framework

Real life complexity with ECOII

Real life complexity with ECOII. Part 1.

Author: Hans Karlsen can be reached at Hans@plexityhide.com

Overview

In the application I use for a sample the following things will be shown:

  • Databinding to properties of ECO-Objects
  • Databinding to collections of ECO-Objects
  • Databinding with nesting, producing structures of data, like trees
  • Derived values and why they are cool
  • The effects of model-view controls

Introduction

To fully appreciate the capacity and potential of the ECOII framework it helps to have an example to set things into perspective. In the following pages I will give you an overview on what I have found very useful and I will describe things in the context of a rudimentary project planner application.

Why a project planner application you may ask? Well there are a couple of reasons:

  1. I work for plexityHide.com and we build, market and sell powerful tools for visualizing and changing time in components with graphical manipulation (this is the only sales pitch for plexityHide.com in this document).
  2. As a co-worker of plexityHide.com I have participated in a multitude of projects that one way or the other visualizes time in such components.
  3. A project planner is commonly understood, and although it might look simple in its structure, it holds enough complexity to show some of the strong points of the ECOII framework.

Why change?

Do you recognize this scenario: You discover that you were wrong about something fundamental, and you do not dare change it? Things always get more and more complex and then one day, you cannot change things in your design anymore. If you do, everything will brake and you have no time to fix it. Remember those times when you get all cold and sweaty and then, in the end, choose to go on and wrap things up anyway although you finally think you know how to actually solve the problem in a clean and neat fashion?

Of course you do. Developers can cope with being looked upon as a hero and at the same time handle the stress of knowing how catastrophic things really are.

The bad news is that you will continue to get into trouble. But you are a developer, and that is what developers do The good news is that with ECOII you finally have tools that are suited for getting out of trouble once you are in it.

When you are familiar and confident with the tools, you will not get as cold as before, or sweat as much as before, as you discover that you have been completely wrong. Instead you will fix the model and put some serious thinking into getting it right. Probably you do not get it right this time either, it never gets completely right. That is why God invented the iterations.

I like the XP statement: trust that you can solve todays problems today, and tomorrows problems tomorrow, but it will not do us much good if we continue to lock ourselves in with solutions that we cannot change, ECOII is the key to let you change your designs along the way. My personal definition of agility is the ability to get out of trouble fast enough to survive. To me ECOII is agility in its purest form.

 

Getting started

The sample application is available for download and you might want to get a copy just to judge if this paper will be of interest to you (http://www.plexityhide.com/pub/AProjectPlanner.zip). This download contains the complete source of this project. The project uses the GTP.NET 2.0 licensed Gantt and Grid control, but it will enter a 30 evaluation state so you should not have a problem looking over the sample code or starting the exe in the bin/debug folder.

 

I have seen a couple of papers on the ECOII framework. The ones I have seen somewhat lack the complexity needed to show why you should start using the framework today, hopefully this paper can fill a bit of that void, but possibly you should read some other paper describing ECOII basics first.

 

 

Getting started building an ECOII winforms application is easy, just click the right buttons.

 

 

Start with the most important; the model

Then you should get started on your model; after all, it is a Model Driven Architecture (MDA) we are using

 

A word of caution: Do not except to produce your final model in the first iteration. I will rephrase that; do not expect to ever really finish your model. This is actually one of the strong points of ECOII: You do not have to do it all at once. This is not the same as you do not have to be careful, and you do not have to think, but you do not have to guess either. Start with what you know.

 

If there is one shift in thinking that you need to adopt to be a successful MDA developer it is that it is about declaration and not about brute force.

In ECOII you will be focusing more on declaration than reaction. For instance, you will be explaining to the framework with OCL(object constraint language) what some logic needs to subscribe to, to keep the result current, rather than implementing many different OnChange events and call some update logic. If there is one thing I have seen in almost every new MDA developer it is the hunt for the OnChange event, and the frustration when they cannot find it.

The OnChange tactic represents the brute force way of coding, and it takes some getting used to, to learn how to live without it and replace it with exact and correct declarations of subscriptions.

 

 

 

 

Get an important book

If you have not already you should get a copy of Martin Fowlers book Patterns of Enterprise Application Architecture (ISBN 0-321-12742-0). In this book all the key building blocks, or patterns, for building a complex multi user system are named and described. Fowler argues about the difference between having a domain model and not having it. How a domain model helps you to get a linear curve of development effort and progress, and how not having a domain model gives an exponential curve of complexity that makes further development harder and harder. I was very happy when I read that explanation, Fowler hits the spot why the initial investment in a domain model sometimes is necessary and sometimes could be seen as overkill. A domain model traditionally comes with a high initial price because you need to code for it. With ECOII there is no such start penalty, and hence it will never be overkill to do it right. This is very good news because everyone knows that things grow. Work that was classified as small and simple and does not qualify for a domain model then grow and pretty soon turn into nightmares with developers calling out in desperation for a fresh-start and a re-write.

 

 

Databinding

Databinding is standard in .NET and most components have good support for using data binding. There are two aspects of databinding, bind to single values (like a field in a datarow) or to bind to a list, like the rows of a datatable. If you have something implementing the IList interface you can bind that list to a Databind enabled control.

 

To bind to a single value you can go like this:

// Set up a 
// Hook it up with data bind
gantt1.Grid.RootNodes_DataSourceList=exprHandleRootTasks.GetList();
 standard text box to bind to the project name
Binding b=new Binding("Text",proj,"Name");
textBox_ProjectName.DataBindings.Add(b);

 

You simple say that the Text property should show the Name property of the proj object.

 

To bind to a list from the ECOII framework we set up an ExpressionHandle. This ExpressionHandle has a RootHandle that defines the context in which the expression of the ExpressionHandle has meaning. The expressions are written in OCL, but for starters just think of them as a point separated list of model navigations.

 

The expression gives us the Y-axis (rows) of the list and we will have a default X-axis (definable in the Columns property of the ExpressionHandle) consisting of the properties of the AbstractTask class since we have left the AddDefaultProperties set to true.

 

We then hook up this handle to the Gantt component:

// Hook it up with data bind
gantt1.Grid.RootNodes_DataSourceList=exprHandleRootTasks.GetList();

 

Derived values

In this application we a have an Identity property for each Task node. But I want Identity to be defined by the position within the Task tree. So that the first task gets 1 and its first child gets 1.1. This is a good example for a derived property.

 

I add the Identity property to the AbstractTask-class and set its properties.

I leave the Derivation OCL empty, since I want to derive this value in code.

ECOII looks for a specific signature to find this derivation code so you can just add it like any other method:

 
// Added this just like explained in the help file "ECO, Deriving attributes in source code"
[UmlElement]
public object IdentityDeriveAndSubscribe(ISubscriber reevaluateSubscriber , ISubscriber resubscribeSubscriber)
{
   string   identity;
 
   // The identity will be dependent on the placement in these links
   IOclService ocl=AsIObject().ServiceProvider.GetEcoService(typeof(IOclService)) as IOclService;
   ocl.EvaluateAndSubscribe(AsIObject(),"ParentTask.SubTasks",reevaluateSubscriber,resubscribeSubscriber);
   ocl.EvaluateAndSubscribe(AsIObject(),"ParentTask.Identity",reevaluateSubscriber,null);
   
   if (ParentTask!=null)
   {
 
      int x = ParentTask.SubTasks.IndexOf(this)+1;
      identity = x.ToString();
      return ParentTask.Identity+"."+identity;
   }
   else
   {
      if (Project!=null)
      {
         int x = Project.RootTasks.IndexOf(this)+1;
         identity = x.ToString();
         return identity;
      }
      else
      {
         // This task is not connected to anything and can have no identity
         return "???";
      }
   }
}

 

 

 

One important part of the code above is how we declare for ECOII exactly when we need to be updated. We do not implement any OnChange events anywhere, we just state that if anything changes in the content of the navigation to the ParentTask.Identity or if anything changes in the content of the navigation ParentTask.SubTasks, we need to re-think our value. This declaration is performed by the context of self. This might feel strange Is it really enough to always keep the Identity up to date? Think that you are a Task-object. You currently have Identity 2.3.7. What happens if the task with identity 2.2 gets deleted? Well there is a change in the SubTasks of Task 2, so 2.3 will be re-evaluated. But then there is a change in 2.3s identity so then 2.3.7 will be re-evaluated as well.

 

Nested lists

Databind of a list is easy and straight forward, but how can we easily keep track of structures like trees? The answer is Nesting in the ExpressionHandle.

If you think of the OCL expression of the ExpressionHandle as the Y-axis definition, and the properties of the class resulting from the expression as the X-axis, then how do we explain Nesting? Think of the result from the ExpressionHandle as a XML-file. You have a main Y-axis in the DocumentElement nodes, and we can see the attributes of these nodes as the X-axis. If you have this mental image, you will have no trouble seeing the Nestings as child nodes to the document element nodes. Of course these child nodes can have both attributes (X-axis) and child nodes (Y-axis) of their own. And that will give you nesting definitions of unlimited depth.

 

To add nestings we start by adding columns for them:

 

 

Name the column and the nesting name. If you hate yourself you will give them different names. If you are full of love and understanding you will give them the same name

The expression I set for the column results in a list, and this is really the whole point of nesting. Always produce expressions that return lists. Set the Nested property to true.

 

Above I add a reference to a nesting for the SubTasks link:

 

I also want to show TaskTimes in the Gantt-chart. So I add a nesting reference for that:

But I need to be careful with context. The expression for TaskTimes will be evaluated in the context of AbstractTask, and AbstractTask does not have a TaskTimes relation. That is why I use the OCL-operation SafeCast.

 

 

And then I also want to show the TaskCollection as a TimeItem in the Gantt. This is a good example when I have one object (a TaskCollection) that I want to visualize in two different aspects in my GUI. I want it to be a GridNode with an Identity and description, but also I want it to show up as a time item span in the time item area of the Gantt. The Gantt handles list binding for time items, so it will be confused if I hand it a single object. I turn my object into a list with the OCL-operator ->asSequence below.

 

Ok. Now we have defined that we have nesting and how they are derived. Now I can define the content of each nesting:

 

The first nesting is the one for the SubTasks:

I can define the X-axis for this nesting:

Since I want the SubTask navigation to go on for ever (no depth limitation in the projects task hierarchy) I repeat the definition of SubTasks and point it back to the nesting of SubTasks. (ohh Im getting dizzy. Note to self  write a property editor so that you can actually understand this)

The TaskTimes must also be defined again in the context of this nesting, same with the CollectionTimeItems (no screenshot).

 

Ok, Now we are back at the nesting definition. Next item

Is TaskTimes. Let us look at the columns for this one:

Here I added only one column. It holds a navigation to Resource.Name.

I will use this value to present the assigned resource inside a TaskTime.

Technically I need the Start, Stop and PercentPerformed properties of the TaskTime as well, but since the AddDefaultProperties is set to true in the nesting, it is added implicitly.

 

One nesting to go:

And the columns:

None! Well the ones I need Start and Stop, are added implicitly due to the AddDefaultProperties=true.

 

Ok. Now we have the expressionHandle defined with our data. How do we get it into the Gantt?

In the OnNodeInserted event of the Gantt we add some code. This event is fired on each GridNode that is inserted, and we did bind the RootTasks to the grid, so we will end up here as soon as there is an AbstractTask available.

 
private void gantt1_OnNodeInserted(PlexityHide.GTP.Grid aGrid, PlexityHide.GTP.NodeEventArgs e)
{
     // We want to handle sub nodes with data bind....
     // And we defined the sub tasks with nesting in our expression handle.
     CurrencyManager cm=e.GridNode.OwningCollection.NodeDataConnect.CurrencyManager;
     object ox=e.GridNode.ListItemWhenDataBound();
     PropertyDescriptor pd=cm.GetItemProperties()["SubTasks"];
     RenderedList subnodes=pd.GetValue(ox) as RenderedList;
     e.GridNode.SubNodes_DataSourceList=subnodes;
 
 
 
     // We want to bind the task times as well...
     PlexityHide.GTP.GanttRow row=PlexityHide.GTP.Gantt.GanttRowFromGridNode(e.GridNode);
     row.Layers[0].NameInDS_Start="Start";
     row.Layers[0].NameInDS_Stop="Stop";
 
     // We treat two very different types of time items... TaskCollection and TaskTime. They both Have Start and Stop as their time properties
     if ((ox as RenderedTuple).Element.AsObject is TaskCollection )
     {
        // TaskCollection
        row.Layers[0].TimeItemLayout="MyTimeItemLayoutForCollectionTasks";
        pd=cm.GetItemProperties()["CollectionTaskTimes"];
        RenderedList tasktimes=pd.GetValue(ox) as RenderedList;
        row.Layers[0].DataSourceList=tasktimes;
        // The TaskCollection is derived and any move-op of it, has it own interpertation that we can handle in this event...
        row.Layers[0].TimeItemDataConnect().OnBeforeTimeItemToDS+=new TimeItemToDSEventHandler(OnBeforeTimeItemToDS);
     }
     else
     {
        row.Layers[0].TimeItemLayout="MyTimeItemLayout";
        // We also set up an event that allows me to set up the Time item texts (that are not databound i GTP.NET 2.0)
        row.Layers[0].TimeItemDataConnect().OnBeforeDSToTimeItem+=new TimeItemToDSEventHandler(OnBeforeDSToTimeItem);
 
        // TaskTime
        pd=cm.GetItemProperties()["TaskTimes"];
        RenderedList tasktimes=pd.GetValue(ox) as RenderedList;
        row.Layers[0].DataSourceList=tasktimes;
     }
 
}

 

 

In the code above we extract the resulting lists from our nesting definitions and we instruct the Gantt to bind to them.

 

Ok there are a few more special cases in the code, like the display of TimeItemTexts that are not databound, and the Start and Stop of TaskCollection that are derived attributes, but on the whole we are actually done.

 

True model-view

One of the many extremely cool things you get with using the ECOII framework is the complete implementation of model-view controls. Of course this is done by the Datasource controls, but traditional MS-programming will have you hooking up a unique datatable to each form you have written, and since the datatable grabbed a snapshot of data from storage, it will not communicate changes to the values to other datatables in your application until you commit the changes and refresh the other datatables.

Since ECOII builds up a domain model and handles identity resolution of all objects that are loaded, you will never get in conflict with yourself again. And if you do display the same data in multiple forms (and possibly for different reasons) you will see the same information in both places, without the need for committing, and without the need for refreshing. To see data from other users you will still need to refresh, and to let other user see your data you will still need to commit. But I want to show this sample.

 

I made another view to the data that shows all resources and for each resource shows their TaskTimes:

 

 

 

So if I change the project view a little

I get an immediate useful response in the other view, showing me a conflict that could have been overlooked:

 

This is better explained if you download and run the application. (http://www.plexityhide.com/pub/AProjectPlanner.zip).

 



Server Response from: ETNASC03