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:
- 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).
- 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.
- 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).