Code derived associations

This article will demonstrate how to use code derived associations to implement user-defined class constraints.

Code derived association is derived association without provided Derived OCL expression - the logic must be implemented in your code.

Please read Malcolm Groves' articles if you are not sure what derived association is.

These two articles are the most relevant to the subject:
ECO : Derived Associations
ECO : Derived Attributes Part 2 - Code Derived Attributes

Problem:
Often users ask us if we can make one or another field of our system mandatory.
They would like to have more required fields or certain data validation rules to achieve better quality of stored information.

Solution:
Sample project used in this article will demonstrate how to implement custom, user-defined class constraints for Person class.

User can create his own simple data validation rules and system will check the rules against the objects in database every time user creates/accesses objects or changes object attributes.

This is different from defining design-time class constraints, which is provided by ECO framework, because user will be able to set his own class constraints at run-time, store them in database and use throughout the system.

In my application I have created 2 classes - Person and PersonConstraint.

Class Person represents Person and its attributes and class PersonConstraint stores all user-defined constraints-rules for Person object.

PersonConstraint class has string attributes OCLExpression and ErrorMessage.

Attribute OCLExpression contains OCL expression to be evaluated against selected Person object.
Attribute ErrorMessage contains a string to display in GUI if constraint/rule validation failed.

Also I have created a derived association between Person and PersonConstraint.
Association end BrokenConstraints returns all violated constraints for Person object.

Because I didn't specified Derived OCL for the association, I need to provide implementation of derived association in my code.

For association end BrokenConstraints I created a function declared like this:

public IList BrokenConstraintsDeriveAndSubscribe(ISubscriber reEvaluateSubscriber,  ISubscriber reSubscribeSubcriber)
{
...
}

Association end has multiplicity n and function should return IList.

The other details of its declaration are similar to code derived attribute function (see Malcolm Groves Blogs).

In my function:

  • I create a new object implementing IList (for example, ArrayList object),
  • Go through all PersonConstrain objects,
  • Evaluate each OCLExpression on Person object,
  • If validation fails, I add PersonConstraint object to the list,
  • Return result list

ECO uses this function to get BrokenConstraints association end.

Though function returns IList, ECO framework transforms result to IPersonConstraintList (this is done by adapter class in the framework).

To reevaluate BrokenConstraints for every object, every time object gets updated, I subscribe on Person object changes using reEvaluateSubscriber and reSubscribeSubcriber like this:

os.EvaluateAndSubscribe(this.AsIObject(), c.OCLExpression, reEvaluateSubscriber, reSubscribeSubcriber)

Another thing (thanks to Jan Norden for help) is to subscribe on PersonConstraints' OCL expressions changes:

os.EvaluateAndSubscribe(null, "PersonConstraint.allInstances.OCLExpression", reEvaluateSubscriber, reSubscribeSubcriber);

This subscription forces all OCLExpressions to be reevaluated for all existing Person objects when new PersonConstraint is added.

This is how my code derived association function looks:
public IList BrokenConstraintsDeriveAndSubscribe(ISubscriber reEvaluateSubscriber,
ISubscriber reSubscribeSubcriber)
{
ArrayList brokenConstraints = new ArrayList();
IObjectList constraints = null;
IOclService os =
(IOclService)AsIObject().ServiceProvider.GetEcoService(
typeof(IOclService));
IElement e = null;
object OCLResult = null;

//Get all constraints
e = os.EvaluateAndSubscribe(null,
"PersonConstraint.allInstances", null, null);
if (e is IObjectList)
constraints = (IObjectList)e;

//subscribe on all OCLExpressions
os.EvaluateAndSubscribe(null,
"PersonConstraint.allInstances.OCLExpression",
reEvaluateSubscriber, reSubscribeSubcriber);

//go through all constraints and evaluate expressions on the object
for (int i = 0; i < constraints.Count; i++)
{
PersonConstraint c = constraints[i].AsObject as PersonConstraint;

if (c.OCLExpression =="")
continue;

e = os.EvaluateAndSubscribe(this.AsIObject(), c.OCLExpression,
reEvaluateSubscriber, reSubscribeSubcriber);
if (e != null)
OCLResult = e.AsObject;

//Add to list if constraint is broken
if (!Convert.ToBoolean(OCLResult))
brokenConstraints.Add(c);
}

return brokenConstraints;
}

And this is my application:

 

Now I can create my own class constraints/rules entering OCL expressions and Error messages.

Every time I access Person object or change its attributes, program evaluates these OCL expressions and displays a list of Error Messages for selected Person object.
If I add more rules, they get validated on selected Person object.

As soon as selected Person object conforms my custom class constraints, Error messages disappear.

Source code can be downloaded from here.

 

Share this article!

Follow us!

Find more helpful articles: