Malcolm Groves
Subtracting from the sum of human knowledge
 
  Index

  Home
Projects
Writing
About Me

 
   
  Blog Categories

  All Posts
Borland
ECO
Personal
Photos
Projects
Misc.

 
   
  Recent Blog Entries

 

 
   
  Previous Posts

 
April 2004
Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  
Mar   May


 
   
 
 
Recent Posts
Click to see the XML version of this web page.

 

Thursday, 29 April 2004

Derived Attributes Part 3 - Reverse Derived Attributes

If you take a look at sourcecode for the Finish derived attribute declaration from the previous model, you'll note it looks like this:

property Finish: DateTime read get_Finish;

i.e. it is a read-only property. This kinda makes sense, given what we now know about derived attributes. If the value of my attribute is calculated, the framework by itself cannot figure out how to undo it i.e. how to tear apart the value into the original parts used to calculate it in the first place. This may not even be algorithmically possible.

However, sometimes it is possible, and in these cases, we need a way to tell the framework how to do it. Let's stick with our Appointment example. The Finish time was derived by taking the Start time and adding the number of minutes held in the Duration attribute. In my case, if someone assigns a new value to the Finish attribute, I'll change the Duration to reflect the difference between my Start time and this new Finish value.

         

How? Well, select the Finish attribute in your model, and in the object inspector, set the DerivedSettable property to True. If you now look at the sourcecode for the Finish declaration, a setter method (called set_Finish) has been added. We'll add our logic to recalculate the Duration in this setter method, but first we need to tell ECO that we are changing the logic in here, and to leave it alone in the future. We do this by removing the [ECOAutoMaintained] attribute from the property declaration in our sourcecode (Don't worry, there is a comment reminding you to do this in the generated setter method). My new set_Finish method looks like this:

procedure Appointment.set_Finish(Value: DateTime);
begin
  // If you add user code here, please remove the [EcoAutoMaintained] attribute
  // from the property declaration in the interface section
  Duration := Convert.ToInt32(Value.Subtract(Start).TotalMinutes);
end;

The new value for Finish is passed in as the Value parameter. I subtract from this the Start time, and use the the difference in minutes to set the value of the Duration attribute.

What this means is that not only can I set the Start and Duration and it will calculate the Finish, but I can set the Finish and it will calculate a new Duration.

|



Derived Attributes Part 2 - Code Derived Attributes

Ok, so yesterday we looked at adding attributes to our ECO classes where the value of those attributes was calculated at runtime using OCL. If you haven't already, look through the link I gave yesterday to Anthony Richardson's OCL Tutorial, as it is possible to embed some fairly rich logic into your model using this language.

That said, lets not kid ourselves. Delphi and C# are much richer languages than OCL, so there will come a time where either it's not possible to calculate your attribute's value using OCL, or where you are more comfortable doing it in code. So, let's look at Code Derived Attributes.

I've created a new class called Appointment, which has a Start attribute of type System.DateTime, which represents the start time of the Appointment and a Duration attribute of type Integer, which is the length of the Appointment in minutes. I've also then created a Derived Attribute called Finish, also of type System.DateTime. Just like yesterday, I've set its Derived property to True and its Persistence property to Transient. However, unlike yesterday, I have not specified any OCL to calculate the value.

         

When the ECO framework finds a derived attribute that has no OCL, it goes looking for a method, using reflection, of a particular signature and name, specifically:

function <Attribute Name>DeriveAndSubscribe(reevaluateSubscriber, resubscribeSubscriber: ISubscriber): system.object;

In this case, my attribute is called Finish, so it looks for a method called:

function FinishDeriveAndSubscribe(reevaluateSubscriber, resubscribeSubscriber: ISubscriber): system.object;

If it finds this method, it executes it, and uses the return value as the attribute's value. So its your job inside this method to do whatever you need to calculate the attribute's value. In my case, I want to add the Duration in minutes to my Start time, so my method looks like this:

function Appointment.FinishDeriveAndSubscribe(reevaluateSubscriber, resubscribeSubscriber: ISubscriber): system.object;
begin
  AsIObject.Properties['Start'].SubscribeToValue(reevaluateSubscriber);
  AsIObject.Properties['Duration'].SubscribeToValue(reevaluateSubscriber);
  result := Start.AddMinutes(Duration);
end;

Now hang on a sec, the last line makes sense, but what are the first two lines doing?

Well, let's step back a bit. ECO does not actually derive the value of your attribute every time someone asks for it. Instead it caches the value of the attribute after it is first calculated, and returns this cached value on subsequent requests, provided the attrbiute is not marked as out of date. How would our attribute get marked as out of date?  Well, that's where the first two lines of code above come in. We've subscribed to be notified whenever Start or Duration are changed. When we are notified, the ECO framework will flag our Finish attribute as out of date. Then, when the next request for its value is made, before the cached value is returned this flag will be checked and, the value of the atribute will be recalculated.

Subscriptions in ECO are an important topic that I plan to cover in future posts, but in the meantime, make sure you subscribe to any other attributes on which you are depending.

Not quite as easy as using OCL, but way more powerful, as you now have full access to the entire .NET Framework to use in calculating your attribute's value.

|