ECO Services

Copyright (c) 2007-2009. All rights reserved.

ECO Services

Table of Contents
Overview

1

IEcoServiceProvider

1

Terminology

2Standard ECO services

3

IExternalIdService

4

IStateService

6

IDirtyListService

8

IUndoService

9

StartTransaction, RollbackTransaction, and CommitTransaction

9

Undo blocks

10

Working with the undo service

12

Working with an undo block15

IObjectFactoryService

16

IVariableFactoryService

18

Query services

22

IOclPsService

25

OCL operations supported by IOclPsService

28

+

28

-

28

*

29

/

29

<

29

<=

30

<>

30

=

30

>

31

>=

31

AllInstances

31

Average32

Difference

32

Div

32

Exists33

ForAll33

Implies34
ii

ECO Services
Includes34

Intersection

34

IsEmpty

35

IsNull

35

Length36

MaxValue36

MinValue

36

Mod

37

Not

37

NotEmpty

38

OrderBy

38

OrderDescending

39

OrderGeneric

39

Reject40

Select

40

Size

40

SqlLike

41

SqlLikeCaseInsensitive

41

Sum

42

ToLower (String)42

ToUpper(String)

43

Union

43

IOclService

44

OCL operations supported by IOclService

48

-

49

+

50

AllInstancesAtTime

50

AllLoadedObjects

51

AllSubClasses

51

AllSuperClasses

51

AssociationEnds

52

AsString

52

At

52

AtTime53

Attributes

53

Collection operations

53

Compare61

Constraints

62

Create63

Date and time operations

66

EmptyList

83

Existing

83
iii

ECO Services
ExternalId

84

If

84

Let

84

Mathematical operations

85

MaxLength

94

ModifiedSinceTimeStamp94

NewGuid95

ObjectFromExternalId

95

ObjectTimeStamp95

OclAsType

96

OclIsKindOf

96

OclIsTypeOf

97

Parse

97

SafeCast

98

State machine operations

98

String operations

101

SuperTypes

124

TimeSpan operations

124

TaggedValue

124

ToByte125

ToDouble

125

ToInt16125

ToInt32

125

ToSByte126

ToSingle126

ToUInt16

126

ToUInt32

127

ToUInt64

127

TypeName

127

Xor

127

IActionLanguageService
Operations support by IActionLanguageService

128
128

Clear

128

Add

129

Create129

Delete129

Remove129

RemoveAt

130

ITypeService

130

InstalledOperations

131

Creating a string operation

131

Creating a collection operation

132
iv

ECO Services

IPersistenceService
Multi user concurrency

133
139

IExtentService

139

IVersionService

141

ICacheContentService

144

Subscriptions

144

ITypeSystemService

147

IModelElement

149

IPackage

149

ITaggedValue

151

IStructuralFeature

151

IClass152

Registering custom services

159

Replacing standard ECO services

160

Replacing the ExternalIdService

160

A validating IPersistenceService

160

Index

a

v

1.1 IEcoServiceProvider

ECO Services

1 Overview
The ECO framework has been designed in such a way that business logic and framework logic are kept as separate as
possible. For example, examining the generated source code for an ECO class will not reveal methods such as “Delete” or“Refresh”, as you might expect to find. Keeping framework methods out of our business classes is an important step towards
making our source code more readable and manageable. Having a clear and almost invisible separation means that when
we inspect the source code of our business classes we only see methods relating to the logical functioning of the class inquestion. This clearly makes our source code easier to understand, refactor, and debug.

This document will cover two aspects of ECO services. First it will cover the most commonly used services implemented by
the ECO framework itself, afterwards it will show how you may create and consume your own services and also show how
separating functionality into services can improve your code by separating logic and making it easier to write unit tests.

1.1 IEcoServiceProvider
The IEcoServiceProvider is an interface that is realized by the EcoSpace class and is the entry point for retrieving areference to a previously registered service. The EcoSpace class itself additionally provides a shortcut to the default
services, the following code snippet will check if there are currently any class instances that have modified persistentmembers which need to be saved to the data storage.

if (EcoSpace.DirtyList.HasDirtyObjects())...

This same code could be achieved using the IEcoServiceProvider.GetEcoService<T>() generic method. This example isslightly longer than the previous example but illustrates how to retrieve a registered service without relying on the EcoSpace
to have a short-cut property, for example when you register your own custom service.

if (EcoSpace.GetEcoService<IDirtyListService>().HasDirtyObjects())...
Note: The EcoSpace class itself does not implement IEcoServiceProvider. The interface is actually implemented by anotherclass which the EcoSpace owns an instance of.

Retrieving services within a business class
ECO business classes are designed so that they may be used in one or more applications, and therefore may beinstantiated by one or more different descendants of EcoSpace. When retrieving services from within a business classmethod it is necessary to go via the IEcoServiceProvider interface.

if(this.AsIObject().ServiceProvider.GetEcoService<IDirtyListService>().HasDirtyObjects())
...

1

1

1.2 Terminology

ECO Services

1. AsIObject() - This is the entry point to the "ECO world" from a business class. It accesses various support functionality
for an object instance within the EcoSpace (they are not implemented by the class itself). The item of interest is theServiceProvider property.
2. ServiceProvider - This provides access to the IEcoServiceProvider in order to obtain references to registered services.
This reference happens to also be the instantiated EcoSpace which owns the business class instance, however, it isrecommended that additional features be implemented via services in order to prevent a strong dependency upon a
specific EcoSpace type.

1

1.2 Terminology
As this document discusses both UML models and the source code produced there will be a number of terms which may be
used to describe what is essentially the same thing, the term used will depend upon the context. For example, whendescribing Person.FirstName in the context of UML modeling the term "Attribute" would be used, when in the context ofsource code the term "Property" will be used. The following table is a list of terms and their meanings

UML model Source
code
Attribute

Description

Property The term "Attribute" will never be used to refer to the System.Attribute class. System.Attribute will
be referred to by its fully qualified name if necessary.

Association Property A reference from one modeled class to another is always done via associations and not attributes.
end
Each association has two ends in UML, and either two or one in source code depending on whether
or not the association is navigable in both directions.
Single role
A single role is an association end which links to at most one instance.
Multi role

Property A multi role is an association end which links to a list of instances, allowing it to hold zero to manyreferences.

Element

ObjectThis term is used to describe either an instance of a modeled object or one of its property values.
instance
Property2

2.1 IExternalIdService

ECO Services

2 Standard ECO services
The following services are created and registered automatically whenever you create a new instance of an EcoSpace. These
services are an interface to the features that ECO implements as standard such as persistence, in-memory transactions, andso on.

A number of the ECO services have overloaded methods where parameters of the type IObject are replaced with aparameter of type IObjectProvider. IObject is what is known as an "object locator", the way in which ECO references
instances of ECO business classes internally. IObject is obtained from an instance of a business class like so

IObject objectLocator = person1.AsIObject();

whereas the actual object instance is retrieved from the object locator like so

2Person p = objectLocator.GetValue<Person>();The business class itself implements IObjectProvider, so it then becomes possible to write code in either of the two following
ways://Passing IObject
SomeEcoService.DoSomething(person1.AsIObject());//Passing the business class instance itself (IObjectProvider)
SomeEcoService.DoSomething(person1);

When there are overloads for both parameter types I shall illustrate the IObjectProvider approach only, just to save fromhaving to type .AsIObject() needlessly.

Note that when ECO services need to read or modify property values of object instances it does not require the use ofreflection. How the values are manipulated depends upon whether or not the model specifies that the property has user code
associated with it, additional code which needs to be executed in addition to manipulating the ECO cached values.

Has
user
code

Action taken

True

ECO will direct all property access via the object instance itself. To identify which property the service requires
some auto-generated code is created in order to get/set property values via an integer index.

False

ECO will access the cache directly, bypassing the object instance completely.

32.1 IExternalIdService

ECO Services

2.1 IExternalIdService
Every business class instance within an EcoSpace is uniquely identifiable. Whether this is by an ECO generated object id, ora single/multi part primary key on a database. ECO requires a unique identifier so that it can perform persistence operations
on the correct object when updating the database. Using the IExternalIdService it is possible to either retrieve a string
representation of an object instance's unique identifier, or to provide such a string representation and have ECO provide an
object instance, if the object has not already been cached it will first be retrieved from the data storage.

The ObjectForId() and IdForObject() methods of the IExternalIdService may be used to hold weak references toobjects, or to pass business class instance references between different EcoSpace instances. This is especially prevalent in
ECO powered web service / web application projects where you may wish to use a pool of EcoSpaces and therefore might
not always working with the same EcoSpace instance across different page requests. If there is more than one EcoSpace inyour web application's/service's EcoSpace pool (which is recommended) then the flow in Figure 05 illustrates a likely
scenario.

2

A user views an object in ViewPerson.aspx and then decides to edit that object, at which point they are redirected to
EditPerson.aspx. Storing the Person object in a session is an incorrect approach because the Person instance belongs to
EcoSpace A, whereas EditPerson.aspx was allocated EcoSpace B from the pool. Instead of holding ECO business classinstances between page requests the web application/service should instead hold the "ID" of the object, which is retrieved
using IExternalIdService.IdForObject(). The receiving page should retrieve an object instance from its allocated
EcoSpace using the mirror method IExternalIdService.ObjectForId().

Although this service is used primarily for web applications/web services, there are many more possible applications. Anytime the identification of an ECO business class instance needs to be stored in some way this service is the answer.
Additionally when developing an ECO WinForms application you may wish to use multiple instances of your application's
EcoSpace so that cached data is released as soon as an individual form is closed, the ExternalIdService is an excellent wayof passing object identities between these forms.
//Sending the ID of an object in Form1
string personID = EcoSpace.ExternalIds.IdForObject(person);SomeOtherForm.EditPerson(personID);4

2.2 IStateService

ECO Services//Retrieving the object from the ID
Person p = EcoSpace.ExternalIds.ObjectForId(personID).GetValue<Person>();Object ID structure
The structure of these ID's is always in the format {ClassID}!{InstanceID}, an ID identifying the class, followed by an
exclamation mark, followed by an ID identifying the instance itself. There are two points at which the ID for an object may
change1. A new instance is persisted• Before - $new$!a791bdee-7cae-4cd6-882d-c983274a65ea!0• After - 0!1
2. The model is changed and the application rerun• Before - 0!1• After - 2!1
Note: The instance ID is an integer by default, but may be another data type such as a Guid depending on how you
configure your persistence.

In the first case the ID is constructed by representing the class ID as the string $new$ followed by a Guid, and the instanceID as zero. The purpose of this is to prevent the ID of an new instance from being passed to another EcoSpace instance
before it is persisted. When passing object references using external ID's the target EcoSpace is able to locate the object
either in its cache or by fetching it from the data storage. If the ID being passed is a reference to a new instance then thetarget EcoSpace instance has no way of locating the object. Once the new instance has been persisted its ID will change tothe format in the second example.

In the second example the ID is constructed by representing the class ID as an integer and the instance ID as the key the
instance was assigned when it was first persisted to the data storage. The class ID is determined by locating the class in themodel's list of classes. The list is sorted so that it remains in a predictable order, however, when the model is changed thiscan lead to classes appearing in the list or being removed from the list and can therefore cause the class index to change.

External ID's were designed only to be used for short-term references in order to pass instance references between
EcoSpace instances. Therefore it is not recommended that these ID's be held onto long term, for example• Storing the ID in a config file.
• Storing the ID in a persistent property (associations are better suited for this purpose anyway).
• Using the external ID as a parameter in a URL which might later be archived by search engines.
It is possible to replace the ExternalIdService if you wish to ensure that external ID's do not change when the model changes
if you require it. This is a technique that will be demonstrated later in this document. See Replacing the ExternalIdService (see page 160) for details.

52

2.2 IStateService

ECO Services

2.2 IStateService
Whenever a new instance of a business class is created or an existing instance is modified ECO keeps track of these kindsof actions so that it later knows how to update the data storage. The IStateService provides a way of accessing these states.

bool IsNew(IObjectProvider obj)
If the ECO business class instance passed has been created and not yet persisted to the data storage this will return true.
Once the object has been persisted this method will always return false, even if the object is later modified and requires itschanges to be persisted.

bool IsDirty(IProperty property)
If IsNew() returns true for the object instance that owns the property then this method will always return true. If the member
in question has not been modeled as persistent (it is transient or derived) then this method will return false.

If the member has been modeled as persistent and has a change which needs updating to the data storage this method will
return true. In the case of simple members such as Int32 and String the result will depend simply on whether or not a change
has been made. When the member is an association it is not considered dirty (modified) if it is not embedded. Here aresome scenarios which illustrate this last point:

1. Neither end is marked as embedded in the model. An additional table will be created in the database with the name of theassociation "DriverCurrentVehicle".

Driver driver = new Driver(EcoSpace);
Vehicle vehicle = new Vehicle(EcoSpace);//Get the property references for use with ECO services
IProperty driverCurrentVehicleProp = driver.AsIObject().Properties["CurrentVehicle"];IProperty vehicleCurrentDriverProp = vehicle.AsIObject().Properties["CurrentDriver"];//Both return true because the object itself is new
Debug.WriteLine(EcoSpace.States.IsDirty(driverCurrentVehicleProp));
Debug.WriteLine(EcoSpace.States.IsDirty(vehicleCurrentDriverProp));
EcoSpace.UpdateDatabase();driver.CurrentVehicle = vehicle;//Both return false because neither end is embedded
Debug.WriteLine(EcoSpace.States.IsDirty(driverCurrentVehicleProp));
Debug.WriteLine(EcoSpace.States.IsDirty(vehicleCurrentDriverProp));

2. The Driver end of the association is marked as embedded. The Vehicle table in the database will have an additional
column named "CurrentDriver" which holds the ID of the driver instance.

62

2.2 IStateService

ECO Services

Driver driver = new Driver(EcoSpace);
Vehicle vehicle = new Vehicle(EcoSpace);//Get the property references for use with ECO services
IProperty driverCurrentVehicleProp = driver.AsIObject().Properties["CurrentVehicle"];IProperty vehicleCurrentDriverProp = vehicle.AsIObject().Properties["CurrentDriver"];//Both return true because the object itself is new
Debug.WriteLine(EcoSpace.States.IsDirty(driverCurrentVehicleProp));
Debug.WriteLine(EcoSpace.States.IsDirty(vehicleCurrentDriverProp));
EcoSpace.UpdateDatabase();driver.CurrentVehicle = vehicle;//Returns false because it is not embedded
Debug.WriteLine(EcoSpace.States.IsDirty(driverCurrentVehicleProp));//Returns true because the ID of the Driver is embedded into this class's table
Debug.WriteLine(EcoSpace.States.IsDirty(vehicleCurrentDriverProp));2
3. The Order end of the association is marked as embedded. The OrderLine table in the database will hold the column
referencing which order it belongs to.

Order order = new Order(EcoSpace);
OrderLine orderLine = new OrderLine(EcoSpace);//Get the property references for use with ECO services
IProperty orderLinesProp = order.AsIObject().Properties["Lines"];
IProperty orderLineOrderProp = orderLine.AsIObject().Properties["Order"];//Both return true because the object itself is new
Debug.WriteLine(EcoSpace.States.IsDirty(orderLinesProp));
Debug.WriteLine(EcoSpace.States.IsDirty(orderLineOrderProp));
EcoSpace.UpdateDatabase();order.Lines.Add(orderLine);//Returns false because it is not embedded
Debug.WriteLine(EcoSpace.States.IsDirty(orderLinesProp));//Returns true because the ID of the Order is embedded into this class's table
Debug.WriteLine(EcoSpace.States.IsDirty(orderLineOrderProp));

4. Neither end of this association may be embedded because both ends are multiple (not all databases support multi-values
in a single column). An additional table is created in the database with the name of the association (FoodLikedBy) in orderto maintain the associations.

Person wallis = new Person(EcoSpace);
Food cheese = new Food(EcoSpace);//Get the property references for use with ECO services
7

2.3 IDirtyListService

ECO Services

IProperty personFoodLikedProp = wallis.AsIObject().Properties["FoodLiked"];
IProperty foodLikedByProp = cheese.AsIObject().Properties["LikedBy"];//Both return true because the object itself is new
Debug.WriteLine(EcoSpace.States.IsDirty(personFoodLikedProp));
Debug.WriteLine(EcoSpace.States.IsDirty(foodLikedByProp));
EcoSpace.UpdateDatabase();wallis.FoodLiked.Add(cheese);//Returns false because neither end may be embedded
Debug.WriteLine(EcoSpace.States.IsDirty(personFoodLikedProp));
Debug.WriteLine(EcoSpace.States.IsDirty(foodLikedByProp));bool IsDirty(IObjectProvider obj)
If any of the properties for the passed object instance are considered dirty or the object instance is new or marked as deleted
then this method will return true, otherwise there are no changes to persist to the data storage and this method will returnfalse.

Person person = new Person(EcoSpace);//Will return true because the object is new
Debug.WriteLine(EcoSpace.States.IsDirty(person));2

EcoSpace.UpdateDatabase();//Will return false, the object remains unaltered since it was persisted
Debug.WriteLine(EcoSpace.States.IsDirty(person));person.AsIObject().Delete();//Will return true, the object has not been removed from the data storage since deleted
Debug.WriteLine(EcoSpace.States.IsDirty(person));EcoSpace.UpdateDatabase();//Will return false, the object cannot be dirty now that it no longer exists
Debug.WriteLine(EcoSpace.States.IsDirty(person));

2.3 IDirtyListService
Whenever a persistent object is created, modified, or deleted, it is considered to be"Dirty"; this means that it has in some
way been altered. This Dirty state is an indication that the data storage needs to be updated in order to reflect the changes
made to the object instance in question.

Using the IDirtyListService interface the dirty list service enables the developer to obtain a list of objects that have been
modified. Note that the "Dirty" state is reserved for persistent objects only, object instances of a class marked as Transient in
the model are never saved to the persistence storage and therefore cannot have such a state. Additionally, if the EcoSpace
has no persistence defined then all classes are considered to be Transient regardless of how they have been defined in themodel, in such a case the IDirtyListService will never hold any object references.

IObjectList AllDirtyObjects()
This method returns an IObjectList containing an IObject for each dirty object held within the EcoSpace. This list isimmutable, meaning that if you try to modify it using Remove() for example a System.InvalidOperationException will bethrown. The size of this list will alter as more objects become dirty, the data storage is updated marking some objects as
non-dirty, or as a result of objects becoming dirty/non-dirty due to in-memory transactions being rolled back or reapplied 8

2.4 IUndoService
see IUndoService (ECO Services

StartTransaction, RollbackTransaction,

see page 9).

bool HasDirtyObjects()
If AllDirtyObjects().Count is greater than zero then this method will return true, otherwise it will return false indicatingthat there are no dirty objects to persist to the data storage.

void Subscribe(ISubscriber subscribe)
Passing an instance of ISubscriber to this method will cause the ISubscriber.Receive method to be executed whenever
the number of objects held in the AllDirtyObjects() list alters. This is a useful global hook to perform tasks on objects
that have been modified, for example to provide on-screen validation.

2.4 IUndoService
Using the IUndoService the programmer is able to perform in-memory transactions on objects within the EcoSpace. Thesetransactions may be committed, reversed, or reapplied at any point ensuring that if an operation fails the state of all affected
objects is returned to a specific state.

To use a classic example; if a funds transfer is initiated from bank account "A" to bank account "B" two operations must takeplace. The balance of account "A" must decrease by the transaction value and the balance of account "B" must increase by
the transaction value. This kind of atomic operation has been available in all good databases for quite some time now, but
the ECO undo service allows the same kind of atomic operation to be performed in-memory as well, eliminating the need toreload object contents from the data storage if you wish to abandon a set of changes.

The undo service provides two main pieces of functionality. Firstly it provides named undo-blocks; changes within the undo
block may be reversed or reapplied repeatedly. Secondly it provides in-memory transaction support, which internally uses
the undo-block functionality to provide nested StartTransaction, RollbackTransaction, and CommitTransaction
methods.

The undo blocks are also affected by the persistence service (see page 133) whenever an update is performed.

2.4.1 StartTransaction, RollbackTransaction, andCommitTransaction
This first example will demonstrate how to perform an in-memory transaction on a number of objects within the EcoSpace.
The example will transfer a given amount of money from one bank account to another, it will adjust the CurrentBalance ofeach account and additionally create a transaction object to record the transfer. If an exception of some kind occurs within
the transfer method then all changes will be rolled back, otherwise the in-memory transaction will be committed.

92

2.4 IUndoService

ECO Services

Undo blocks

public class FundsTransfer .....
{...
public static void TransferFunds(IEcoServiceProvider serviceProvider, BankAccountdebitAccount, BankAccount creditAccount, decimal amount){//Perform parameter validation here
IUndoService undoService = serviceProvider.GetEcoService<IUndoService>();undoService.StartTransaction();try{
FundsTransfer transfer = new FundsTransfer(serviceProvider);transfer.AccountDebited = debitAccount;transfer.AccountCredited = creditAccount;transfer.Amount = amount;debitAccount.Balance -= amount;creditAccount.Balance += amount;2//Perform overdraft validation etc here
}
catch
{
undoService.RollbackTransaction();throw;}
undoService.CommitTransaction();}}

1. A reference to the IUndoService is obtained.
2. An in-memory transaction is started.
3. A new FundsTransfer object is created to record the transfer.
4. The FundsTransfer object is associated with the debit and credit accounts.
5. The balances of both the credit and debit accounts are modified.
If all goes as expected the EcoSpace will have a new FundsTransfer object to persist to the data storage along with modified
account balances. If any exception is thrown during the operation the in-memory transaction is rolled back restoring the
EcoSpace to its former state, the FundsTransfer object will no longer exist and the two accounts in question will remain
unaltered.

2.4.2 Undo blocks
Undo blocks provide a mechanism similar to transactions, in fact the transaction mechanism internally uses undo blocks.
Whenever a modification is made to an ECO element (object / property) ECO will check if there is an active undo block. If an
undo block is found, and the element in question is not already in the active undo block, ECO will record the elements
original value in the undo block. In the case of a property the original value will be recorded, in the case of an IObject its
state will be recorded (new, existing, deleted).

10

2.4 IUndoService

ECO Services

Undo blocks

2
Holding a collection of elements plus their original states allows the changes recorded in an undo block to be reversed,
restoring the EcoSpace to the exact state it was in at the point the undo block was created. The undo service may holdmultiple undo blocks, only the topmost undo block is considered to be active therefore changes made within the EcoSpace
will always be applied only to the topmost undo block, new undo blocks are always placed at the top of the undo list. This
makes it possible to have multiple separate transactions being performed within the EcoSpace at the same time, each withthe ability to be independently reversed or reapplied.

One example use of the undo service is when working with multiple forms against a single EcoSpace instance. Each form
could own its own undo block and move it to the top of the list (thus making it active) whenever that form is focused. This
would make it possible to track changes made by an individual form and then undo / redo those changes or update the data
storage with those changes only. Note that it is recommended to have one EcoSpace instance per form whenever possible.

Creating an undo block
Undo blocks in ECO are identified using a unique block name. Although it is possible to hold a reference to an undo blockusing an IUndoBlock it is only advisable to hold such a reference for a short period of time; only as a local variable forexample. The reasoning is quite simple, undo blocks may be removed from the undo service completely (effectively"committed"), accessing the undo block by name will correctly return null whereas holding onto an IUndoBlock reference
would result in your application performing operations on an undo block that is no longer valid.

To ensure that block names are unique, the undo service provides the GetUniqueBlockName method. Executing thismethod will provide your application with a block name that is guaranteed to be unique.

int i = 1;
while (i++ <= 3){
string uniqueName = EcoSpace.Undo.GetUniqueBlockName("Test");EcoSpace.Undo.StartUndoBlock(uniqueName);
MessageBox.Show(uniqueName);}112.4 IUndoService

ECO Services

Working with the undo service

In this example the application creates three undo blocks. Rather than hard-coding the block name as "Test" the application
asks the undo service to return a unique name using "Test" only as a suggested name. The output of the program as each
iteration of the loop is executed is as follows; Test, Test1, Test2.

Note that if the application makes a modification to an object in the EcoSpace and there are no undo blocks present ECO
will automatically create an undo block named "Unnamed", trying to create an undo block with a name that is already in use
will result in a System.InvalidOperationException being thrown.

2.4.3 Working with the undo service
Undo operations
Using the UndoBlock method it is possible to reverse any changes that have been made since the undo block was
activated; i.e. when it became the undo block at the top of the UndoList. Calling UndoBlock will perform the following actions

2

1. Move the block into the RedoList
2. Store the new values instead of the original values, so that the changes may be reapplied later if necessary
3. Restore the original values. This includes modified members, and also created / deleted object instances

Person person1 = new Person(EcoSpace);
person1.FirstName = "Peter";person1.LastName = "Morris";
EcoSpace.Undo.StartUndoBlock("D");//Update undo block D with LastName=Morris
person1.LastName = "Smith";//Restore LastName to Morris//Record modified LastName as Smith//Move block to RedoList
EcoSpace.Undo.UndoBlock("D");12

2.4 IUndoService

ECO Services

Working with the undo service

You may also use the UndoLatest method to undo the block at the top of the UndoList.

Redo operations
Once a block is in the RedoList it is possible to reapply its changes using the RedoBlock method. Whereas an undo
operation reinstates the original state a redo operation will reinstate the modifications made. Calling RedoBlock will performthe following actions

1. Move the block back to the UndoList
2. Store the very original values in case UndoBlock is executed again
3. Restore the modified values

2#region Code from undo example
Person person1 = new Person(EcoSpace);
person1.FirstName = "Peter";person1.LastName = "Morris";
EcoSpace.Undo.StartUndoBlock("D");//Update undo block D with LastName=Morris
person1.LastName = "Smith";//Restore LastName to Morris//Record modified LastName as Smith//Move block to RedoList
EcoSpace.Undo.UndoBlock("D");#endregion//Restore modified value LastName=Smith//Record original value LastName=Morris//Move block back to UndoList
EcoSpace.Undo.RedoBlock("D");You may also use the RedoLatest method to redo the block at the top of the RedoList.

13

2.4 IUndoService

ECO Services

Working with the undo service

Moving blocks
Blocks in the UndoList/RedoList may be rearranged. The purpose of this is to bring a different undo block to the top of the listin order to make it active. As mentioned earlier this is useful for example if you wish to track changes made by the user on aform by form basis so that they may be undone/redone independently of each other. Both the UndoList and RedoList
implement a MoveBlock(int currentIndex, int newIndex) method.

There is a restriction which must be adhered to when moving blocks. When multiple blocks A,B,C contain information about
the same element it makes sense logically to undo changes only in the order C,B,A and to redo changes only in the order
A,B,C. To enforce this rule ECO will always attempt to move the specified block to the top of its list before either undoing or
redoing its contents. If any blocks above the block being moved contain any common elements the process is aborted with a
System.InvalidOperationException. The following example illustrates this point

EcoSpace.Undo.StartUndoBlock("A");
Person person1 = new Person(EcoSpace);
person1.FirstName = "Peter";//Create a new block at the top of the list
EcoSpace.Undo.StartUndoBlock("B");
person1.FirstName = "John";

2//Uncreate person1 - this cannot be done due to undo block B
EcoSpace.Undo.UndoBlock("A");//Revert person1.FirstName to "Peter"
EcoSpace.Undo.UndoBlock("B");In this example it is not possible to undo the changes in block A first because block B is above it and contains a common
element. If this were permitted it would result in block B remaining in the list holding undo information about an object that nolonger exists. To determine whether or not it is possible to move a block to a specific location use the CanMoveBlock(intcurrentIndex, int newIndex) method of either the UndoList or RedoList. If your intention is to undo or redo a blockrather than to simply move it then you can use either CanRedoBlock(string blockName) or CanUndoBlock(string
blockName).

To move a block to the top of the UndoList/RedoList you may use the lists' MoveToTop(string blockName) methods.

Merging blocks
destinationBlockName,stringBoth the UndoList and RedoList contain a MergeBlocks(string
sourceBlockName) method. When two blocks are merged the information stored in the source block is added to thedestination block and then the source block is removed from the list. If both blocks contain information one of these pieces of
information must take priority. The following table describes how this priority is determined

List used

Action taken

UndoList

The oldest information takes priority, the change that occurred latest is discarded.

RedoList

The newest information takes priority, the change that occurred earliest is discarded.

As with moving blocks there is a similar restriction. When two blocks are merged ECO will check the contents of each block
between them, if any of those blocks contain an element common to either the source or destination block then the merge
will not be permitted. The method CanMergeBlock(int currentIndex, int newIndex) will indicate whether themerging of two undo blocks is possible.
142.4 IUndoService

ECO Services

Working with an undo block

Removing blocks
Using UndoBlock and RedoBlock removes the undo block from its owning list but also inserts it into the opposite list.
There are two ways in which an undo block is removed from a block list permanently, without being moved to another list.

1. The data storage is updated - Any block containing changes for any of the objects updated to the data storage will be
removed automatically.
2. Manual removal - Executing a list's RemoveBlock(string blockName) method will remove the block with thespecified name, executing ClearAllUndoBlocks will clear both the UndoList and the RedoList.
Once a block has been removed it cannot be manually re-added to the undo mechanism.

2.4.4 Working with an undo block
Undo blocks expose a limited number of features via the IUndoBlock interface.

2

Retrieving an undo block
An IUndoBlock reference may be obtained from either the UndoList or the RedoList either by name or index.

private void button1_Click(object sender, EventArgs e){
EcoSpace.Undo.StartUndoBlock("In UndoList");//Create a block and move it to the RedoList
EcoSpace.Undo.StartUndoBlock("In RedoList");EcoSpace.Undo.UndoLatest();//Show by nameShowBlockName(EcoSpace.Undo.UndoList["In UndoList"]);ShowBlockName(EcoSpace.Undo.RedoList["In RedoList"]);//Show by index
ShowBlockName(EcoSpace.Undo.UndoList[0]);ShowBlockName(EcoSpace.Undo.RedoList[0]);//Non-existent by nameShowBlockName(EcoSpace.Undo.RedoList["This does not exist"]);//Non-existent by index
ShowBlockName(EcoSpace.Undo.RedoList[99]);}
private void ShowBlockName(IUndoBlock undoBlock){
if (undoBlock == null)
MessageBox.Show("<null>");else
MessageBox.Show(undoBlock.Name);}

Retrieving an undo block using an invalid name or index will return null.

15

2.5 IObjectFactoryService

ECO Services

Members of IUndoBlock• Name - The name give to the undo block.
• ContainsChanges - Returns false if the undo block is empty, otherwise returns true.
• GetChangedObjects() - Returns an IObjectList. Each IObject is the identity of the object instance that has been modified.
• Subscribe(ISubscriber subscriber) - The subscriber is notified whenever the contents of the undo block change, when the
undo block moves between the RedoList and UndoList, or the when undo block is removed from the undo service
altogether.

2.5 IObjectFactoryService
This service provides an alternative way of creating instances of modeled classes. Ordinarily a new instance of a modeledclass is created in an application like so//EcoSpace implements IEcoServiceProvider, so we can pass the EcoSpace
OrderLine newLine = new OrderLine(EcoSpace);
CurrentOrder.Lines.Add(newLine);2or to create an instance from a method of another modeled class like so//Business classes don't have an EcoSpace, so we pass the IEcoServiceProvider
OrderLine newLine = new OrderLine(this.AsIObject().ServiceProvider);this.Lines.Add(newLine);

Creating a new object instance is so trivial that it may seem unnecessary to have a service for this purpose, however, theobject factory service makes it easy to create instances of modeled classes when the type is not know until runtime, or isdetermined by reading model information.

Creating an object instance
The first approach is the one most similar to the previous example. An instance will be created by passing the System.Type.

IObjectInstance instance = EcoSpace.ObjectFactory.CreateNewObject(typeof(Person));
Person person1 = instance.GetValue<Person>();First an IObjectInstance is created by instructing the object factory service to create a type of Person. Next the Person
instanced is retrieve from the IObjectInstance object locator. Note that the IObjectInstance reference may be used as aparameter for various other service methods; it is also possible to use person1.AsIObject() to retrieve the IObjectInstance.

The second approach is to use a string to identify the name of the class to create. Unlike the previous approach thisapproach will not cause a compile error if you change the name of the Person class.
IObjectInstance instance = EcoSpace.ObjectFactory.CreateNewObject("Person");The final approach creates an instance identified by an IClass. An IClass is an interface holding information about a specific162.5 IObjectFactoryService

ECO Services

class in the model, this instance holds additional information regarding persistency etc. This is covered in the type systemservice ( see page 147) section of this document.

In the model above the Person class may now contain multiple pieces of contact information. The ContactInformation classitself is abstract so cannot be instantiated, the subclasses TelephoneNumber, EmailAddress, and PostalCode are all
concrete classes and therefore may be instantiated and associated with a person. When the user interface offers the user
the opportunity to add some contact information for a Person it would be nice if the possible types were determined
automatically rather than having to hard code them and have to remember to update the code each time a new kind ofcontact information was added to the model. This is where the IClass comes in. The following code example will show how
to perform this task; it will use some code from a service which has not get been covered so feel free to skip directly to thepoint in the example where the instance is created.

private void ButtonNewContactInformation_Click(object sender, EventArgs e){//Clear the context menu
ContextCreateContactInformation.Items.Clear();//Find the base IClass
IClass contactInformationClass =EcoSpace.TypeSystem.GetClassByType(typeof(ContactInformation));//Recursively add all types
AddClassToMenu(contactInformationClass);
172

2.6 IVariableFactoryService

ECO Services//Show the menu
Point popupPoint = new Point(ButtonNewContactInformation.Width / 2,
ButtonNewContactInformation.Height / 2);ContextCreateContactInformation.Show(ButtonNewContactInformation, popupPoint);}
private void AddClassToMenu(IClass contactInformationClass){//If this is not abstract then create a menu item
if (!contactInformationClass.IsAbstract){
var menuItem = new ToolStripMenuItem(contactInformationClass.Name);
ContextCreateContactInformation.Items.Add(menuItem);menuItem.Tag = contactInformationClass;menuItem.Click += MenuItemCreateClass_Click;}//Now recursively add any sub classes
foreach (IClass subClass in contactInformationClass.SubTypes)
AddClassToMenu(subClass);}
private void MenuItemCreateClass_Click(object sender, EventArgs e){//Normally we would pass the type to a specific form to edit//but for this example we will just add directly to the list
ToolStripMenuItem menuItem = (ToolStripMenuItem)sender;
IClass contactInfoClass = (IClass)menuItem.Tag;

2//Create the new instance based on IClass
IObjectInstance instance = EcoSpace.ObjectFactory.CreateNewObject(contactInfoClass);
ContactInformation newContactInfo = instance.GetValue<ContactInformation>();CurrentPerson.ContactInformation.Add(newContactInfo);}

2.6 IVariableFactoryService
This service enables the develper to create a number of IElement based objects which may then be used in various different
parts of the ECO framework. The IElement interface is prevalent throughout the ECO framework as it is used to represent
instances of modeled classes (IObject ultimately descends from IElement), property values (Int32, String, etc), andassociation ends (IObjectList). These variables may then be used in various other services, for example the OCL Service
may evaluate OCL expressions which include variable names (a bit like parameterized queries). Although in mostcircumstances it is anticipated that the developer will want to use a handle for declaring variables this service allows you tocreate variables in code, which is useful in methods of modeled classes where you have no design surface onto which todrop a VariableHandle etc.

Creating a constant
A constant is most useful when adding an event derived column to an ECO handle. One very useful implementation of thisfunctionality is when you have a many to many association between two classes and wish to show the user a selection of
options with a check box, checking the check box will add the item to the association whereas unchecking the check box willremove the option from the association.

18

2.6 IVariableFactoryService

ECO Services

The model above allows a single instance of BookingOption to be associated with many bookings, and for a booking to have
many BookingOption instances (extras). The following user interface displays a booking form with a list of all available
BookingOption instances rather than only the ones associated with the booking in question.

2
1. An expression handle was added with the expression "BookingOption.allInstances".
2. The columns were edited and a new EventDerivedColumn was added using the drop down list on the Add button.
3. The DeriveValue and ReverseDeriveValue events on this expression handle were implemented like so

private void ehBookingOptions_DeriveValue(object sender, DeriveEventArgs e){//Get the BookingOption in question
BookingOption option = e.RootElement.GetValue<BookingOption>();switch (e.Name){case "Included"://Result is true if this option is in CurrentBooking.Extras
bool isIncluded = CurrentBooking.Extras.Contains(option);//Create the boolean constant and specify it as the result
e.ResultElement = EcoSpace.VariableFactory.CreateConstant(isIncluded);//Additional: We need to tell ECO to update the ticks in the boxes whenever//the size of CurrentBooking.Extras changes//01: Get the IProperty for CurrentBooking.Extras
int propIndex = Booking.Eco_LoopbackIndices.Extras_MemberIndex;
IProperty extrasProperty =CurrentBooking.AsIObject().Properties.GetByLoopbackIndex(propIndex);//02: Subscribe to it
extrasProperty.SubscribeToValue(e.ResubscribeSubscriber);break;
default:
throw new NotImplementedException(e.Name);}}
private void ehBookingOptions_ReverseDeriveValue(object sender, ReverseDeriveEventArgs e){//Get the BookingOption in question
BookingOption option = e.RootElement.GetValue<BookingOption>();switch (e.Name){case "Included":19

2.6 IVariableFactoryService

ECO Services

bool isIncluded = (bool)e.Value;//Add or remove it from the list. This will update the//UI automatically because when we derived the value we//subscribed to CurrentBooking.Extras
if (isIncluded)
CurrentBooking.Extras.Add(option);
else
CurrentBooking.Extras.Remove(option);break;
default:
throw new NotImplementedException(e.Name);}}

Creating a list of objects
When creating an IObjectList you have the option of either allowing IObject instances representing any modeled class, orones which represent a specific modeled class and its subclasses only. To create an untyped object list you would use theCreateUntypedObjectList(bool allowDuplicates) method, this kind of list is useful when you wish to update thedata storage with a collection of objects rather than updating all dirty objects.

//Create three people
Person person1 = new Person(EcoSpace);
Person person2 = new Person(EcoSpace);
Person person3 = new Person(EcoSpace);//Create the object list
IVariableFactoryService vfs = EcoSpace.VariableFactory;
IObjectList objectsToPersist = vfs.CreateUntypedObjectList(false);//Add only the locators for person1 and person3
objectsToPersist.Add(person1.AsIObject());
objectsToPersist.Add(person3.AsIObject());//Now update the data storage with only those two objects
EcoSpace.Persistence.UpdateDatabaseWithList(objectsToPersist);

To create a typed object list you would use either• CreateTypedObjectList(Type type, bool allowDuplicates)• CreateTypedObjectList(IClass umlClass, bool allowDuplicates)
The first accepts a .NET Type whereas the second accepts an IClass representing the modeled class to use, this IClass
reference may be obtained from the model at runtime using the type system service ( see page 147). This kind of list is
useful when you require a strongly typed list of objects, for example if you wish to create a list in code rather than using an
OCL ( see page 22) expression and then present that list to the user via a ReferenceHandle. In this case a grid isconnected to rhRoot which has its StaticValueTypeName property set to Collection(Person).

//Create three people
Person person1 = new Person(EcoSpace);
person1.FirstName = "Peter";person1.LastName = "Morris";
Person person2 = new Person(EcoSpace);
person2.FirstName = "John";
person2.LastName = "Smith";
Person person3 = new Person(EcoSpace);
person3.FirstName = "Fred";
person3.LastName = "Jones";202

2.6 IVariableFactoryService

ECO Services//Create the object list
IVariableFactoryService vfs = EcoSpace.VariableFactory;
IObjectList objectsToPresent = vfs.CreateTypedObjectList(typeof(Person), true);//Add only the locators to the list
objectsToPresent.Add(person1.AsIObject());
objectsToPresent.Add(person2.AsIObject());
objectsToPresent.Add(person3.AsIObject());
objectsToPresent.Add(person2.AsIObject());
objectsToPresent.Add(person1.AsIObject());//Now present the list to the user interface
rhRoot.SetElement(objectsToPresent);

2

Creating a variable list
A variable list is a collection of values cross referenced by name. Variable lists are useful when you need to execute an OCL
expression which refers to multiple object instances. For example the following expression would select all bookings for a
specific customer and flight combination, note that the prefix var_ is purely optional and only used here for clarity

PropertyBooking.allInstances->select(customer = var_Customer)->select(flight =var_Flight)

The above expression could have been implemented with constants rather than variables, in fact when you create a variable
list you may add constants or variables. However, variable lists may also be used in a similar way to how query parameters
work in a SQL statement within a standard DB application.

IVariableFactoryService vfs = EcoSpace.VariableFactory;//Create three people
Person person1 = new Person(EcoSpace);
person1.FirstName = "Peter";person1.LastName = "Morris";
Person person2 = new Person(EcoSpace);
person2.FirstName = "John";
person2.LastName = "Smith";
Person person3 = new Person(EcoSpace);
person3.FirstName = "Fred";
person3.LastName = "Jones";
EcoSpace.UpdateDatabase();//Create the variable
IModifiableVariableList variables = vfs.CreateVariableList();IElement lastNameVar = vfs.CreateVariable(typeof(string));21

2.7 Query services

ECO Services

variables.Add("var_LastName", lastNameVar);//Define a list of values to loop throughstring[] lastNames = new string[] {"Morris", "Smith", "Jones"};//Define the OCL expression using var_LastName
string ocl = "Person.allInstances->select(lastName = var_LastName)";//Now loop through each value
foreach (string currentLastName in lastNames){//Set the value of the variable
lastNameVar.AsObject = currentLastName;//Execute the query
IList<Person> people = EcoSpace.OclPs.Execute(null, variables, ocl, -1,0).GetAsIList<Person>();//Now show the result
foreach (Person currentPerson in people){
string message = string.Format("{0} -> {1} {2}",currentLastName,currentPerson.FirstName,currentPerson.LastName);
MessageBox.Show(message);}}

2
The above example creates a variable of type System.String, the return value of CreateVariable is an IElement. The variable
is then added to a variable list with the name var_LastName. Within the loop it is possible to set the value of the variable bysetting lastNameVar.AsObject. When the OCL expression is evaluated the text var_LastName will substituted with thevariable's value instead. Just like query parameters this approach prevents the developer from having to escape special
query characters in order to ensure the query always remains valid (see SQL Injection http://en.wikipedia.org/wiki/SQL_injection, see also http://xkcd.com/327).

The important parameters being passed to OclPs.Execute are variables and ocl. The other parameters will be
explained in the IOclPsService ( see page 25) section.

2.7 Query services
These ECO services process queries that are based on the Object Constraint Language (OCL) in order to retrieve
information from the ECO cache. When an EcoSpace is persistent ECO will also translate the OCL query into the querylanguage of the data storage (usually SQL) to ensure that the data necessary to process the query is loaded into the ECO
cache.

As not all OCL operations are supported by all target data storages OCL query support is cleanly separated into three
distinct services. Each service is described in a particular order; the service being described is capable of processing a
superset of any previously described OCL services.

Format of OCL expressions
All OCL expressions start with a root context. This context may be a modeled class type, an instance of a modeled class, asingle element (such as an integer or string), or a collection. The following expression shows how to use the modeled
Person class to start the expression, and return all instances of that class:
Person.allInstances222.7 Query services

ECO Services

The above expression would result in a collection of Person instances. From this context it is possible to either perform acollection operation or a member operation. Like many OOP languages to identify a member the context and member nameare separated by a period, collection operations are separated by the token ->//Returns the first person in the collection
Person.allInstances->first//Returns the DateOfBirth of the first person in the collection
Person.allInstances->first.dateOfBirth
When a member name is appended directly to a collection the OCL parser will automatically iterate through each element
within the collection and return a collection of the member specified.
//Returns a collection of DateTime, one for each person in the collection
Person.allInstances.dateOfBirth

OCL expressions with a specific context
It is possible to evaluate OCL expressions on an individual instance of a modeled class. For example when defining OCL
based validity constraints on a class those expressions would be evaluated in the context of an instance of that class. Whenevaluating against an instance the context is assumed to be that instance. The keyword self may be used within theexpression to identify the root object

2

self.dateOfBirth
The self keyword is case sensitive and must always be written in lowercase. When evaluating an expression against a root
this does not mean that the OCL expression must start with that root object. At any point within an OCL expression it ispossible to reference a modeled class by its name and use that as the context of the expression.
//using self other than as the context of the expression//self references an instance of Customer
Booking.allInstances->select(bookedBy = self)//Returns the same result as
self.bookings
The above two expressions will return the same result. The first expression is less efficient than the second as it will first
retrieve all instances of Booking and then filter the list down to only those booked by the root object (Customer), the second
expression uses the Customer as the context of the expression to find its bookings which results in retrieving only bookings
assigned to this Customer and not having to filter the list afterwards.

Changing context within an expression
In the previous examples you can see that you can switch the context to a class despite having a root object to evaluateagainst. It is in fact possible to switch the context to a class at any point within an expression
self.eventsAttended->intersection(
Event.allInstances->orderDescending(date)->subSequence(1, 5))This more complicated expression uses a Customer as the root to evaluate the expression against (and therefore by default
the initial context). It switches the context to all events attended by that customer and then filters that event list down to
include only the last 5 events held. The last 5 events held are determined by selecting all Event instances, ordering thecollection by their date (descending), and then selecting the top 5.

23

2.7 Query services

ECO Services

Members with the same name as a class

In this simple model it makes sense that the association from a booking to the Payment class should be named "Payment",
the association from a booking to the event should be named "Event", and that the association from Booking to Customer
should be named "Customer".

When evaluating an expression such as the following (using Customer as the root)2

bookings->orderBy(date)
it is quite clear that the start of the expression refers to the member Customer.Bookings. When evaluating an expression thatstarts with a member that has the same name as a class in your model it is not clear whether you intend the OCL evaluator
to interpret the token as the member name or if you intend to switch the context to another class.
//Error - Event is interpreted as a classevent.cancelled
In such a case you would precede the member name explicitly with the keyword self.
self.event.cancelled
By now you may have noticed that all of the examples in this section use a lowercase initial letter for members and an
uppercase initial letter for class names. This is a standard in the OCL and although it is not enforced in ECO it does help toclarify your intentions. It is good practise to precede association names with the self keyword when the association name issingular (event) rather than plural (events).

Aliases
Building on the following example of explicitly identifying a token as a member and not a class there is a situation where it is
not logical to precede the member name with the self keyword. The following OCL expression returns a collection of events
a Customer has bookings for.
//Using a customer instance as the root
self.bookings.event
In this expression it is clear that the token "event" refers to the association Booking.Event because it is connected directlywith a period, identifying it as a member; the previous expression would return a collection of Event instances. However, thefollowing expression (using a Booking as the root) is not valid and will not evaluate at all.
self.bookings->select(event.cancelled)
This expression will not evaluate because the token "event" is not preceded with a period, identifying it as a member ofBooking, but unlike in the previous scenario it is not possible to precede "event" with the self keyword because self will
always refer to the root that the expression is being evaluated against; in this case that would be an instance of Customer,
and Customer does not have an Event association.

The solution to this is to use an alias for each booking, so that the "event" token can be replaced with "alias.event" which24

2.7 Query services

ECO Services

IOclPsService

then clearly identifies it as a member and not a class. Aliases are identified with the | (pipe) symbol and look like this//Long format
self.bookings->select(currentBooking | currentBooking.event.cancelled)//Shorter format
self.bookings->select(b | b.event.cancelled)

ComparisonsComparisons in the OCL are made using a single equals sign.
self.bookings->select(date = DateTime.today)

2.7.1 IOclPsService
OclPs is an abbreviation for Object Constraint Language - (evaluated in) Persistent Storage. The IOclPsService translates
OCL expressions into the data storage specific query language (usually SQL) in order to perform queries without first having
to load object contents into the local ECO cache. The IOclPsService never retrieves any object data, instead it always
returns a result type of IObjectList, which is a list of "object locators", an object's contents are not loaded until you reference
the .NET object from the locator, for example

IObject myLocator = { retrieved from somewhere else };//ECO will ensure the object is loaded if not alreadyConsole.WriteLine(myLocator.GetValue<Person>().FirstName);

This is an important detail because it means that the only information returned from the data storage is a list of object ID'srather than the entire contents of the objects. Without this ability evaluating OCL could prove to be very expensive. Take asimple harmless looking OCL query such as the following

Person.allInstances->select(dateOfBirth = #1978-01-01)

If this query were to be evaluated in memory ECO would first have to load every instance of the Person class into memory
and then filter the list down to only those with the specific date of birth. If there were millions of Person objects in the data
storage this would obviously require a lot of memory and also a lot of network bandwidth.

1. [DB] Select FirstName, LastName, etc from Person;
2. [ECO] Receive row data for millions of Person objects
3. [ECO] Filter the list where the DateOfBirth matches
4. [App] Receive a collection of object locators that match the criteria
When this same query is executed with the IOclPsService the filtering will be performed by the data storage and only the
ID's of the matching objects will be returned.

1. [DB] Select FirstName, LastName, etc from Person WHERE DateOfBirth = {format specific to the database};
2. [ECO] Receive only the IDs of only the rows that match the critera
3. [App] Receive a collection of object locators that match the criteria
Note: The ECO component "OclPsHandle" uses the IOclPsService to execute its expression.
25

22.7 Query services

ECO Services

IOclPsService

As you can see there is a big gain from using the IOclPsService when there is a large number of objects involved such as
SomeClass.allInstances where it is anticipated there will be a lot of object instances (Country.allInstances would
be okay, but Person.allInstances might prove problematic). This also includes "rooted" queries such as
self.Residents where "self" is in instance of Country, in this case it is anticipated that a country is likely to have many
residents so you woud really want any ->select(....) to be performed in the data storage rather than in memory.

Restrictions of the IOclPsService
1. It does nottake into
account
unsavedchanges.

It is important to note that as the filtering is performed by the data storage it is only capable of providing
a list of objects that have been persisted. If for example you were to modify the DateOfBirth of analready persisted Person in your local EcoSpace cache so that it does not match the date #1978-01-01,
and then not persist those changes, when you execute the OclPs expression
Person.allInstances->select(dateOfBirth = #1978-01-01) the result would still include
the modified Person. There is a way of ensuring the collection represents both changes that have andhave not yet been persisted using the in-memory OCL expression AllLoadedObjects, which will becovered in the section IOclService ( see page 44).

The result type is always an IObjectList. This means that it is not possible to end your OCL query with
2. It can only
operators such as ->size to return the number of items in the list, nor can you for example return a list
return acollection of of unique values using ->collect(dateOfBirth).
objectlocators.
3. Is supports
only asubset of
OCL.

It is not possible to map all OCL operations to SQL for example. As a consequence not all OCL
operations are supported.

4. Derived
members
are not
supported.

Members in the model marked as derived are calculated in-memory upon request. These values arenever persisted to the data storage and as a consequence it is not possible to use them in OCL
expressions evaluated by this service.

Executing queriesThis service has a number of overloaded methods for evaluating OCL expressions, each of which is named Execute().
The most simple overload accepts only an OCL expression to evaluate.

IObjectList locators;
string query = "Person.allInstances->select(dateOfBirth = #1978-01-01)";
locators = EcoSpace.OclPs.Execute(query);

The above example selects the object locators only for the Person objects that were born on January the 1st, 1978. Notethat no Person data is returned from this query. Any time you access locator.AsObject or locator.GetValue<T> theindividual object will be loaded. To retrieve multiple objects in as few DB trips as possible you can either use the
GetAsIList<T>() method on the result or use the EnsureRange() method on the IPersistenceService ( see page 133)
to pre-load. Note that evaluating an in-memory query against the result using the IOclService will also fetch object data into
the local EcoSpace cache. The following example not only identifies which Person objects match the criteria but also loads
the objects' contents into the cache.

IList<Person> people;
string query = "Person.allInstances->select(dateOfBirth = #1978-01-01)";
people = EcoSpace.OclPs.Execute(query).GetAsIList<Person>();26

2

2.7 Query services

ECO Services

IOclPsService

The next overload is similar to the first except that it additionally takes two integer parameters.
Name

Type

PurposemaxAnswers

Int32

Limits the number of object locators returned.

offsetInt32

Zero based index of the first object in the list to return.

This overload is useful for either data paging (in which case you should have an ->orderBy(...) operation in your OCL
expression to ensure a consistent order) or simply to reduce the number of objects returned on a search screen for example.

int maxAnswers = 50;
IList<Person> people;
string query = "Person.allInstances->select(dateOfBirth = #1978-01-01)";
people = EcoSpace.OclPs.Execute(query, maxAnswers, 0).GetAsIList<Person>();

The next overload expects not only an OCL expression to evaluate but also a root context to evaluate it against. The root is
an IElement, which means that the context could be either a single instance of a modeled business class or a collection of
instances. The root provided is always referenced as the OCL keyword self in expressions. Note that although IElement
can be used to represent other values such as strings, integers, and dates it is not possible to use these as a root context forevaluating an OclPs expression.

City city = { retrieved from somewhere else };IList<Person> people;
string query = "self.residents->select(dateOfBirth = #1978-01-01)";
people = EcoSpace.OclPs.Execute(city.AsIObject(), query).GetAsIList<Person>();/*Equivalent to SQL
select FirstName, LastName, etc from Person
where City = 132232 and DateOfBirth = { DB specific date format};*/

It is not mandatory to use the root context as the root of the OCL evaluation. The following example produces the same
result as the previous example despite starting the OCL expression with Person.allInstances.

City city = { retrieved from somewhere else };IList<Person> people;
string query = "Person.allInstances->select(city = self)->select(dateOfBirth =#1978-01-01)";
people = EcoSpace.OclPs.Execute(city.AsIObject(), query).GetAsIList<Person>();The final overload accepts a number of additional parameters.
Name

Type

Purposeroot

IElement

Acts as a context for the keyword self in OCL expressions. This parameter may benull.

variableList

IExternalVariableList This parameter provides a variable list which is used when parsing the OCL
expression. Any unidentified part of the expression that looks like it should be a literalvalue is checked against this variable list to see if it is a named value. This parameter
may be null.

expression

StringmaxAnswers Int32

The OCL expression to evaluate.
The total number of answers to return.
27

2

2.7 Query services

offsetInt32

The IVariableFactoryService (ECO Services

IOclPsService

The zero based index of the first result to return.
see page 18) section of this document provides an example for the use of this overload.

2.7.1.1 OCL operations supported by IOclPsService
2.7.1.1.1 +SourceInt32

Parameters

Int32 value

ResultInt32

Description
Returns the result of adding the specified value to the source.

2
Example
1 + 2

Notes
Additional overloads exist for Int64 and Double.

2.7.1.1.2 Source

Int32

Parameters

Int32 value

ResultInt32

Description
Returns the result of subtracting the specified value from the source.

Example
3 - 2

Notes
Additional overloads exist for Int64 and Double.

28

2.7 Query services

ECO Services

IOclPsService

2.7.1.1.3 *SourceInt32

Parameters

Int32 factor

ResultInt32

Description
Returns the result of multiplying the source by the factor.

Example
5 * 5

Notes

2

Additional overloads exist for Int64 and Double.

2.7.1.1.4 /SourceInt32

Parameters

Int32 divisor

ResultInt32

Description
Returns the result of dividing the source by the divisor.

Example10 / 2

Notes
Additional overloads exist for Int64 and Double.

2.7.1.1.5 <Source<Any>Parameters<Any> value

ResultBoolean29

2.7 Query services

ECO Services

IOclPsService

Description
Returns true if the source is less than the parameter.

Example6 < 122.7.1.1.6 <=Source<Any>Parameters<Any> value

ResultBooleanDescription
Returns true if the source is less than or equal to the parameter.

2

Example6 <= 122.7.1.1.7 <>Source<Any>Parameters<Any> value

ResultBooleanDescription
Returns true if the source is not equal to the parameter.

Example6 <> 122.7.1.1.8 =Source<Any>Parameters<Any> value

ResultBooleanDescription
Returns true if the source is equal to the parameter.

30

2.7 Query services

ECO Services

IOclPsService

Example1 = 1

2.7.1.1.9 >Source<Any>Parameters<Any> value

ResultBooleanDescription
Returns true if the source is greater than the parameter.

Example2

12 > 62.7.1.1.10 >=Source<Any>Parameters<Any> value

ResultBooleanDescription
Returns true if the source is greater than or equal to the parameter.

Example12 >= 62.7.1.1.11 AllInstances
Source<Type>

Parameters
ResultCollection(<Instance>)

Description
Returns all instances of the source type. The type can be a business class such as Person. In OCL and EAL the type canalso be an enumeration type used in the model such as Gender or OrderState

31

2.7 Query services

ECO Services

IOclPsService

ExamplePerson.allInstances

2.7.1.1.12 Average
Collection(Decimal)

SourceParameters

DecimalResultDescription
Returns the average of a collection of numerical values.

ExamplePerson.allInstances.age->average2
Notes
Additional overloads exist for Int32, Int64, and Double; each of which return Double and not Decimal.

2.7.1.1.13 Difference
SourceCollection(<Any>)

Parameters

Collection(<Any>) value

ResultCollection(<Any>)

Description
Returns a copy of the source collection minus all elements in the parameter.

Examplevar_Person1.friends->difference(var_Person2.friends)

Notes
To remove a single element see Excluding.

2.7.1.1.14 Div
SourceInt32

Parameters

Int32 divisor

ResultInt32

32

2.7 Query services

ECO Services

IOclPsService

Description
Returns the result of dividing the source by the divisor.

Example10 div 2

2.7.1.1.15 Exists
SourceCollection(<Any>)

Parameters

Boolean expression

ResultBooleanDescription

2

Returns true if the parameter results in true for any of the items in the collection.

Exampleself.friends->exists(f | f.Gender = Gender::Male)

Notes
The syntax within the brackets is similar to a lambda expression, where each item in the Collection(Person) is assigned thealias "f" for the rest of the expression.

2.7.1.1.16 ForAll
SourceCollection(<Any>)

Parameters

Boolean expression

ResultBooleanDescription
Returns true if the parameter results in true for every item in the collection.

Exampleself.friends->forAll(f | f.Gender = Gender::Male)The person in question only has male friends.

33

2.7 Query services

ECO Services

IOclPsService

2.7.1.1.17 Implies
SourceBooleanParameters

Boolean comparison

ResultBooleanDescription
The following table shows possible permutations and the result.

SourceParameter

Result

False

False

True

False

True

True

True

False

False

True

True

True

2
ExampleIf a Weather class has two boolean attributes "Raining" and "Cloudy" the following constraint would ensure that it must be
cloudy in order for it to be raining.

raining implies cloudy

2.7.1.1.18 Includes
SourceCollection(<Any>)

Parameters<Any> value

ResultBooleanDescription
Returns true if the parameter exist in the source.

Exampleself.friends->includes(self.Father)
The person in question is friends with his/her father.

2.7.1.1.19 Intersection
SourceCollection(<Any>)

Parameters

Collection(<Any>) value

ResultCollection(<Any>)34

2.7 Query services

ECO Services

IOclPsService

Description
Returns a collection of all elements in the source that are also in the parameter.

Exampleself.debtors->intersection(self.creditors)
Returns a collection of people who are both debtors and creditors.

2.7.1.1.20 IsEmpty
SourceCollection(<Any>)

Parameters
ResultBoolean2

Description
Returns true if a collection has no elements in it.

Exampleself.debtors->isEmpty
Returns True if the person has no debtors.

Notes
IsEmpty may also be used on an association to a single object to check if it has a value. It is recommended that IsEmpty isused in such a case rather than comparing the associated object with nil.
self.linkToSingleObject = nil [Incorrect]
self.linkToSingleObject->isEmpty [Correct]

2.7.1.1.21 IsNull
Source<Any>Parameters
ResultBooleanDescription
Returns true if the source is null.

Exampleself.firstName.isNull

35

2.7 Query services

ECO Services

IOclPsService

Notes
To check an association to a single object use isEmpty.

2.7.1.1.22 Length
StringSourceParameters

Int32

ResultDescription
Returns the length of a string.

ExamplePerson.allInstances->select(firstName.length < 2)

2

2.7.1.1.23 MaxValue
SourceCollection(Int32)

Parameters
ResultInt32

Description
Returns the largest value found in the source collection.

ExamplePerson.allInstances->select(height = Person.allInstances.height->maxValue)
Selects all of the tallest people.

Notes
Additional overloads exist for Int64, Double, and Decimal.

2.7.1.1.24 MinValue
SourceCollection(Int32)

Parameters
ResultInt32

36

2.7 Query services

ECO Services

IOclPsService

Description
Returns the minimum value found in the source.

ExamplePerson.allInstances->select(height = Person.allInstances.height->minValue)
Selects all of the shortest people.

Notes
Additional overloads exist for Int64, Double, and Decimal.

2.7.1.1.25 Mod
SourceInt32

Parameters

Int32 modulo

ResultInt32

2

Description
Returns the result of performing a modulus on the source using the modulo.

Example17 mod 10
Returns 7.

2.7.1.1.26 Not
SourceBooleanParameters
ResultBooleanDescription
Returns the result of performing a logical NOT operation on the source.

ExampleUser.allInstances->select(not suspended)37

2.7 Query services

ECO Services

IOclPsService

2.7.1.1.27 NotEmpty
Collection(<Any>)

SourceParameters

BooleanResultDescription
Returns true if a collection has at least one element in it.

Exampleself.debtors->notEmpty
Returns True if the person has debtors.

2

Notes
NotEmpty may also be used on an association to a single object to check if it has a value. It is recommended that NotEmpty
is used in such a case rather than comparing the associated object with nil.
self.linkToSingleObject <> nil [Incorrect]
self.linkToSingleObject->notEmpty [Correct]

2.7.1.1.28 OrderBy
SourceCollection(<Any>)

Parameters<Any> expression

ResultCollection(<Any>)

Description
Returns a collection based on the source which has been ordered into ascending order based on the value in the parameter.

Exampleself.debtors->orderBy(oldestUnpaidDebt)
Returns all debtors in order of the debtor with the oldest unpaid debt first.
self.debtors->orderBy(lastName, firstName)
Returns all debtors ordered by their last name, for debtors with the same last name, the first name is used as a secondary
key

Notes
Using OrderBy it is possible to order by multiple keys, but only in ascending order. To order by multiple items in different
directions, use OrderGeneric.

38

2.7 Query services

ECO Services

IOclPsService

2.7.1.1.29 OrderDescending
SourceCollection(<Any>)

Parameters<Any> expression

ResultCollection(<Any>)

Description
Returns a collection based on the source which has been ordered into descending order based on the value in theparameter.

Exampleself.debtors->orderDescending(amountOwed)
Returns all debtors in order of the debtor with the greatest amount of debt first.

2
Notes
Using OrderDescending it is possible to order by multiple keys, but only in ascending order. To order by multiple items in
different directions, use OrderGeneric.

2.7.1.1.30 OrderGeneric
SourceCollection(<Any>)

Parameters<Any> expression
SortDirection direction

ResultCollection(<Any>)

Description
Returns a collection based on the source which has been ordered as specified by the parameters. OrderGeneric takes aminimum of two parameters; the first identifies the sort expression, and the second identifies the sort direction (ascending ordescending). These two parameters may be repeated multiple times in order to provide sorting by multiple items.

Exampleself.debtors->orderGeneric(oldestUnpaidDebt, OclSortDirection::ascending, amountOwed,
OclSortDirection::descending)
Returns all debtors primarily in order of the debtor with the oldest unpaid debt first, and then secondarily by the debtor
owning the highest amount of money.

OldestUnpaidDebt

AmountOwed

2008-01-01

1000.00

2008-01-01

800.002008-01-01

700.0039

2.7 Query services

ECO Services

IOclPsService

2008-02-01

1000.00

2008-02-01

550.002.7.1.1.31 Reject
SourceCollection(<Any>)

Parameters

Boolean expression

ResultCollection(<Any>)

Description
Returns a collection based on the source excluding any elements where the boolean expression evaluated to True.

Exampleself.friends->reject(age < 18)2This is functionally equivalent to the following
self.friends->select(age >= 18)

2.7.1.1.32 SelectSourceCollection(<Any>)

Parameters

Boolean expression

ResultCollection(<Any>)

Description
Returns a collection based on the source including only elements where the boolean expression evaluated to True.

ExampleUser.allInstances->select(u | u.firstName = 'Peter')->select(u | u.lastName = 'Morris')
Returns all users with the name "Peter Morris".
this is equivalent toUser.allInstances->select(u | (u.firstName = 'Peter') and (u.lastName = 'Morris'))2.7.1.1.33 Size
SourceCollection(<Any>)

Parameters
ResultInt3240

2.7 Query services

ECO Services

IOclPsService

Description
Returns the size of a collection.

ExamplePerson.allInstances->select(p | p.friends->size >= 5)
Returns all people with 5 or more friends.

2.7.1.1.34 SqlLike
SourceStringParameters

String pattern

ResultBooleanDescription

2

Returns True if the source matches the pattern specified in the parameter. This is similar to the LIKE statement in SQL.

ExamplePerson.allInstances->select(p | p.lastName.sqlLike('%orris'))Returns a collection of people who have a last name ending with "orris".

Notes
When evaluated in memory, this operation is case sensitive, but some databases treats the LIKE operator as acaseinsensitive operator. For guaranteed case insensitivity, use sqlLikeCaseInsensitive% is used to denote a match with zero or more unknown letters_ is used to denote a match with exactly one unknown letter.

Value

Match

Morris

True

Norris

True

Orris

False

2.7.1.1.35 SqlLikeCaseInsensitive
SourceStringParameters

String pattern

ResultBoolean41

2.7 Query services

ECO Services

IOclPsService

Description
Returns True if the source matches the pattern specified in the parameter. This is similar to the LIKE statement in SQL butcase insensitive.

ExamplePerson.allInstances->select(lastName.sqlLikeCaseInsensitive('%orris'))Returns a collection of people who have a last name ending with "orris", regardless of case.

Notes
Unlike the SqlLike operation the comparison is case insensitive.

Value

Match

Morris

True

Norris

True

Orris

True

2

2.7.1.1.36 Sum
SourceCollection(Int32)

Parameters
ResultInt32

Description
Returns the sum of the values in the collection

ExamplePerson.allInstances->select(debts.value->sum > 10000)
Selects all people who have a debt of at least 10,000.

Notes
Additional overloads exist for Int64, Double, and Decimal.

2.7.1.1.37 ToLower (String)
SourceStringParameters
ResultString42

2.7 Query services

ECO Services

IOclPsService

Description
Converts a string value to lower case.

ExamplePerson.allInstances->select(firstName.toLower = 'peter')
Returns people with the first name "Peter", "pETeR" or any other case-variation of "peter".

Notes
When selecting you may wish to use SqlLikeCaseInsensitive as it permits the use of wildcards. The ToLower operation ismostly used for presenting data in a user interface in a specific format.

2.7.1.1.38 ToUpper(String)
StringSource2

Parameters
StringResultDescription
Converts a string value to upper case.

ExamplePerson.allInstances->select(firstName.toLower = 'PETER')
Returns people with the first name "Peter".

Notes
When selecting you may wish to use SqlLikeCaseInsensitive as it permits the use of wildcards. The ToUpper operation ismostly used for presenting data in a user interface in a specific format.

2.7.1.1.39 Union
SourceCollection(<Any>)

Parameters

Collection(<Any>) value

ResultCollection(<Any>)

Description
Returns a collection of all elements in the source combined with all elements in the parameter.

432.7 Query services

ECO Services

IOclService

Exampleself.debtors->union(self.creditors)
Returns a collection of people who are either a debtors or creditors of a person.

2.7.2 IOclService
Ocl is an abbreviation for Object Constraint Language. The IOclService evaluates OCL expressions in memory. Objects arefetched from the data storage automatically if they are required in order to evaluate the expression passed, the following
expression evaluated against a PurchaseOrder would automatically load all PurchaseOrderLine objects related to the order
if they were not already loaded into the local EcoSpace cache.

self.lines.value->sum

Due to the fact that the IOclService evaluates in memory it is advisable that you do not evaluate expressions such as
Class.allInstances unless you are certain that there will be an acceptable number of instances. For example evaluating
Country.allInstances
wouldbe
acceptable
whereasevaluating
anexpression
suchas
StockMovement.allInstances would most likely be unacceptable due to the fact that you cannot guarantee that overtime there will not be millions of StockMovement objects.

It is important to note that even OCL expressions which return a small number of results may still load a large number ofobjects into the local EcoSpace cache.

StockMovement.allInstances->select(id = 123456789)

1. [DB] Select {names of columns} from StockMovement
2. [ECO] Receive row data for millions of StockMovement objects
3. [ECO] Store the data in the local EcoSpace cache
4. [ECO] Filter the list where the id matches
5. [App] Receive a collection of object locators that match the criteria, in this case a single object out of over 100 million
objects
When such an expression is required it is advisable to use the IOclPsService (see page 25) instead.

Evaluating OCL expressions
This service has a number of overloaded methods for evaluating OCL expressions, each of which is named Evaluate().
The most simple overload accepts only an OCL expression to evaluate.

EcoSpace.Ocl.Evaluate("1 + 2");The next overload accepts an additional parameter identifying a context for the evaluation.

442

2.7 Query services

Name Typeroot

ECO Services

IOclService

PurposeIElement Identifies an element to be used as the context for the evaluation and also to determine the value to use
wherever the evaluator encounters the keyword self in the expression.
For in-memory evaluations the context may be any kind of IElement• Value types such as Int32, String, etc• Instances of modeled classes (as per the following example)• A collection of IElement such as a list of instances or a list of values

Person person1 = new Person(EcoSpace);
person1.DateOfBirth = DateTime.Today;
EcoSpace.Ocl.Evaluate(person1.AsIObject(), "age + 1")

The next overload accepts provides a way of identifying multiple values as parameters
Name

Type

Purposeroot

IElement

Identifies an element to be used as the context for the evaluation.

expression

StringThe OCL expression to evaluate.

variableList

IExternalVariableList A collection of variables to use when parsing the OCL expression.

2

Person person1 = new Person(EcoSpace);
person1.DateOfBirth = DateTime.Today;
IModifiableVariableList vars = EcoSpace.VariableFactory.CreateVariableList();vars.AddConstant("var_Years", 2);
EcoSpace.Ocl.Evaluate(person1.AsIObject(), "age + var_Years", vars);

Note: There is an additional overload which is the same as this except it does not have the initial root parameter.

Evaluating and subscribing
The IOclService has a number of overloaded methods named EvaluateAndSubscribe(). These methods are essentiallythe same as the Evaluate methods except that they additionally take two parameters of the type ISubscriber. The
subscription mechanism is covered in more detail in the subscriptions ( see page 144) section of this document so will not
be covered in great depth here. The only method that involves subscriptions which will be covered here is the
GetDerivedElement() method.

As ECO parses an OCL expression to evaluate it it accesses various element values held within the local cache, elements
such as object instances, attributes, and roles are known as domain elements because they are physical parts of themodeled domain (Person, Person.FirstName, Person.LastName, etc). When an OCL expression returns a domain element
you can be sure that whenever the value of the element changes your result will also change, for example:

Person person1 = new Person(EcoSpace);
person1.FirstName = "Fred";
string ocl = "firstName";
IElement resultElement = EcoSpace.Ocl.Evaluate(person1.AsIObject(), ocl);
45

2.7 Query services

ECO Services

IOclService

person1.FirstName = "Peter";
MessageBox.Show("resultElement value = " + resultElement.GetValue<string>());

At the point the evaluation is performed the person's first name is "Fred", the element returned by the evaluation is stored in
the local variable resultElement. The person's first name is then changed to "Peter" and a message box is then used toshow the value held by the element returned earlier.

Here you see that the message box shows the new value "Peter" and not the initial value "Fred". This is because the resultof the evaluation was a domain element, meaning that the expression identified a member of a class rather than merely acalculated value. To clarify this take a look at the following example

Person person1 = new Person(EcoSpace);
person1.FirstName = "Fred";
person1.LastName = "Morris";
string ocl = "firstName + ' ' + lastName";
IElement resultElement = EcoSpace.Ocl.Evaluate(person1.AsIObject(), ocl);
person1.FirstName = "Peter";
MessageBox.Show("resultElement value = " + resultElement.GetValue<string>());

Here you see that the message box now shows the result as it would have been at the time of evaluation rather thanreflecting any changes made since. This is because the result is evaluated as the values of two domain elements combined
together with a single space between them, the result itself is not a domain element but a new element constructed solely forthe purpose of holding the result. If you require the value of your element to change to reflect modifications after it wasevaluated you can use the GetDerivedElement() method.

Person person1 = new Person(EcoSpace);
person1.FirstName = "Fred";
person1.LastName = "Morris";
string ocl = "firstName + ' ' + lastName";
IElement resultElement = EcoSpace.Ocl.Evaluate(person1.AsIObject(), ocl);
IElement derivedElement = EcoSpace.Ocl.GetDerivedElement(person1.AsIObject(), ocl);
person1.FirstName = "Peter";
MessageBox.Show("resultElement value = " + resultElement.GetValue<string>());
MessageBox.Show("DerivedElement value = " + derivedElement.GetValue<string>());
462

2.7 Query services

ECO Services

IOclService

Here you can see that the derived element's value changes as the values it is derived from change. Subscriptions (page 144) are covered in another part of this document.

seeCombining evaluations
It is sometimes necessary to evaluate expressions in memory due to one or more of the following reasons:
1. You need to select on a derived (calculated) member or the result of a method, and the IOclPsService does not support
this functionality.

2

2. You need to include new and/or modified object instances in the local EcoSpace cache that also meet this criteria.
The following OCL example illustrates this point.

GetAge() is defined as a method with IsQuery=True. This is a useful trick for when you want to create a calculated member
in code but not all of the elements involved in calculating its result are capable of notifying subscribers when they change - inthis example there is no way to subscribe to DateTime.Today using an ECO ISubscriber so a method is created instead with
IsQuery set to True so that the IOclService knows it may execute the query without causing any side affects.

public int GetAge(){
DateTime today = DateTime.Today;
int result = today.Year - DateOfBirth.Year;if (today.DayOfYear < DateOfBirth.DayOfYear)
result--;return result;}

Now consider the following OCL expression:

Person.allInstances->select(firstName.sqlLikeCaseInsensitive('Pete%'))->select(GetAge()>= 18)

It is not possible to evaluate this expression using the IOclPsService because part of the expression refers to the method
GetAge(). It is also not recommended that this expression is evaluated using the IOclService as this would load all
instances of the Person class into memory in order to evaluate the rest of the expression, and this could be very detrimental
47

2.7 Query services

ECO Services

IOclService

to system performance if there are many object instances. The solution to this is to perform two expression evaluations, one
against each of the two services.

string psCriteria ="Person.allInstances" +"->select(firstName.sqlLikeCaseInsensitive('Pete%'))";
IObjectList people = EcoSpace.OclPs.Execute(psCriteria);
string memoryCriteria = "self->select(GetAge() >= 18)";
people = EcoSpace.Ocl.Evaluate(people, memoryCriteria);
IList<Person> result = people.GetAsIList<Person>();The above code example first executes part of the expression as SQL and loads into memory only a collection of object
locators,
one
for
each
objectinthedatastoragethatmatchedthecriteria
firstName.sqlLikeCaseInsensitive('Pete%'). The list of object locators is then used as the context for an
in-memory evaluation to filter the collection down to only people who are at least 18 years old at the time of evaluating theexpression. The problem with this example is that only objects identified as a match by the database will be considered forinclusion by the IOclService, this is because we are providing a specific list of object locators and then starting theexpression with the self keyword. If there are instances of the Person class in the local cache which have not yet beensaved then the data base has no way of knowing about this instance and including its object locator in the initial result;
likewise if a previously saved object has been modified and the changes not yet saved the final result will not include objects
which should be there.

One useful side affect of using the IOclPsService to retrieve a list of object locators is that these objects are then considered
to be "loaded" even though we only have an object locator (identity of the object) and not the actual data for those objects.
This is useful because there is an OCL operation available in the IOclService named AllLoadedObjects which can beused to evaluate expressions only against objects that have already had their identity loaded into the cache.

//Execute the query to load object locators into memory
string psCriteria ="Person.allInstances" +"->select(firstName.sqlLikeCaseInsensitive('Pete%'))";
EcoSpace.OclPs.Execute(psCriteria);//Now evaluate only on objects already loaded
string memoryCriteria ="Person.allLoadedObjects" +"->select(firstName.sqlLikeCaseInsensitive('Pete%'))" +"->select(GetAge() >= 18)";
IElement people = EcoSpace.Ocl.Evaluate(memoryCriteria);
IList<Person> result = people.GetAsIList<Person>();This is exactly the kind of approach you will see on the EcoDataSource component when creating ECO powered web
applications, where it is possible to specify both a PsExpression property for evaluating the query as SQL on the databaseand also an Expression property which is evaluated against the result of the PsExpression.

2.7.2.1 OCL operations supported by IOclService
The IOclService supports all of the IOclPsService operations (see page 28) plus the following additional operations.

48

2

2.7 Query services

ECO Services

IOclService

2.7.2.1.1 Source

DateTime

Parameters

TimeSpan value

Result

DateTime

Description
Returns the source DateTime with the specified TimeSpan parameter subtracted from it.

Exampleself.dateOfBirth - #00:01

SourceDateTime

Parameters

DateTime

ResultTimeSpan2

Description
Subtracts the DateTime parameter from the source and returns a TimeSpan.

ExampledateReturned - dateHired

SourceTimeSpanParameters

TimeSpan

ResultTimeSpanDescription
Subtracts the parameter from the source and returns a TimeSpan.

ExampleendTime - startTime49

2.7 Query services

ECO Services

IOclService

2.7.2.1.2 +SourceDateTime

Parameters

TimeSpan value

Result

DateTime

Description
Returns the source DateTime with the specified TimeSpan parameter added to it.

Exampleself.dateOfBirth + #08:30

SourceTimeSpanParameters

TimeSpan

ResultTimeSpan2

Description
Returns the source TimeSpan with the specified TimeSpan parameter added to it.

ExampleendTime > (startTime + #01:00)

2.7.2.1.3 AllInstancesAtTime
Source<Type>

Parameters

Int32 versionNumber

ResultCollection(<Instance>)

Description
Returns all instances of the source type for the given version number. See the IVersionService (details.

see page 141) for more

ExamplePerson.allInstancesAtTime(0)Returns a historical view of all Person instances that existed after the first ever call to UpdateDatabase.
50

2.7 Query services

ECO Services

IOclService

2.7.2.1.4 AllLoadedObjects
Source<Type>

Parameters
ResultCollection(<Instance>)

Description
Returns all instances of the source type that have already been loaded into the local cache. This list will also include objects
that have had their unique ID (object locator) loaded but not yet had their data contents loaded. No additional object locators
will be retrieved from the data storage, however, accessing a member value via code or OCL will ensure that the instance's
member data is in the cache.

ExamplePerson.allLoadedObjects

2
Returns all instances of Person that have already been loaded.

2.7.2.1.5 AllSubClasses
Source<Type>

Parameters
ResultCollection(String)

Description
Returns the names of all classes descended from the source type.

ExamplePerson.allSubClasses

2.7.2.1.6 AllSuperClasses
Source<Type>

Parameters
ResultCollection(String)

Description
Returns the names of all classes the source type descends from.

51

2.7 Query services

ECO Services

IOclService

ExamplePerson.allSuperClasses

2.7.2.1.7 AssociationEnds<Type>

SourceParameters

Collection(String)

ResultDescription
Returns the names of all properties of the class that are association ends to another modeled class. This list includes bothnavigable and non-navigable association ends, and also includes association ends inherited from all super classes.

Example2

Person.associationEnds

2.7.2.1.8 AsString<Any>

SourceParameters

StringResultDescription
Represents the source as a string. When the source is an instance of a class it will evaluate the Default String
Representation expression of the class.

Exampleself.dateOfBirth.asString

2.7.2.1.9 At
SourceCollection(<Any>)

Parameters

Int32 index

Result<Any>

Description
Returns the item within the collection at the specified index. The lower bound of the index is 1.

52

2.7 Query services

ECO Services

IOclService

ExamplePerson.allInstances->at(1)Notes
To specify a zero based index use ->at0

2.7.2.1.10 AtTime
Source<Object>

Parameters

Int32 versionNumber

Result<Object>

Description

2

Returns a historical view of the source object at the point in time identified by the version number. See the IVersionService( see page 141) for more details.

Exampleself.atTime(0)Returns a historical view of the source object that existed after the first ever call to UpdateDatabase.

2.7.2.1.11 Attributes
Source<Type>

Parameters
ResultCollection(String)

Description
Returns a collection of strings, each one being the name of a modeled property (UML attribute) on the specified class type.
The list will contain attributes modeled on the current class plus any inherited from its base class; the list will not include any
association ends.

ExampleIElement attributes;
attributes = EcoSpace.Ocl.Evaluate("Class_1.attributes");
foreach (string current in attributes.GetAsIList<string>())
MessageBox.Show(current);

2.7.2.1.12 Collection operations53

2.7 Query services

ECO Services

IOclService

2.7.2.1.12.1 Append
SourceCollection(<Any>)

Parameters<Any> value

ResultCollection(<Any>)

Description
Returns the source collection with the parameter added to it.

Examplevar_customer1.orders->append(var_customer2.orders)

Returns a collection of orders from customer1 with a single order from customer2 appended to the end of the list.

2

2.7.2.1.12.2 AsBag
SourceCollection(<Any>)

Parameters
ResultCollection(<Any>)

Description
Evaluates the source and returns a collection based upon it that is capable of holding duplicate entries. By default
associations are Sets, meaning that they do not allow duplicate values. This operation does not alter the source collection, it
merely changes the context.

Example//Products will only appear once
var_order1.lines.product->including(var_order2.lines.product)//Duplicate products may appear
var_order1.lines.product->asBag->including(var_order2.lines.product)
This operation is not very useful in ECO. It is implemented as a part of complying with the OCL specification. In ECO, allobject lists have an explicit order order.

2.7.2.1.12.3 AsCommaList
SourceCollection(String)Parameters
ResultStringDescription
Takes a collection of strings as a source and returns a single string containing each of the values in the collection separated
by commas.

54

2.7 Query services

ECO Services

IOclService

ExampleCity city = new City(EcoSpace);
Person person1 = new Person(EcoSpace);
person1.City = city;
person1.FirstName = "Peter";
Person person2 = new Person(EcoSpace);
person2.City = city;
person2.FirstName = "Fred";
string ocl = "self.people.firstName->asCommaList";
string result =EcoSpace.Ocl.Evaluate(city.AsIObject(), ocl).GetValue<string>();MessageBox.Show(result);

Returns the value
Peter, Fred

2.7.2.1.12.4 AsSeparatedList

2

SourceCollection(String)Parameters

String separator

ResultStringDescription
Takes a collection of strings as a source and returns a single string containing each of the values in the collection separated
by the specified separator.

ExampleCity city = new City(EcoSpace);
Person person1 = new Person(EcoSpace);
person1.City = city;
person1.FirstName = "Peter";
Person person2 = new Person(EcoSpace);
person2.City = city;
person2.FirstName = "Fred";
string ocl = "self.people.firstName->asSeparatedList('+')";
string result =EcoSpace.Ocl.Evaluate(city.AsIObject(), ocl).GetValue<string>();MessageBox.Show(result);

Returns the value
Peter+Fred55

2.7 Query services

ECO Services

IOclService

2.7.2.1.12.5 AsSequence
SourceCollection(<Any>)

Parameters
ResultCollection(<Any>)

Description
Evaluates the source and returns a copy of the that permits manual reordering. Reordering the resulting element does notaffect the source.

2.7.2.1.12.6 AsSet
SourceCollection(<Any>)

Parameters
ResultCollection(<Any>)

2

Description
Returns a collection based on the source with all duplicates removed.

Exampleself.orders.lines.product->asSet

Returns a distinct list of products sold to a specific customer.

ExamplePerson person1 = new Person(EcoSpace);
person1.FirstName = "Peter";
Person person2 = new Person(EcoSpace);
person2.FirstName = "Fred";
IObjectList originalPeople =EcoSpace.VariableFactory.CreateTypedObjectList(typeof(Person), true);originalPeople.Add(person1.AsIObject());
originalPeople.Add(person1.AsIObject());
originalPeople.Add(person2.AsIObject());
originalPeople.Add(person2.AsIObject());
string ocl = "self->asSet";
IElement result =EcoSpace.Ocl.Evaluate(originalPeople, ocl);
IList<Person> people = result.GetAsIList<Person>();foreach (Person person in people)
MessageBox.Show(person.FirstName);Will show only two message boxes.

562.7 Query services

ECO Services

IOclService

2.7.2.1.12.7 Collect
SourceCollection(<Any>)

Parameters

expression

ResultCollection(<Any>)

Description
A new collection is created, and for each element in the source the parameter is added.

Exampleprivate void TestCollect(){CreateOrder(1);CreateOrder(2);CreateOrder(3);IElement result;result = EcoSpace.Ocl.Evaluate("Order.allInstances->collect(o |o.orderLines->size)");foreach (int value in result.GetAsIList<int>())
MessageBox.Show(value.ToString());}

2

private void CreateOrder(int count){
Order order = new Order(EcoSpace);for (int i = 0; i < count; i++){
OrderLine line = new OrderLine(EcoSpace);line.Order = order;}}
If multiple arguments are specified in the collect-operation, the result will be a collection of tuples:
Person.allInstances->collect(firstName, lastName)Will return a collection of tuples of pairs of strings
Person.allInstances->collect(firstName, lastName, friends->select(friends->size > 5))
Will return a collection of tuples that contains the firstname, lastname and a list of friends with more than 5 friends.

2.7.2.1.12.8 Count
SourceCollection(<Any>)

Parameters<Any>

ResultInt32

Description
Returns the number of occurances of the parameter in within the source collection.
self.friends.friends->count(self)
calculates how many of a persons friends who have the person listed as a friend.

57

2.7 Query services

ECO Services

IOclService

2.7.2.1.12.9 Excluding
SourceCollection(<Any>)

Parameters<Any> value

ResultCollection(<Any>)

Description
Returns a copy of the source minus the value specified in the parameter.

ExamplePerson.allInstances->excluding(self)

2.7.2.1.12.10 FilterOnType
SourceCollection(<Object>)Parameters

Type requiredType

ResultCollection(<Object>)2

Description
Returns a copy of the source; excluding any elements that are not of the type specified and not descended from the typespecified.

Exampleself.actions->filterOnType(DatabaseAction)

Returns all associated actions that are either DatabaseAction, BackupDatabaseAction, or RestoreDatabaseAction.

2.7.2.1.12.11 First
SourceCollection(<Any>)

Parameters
Result<Any>58

2.7 Query services

ECO Services

IOclService

Description
Returns the first element of the source collection.

Exampleself.actions->first

2.7.2.1.12.12 IncludesAll
SourceCollection(<Any>)

Parameters

Collection(<Any>) value

ResultBooleanDescription
Returns true if every element in the parameter exists in the source.

2
Exampleself.friends->includesAll(var_Person2.friends)
All of var_Person2's friends are also the friends of the current person.

2.7.2.1.12.13 Including
SourceCollection(<Any>)

Parameters<Any> value

ResultCollection(<Any>)

Description
Returns a copy of the source plus the value specified in the parameter.

ExamplesomeContext.people->including(var_Person)

2.7.2.1.12.14 IndexOf
SourceCollection(<Any>)

Parameters<Any> value

ResultInt32

Description
Returns the index of the parameter within the source collection. The first result in the collection is 1, if the parameter is notwithin the collection zero will be returned.

592.7 Query services

ECO Services

IOclService

Exampleself.purchaseOrder.lines->indexOf(self)

If PurchaseOrder.Lines is an ordered association then this OCL expression is a reliable way of determining the line number
on the PurchaseOrderLine class.

Notes
To return a zero based index use ->indexOf0

2.7.2.1.12.15 Last
Collection(<Any>)

SourceParameters<Any>

Result2
Description
Returns the last element of the source collection.

Exampleself.actions->last

2.7.2.1.12.16 Prepend
SourceCollection(<Any>)

Parameters<Any> itemToAdd

ResultCollection(<Any>)

Description
Returns the source collection with the addition of the itemToAdd, which appears as the first element within the result.

2.7.2.1.12.17 SubSequence
SourceParameters

Collection(<Any>)• Int32 firstIndex• Int32 numberOfItems

ResultCollection(<Any>)

Description
Returns a subset of the source collection, starting at the first index specified and containing no more than the number of
items specified. The first index available is 1.
60

2.7 Query services

ECO Services

IOclService

2.7.2.1.12.18 SymmetricDifference
SourceCollection(<Any>)

Parameters

Collection(<Any>) comparison

ResultCollection(<Any>)

Description
Returns a collection of all items that appear in only the source or parameter, excluding any items that appear in both.

2.7.2.1.13 Compare
SourceInt32

Parameters

Int32 comparison

ResultInt32

2
Description
Compares the source to the parameter and returns either-1

The source is less than the specified value.

0

The source and parameter are equal.

1

The source is greater than the specified value.

Exampleage.Compare(18)Notes
Overloads exist for Decimal, DateTime, TimeSpan, and String.

SourceParameters

String• String comparison• Boolean ignoreCase

ResultInt32

Description
Performs the same task except allows you to perform a case-insensitive comparison.
61

2.7 Query services

SourceParameters

ECO Services

IOclService

String• Int32 firstCharacterPositionOfSource• String comparison• Int32 firstCharacterPositionOfComparison• Int32 lengthOfSubstringOfComparison

ResultInt32

Description
A substring of the source is used instead of the whole value using ClrSubstring (substring of the parameter.

see page 102). It is then compared to a2
SourceParameters

String• Int32 firstCharacterPositionOfSource• String comparison• Int32 firstCharacterPositionOfComparison• Int32 lengthOfSubstringOfComparison• Boolean ignoreCase

ResultInt32

Description
Identical to the previous overload except that the comparison may be made in a case insensitive manner.

2.7.2.1.14 Constraints
Source<Object>

Parameters
ResultCollection(Boolean)

Description
Evaluates each of the constraints on the specified business object. The return value itself isn't of much use, but it does
provide a way of quickly determining the validity of an object's state.

Exampleself.constraints->select(c | not c)->isEmpty

62

2.7 Query services

ECO Services

IOclService

Returns True if the object has no broken constraints.

2.7.2.1.15 Create
This operation creates a value of a specific type. This enables your OCL to be specific about the type of value you are
identifying in the expression.

Exampleself.employees->select(salary >= Decimal.Create(52000.12))

In the OclPsService the Create operation may be used with the following types.

Type

Parameters

DateTime ---Overload 01---2• Int64 numberOfTicks---Overload 02--• Int32 year
• Int32 month• Int32 day---Overload 03--• Int32 year
• Int32 month• Int32 day• Int32 hour
• Int32 minute• Int32 second---Overload 04--• Int32 year
• Int32 month• Int32 day• Int32 hour
• Int32 minute• Int32 millisecond

632.7 Query services

DecimalECO Services

IOclService---Overload 01--• Single value---Overload 02--• Double value---Overload 03--• Int32 value---Overload 04--• UInt32 value---Overload 05--• Int64 value---Overload 06--• UInt64 value---Overload 07--• Int32 lo

2• Int32 mid• Int32 hi• Boolean isNegative64

2.7 Query services

Guid

ECO Services

IOclService---Overload 01--• String value---Overload 02--• UInt32 a• UInt16 b• UInt16 c• Byte d• Byte e• Byte f• Byte g• Byte h• Byte i• Byte j• Byte k---Overload 03---2• Int32 a• Int16 b• Int16 c• Byte d• Byte e• Byte f• Byte g• Byte h• Byte i• Byte j• Byte k65

2.7 Query services

ECO Services

IOclService

TimeSpan ---Overload 01--• Int64 numberOfTicks---Overload 02--• Int32 hours• Int32 minutes• Int32 seconds---Overload 03--• Int32 days
• Int32 hours• Int32 minutes• Int32 seconds---Overload 04--• Int32 days
• Int32 hours• Int32 minutes

2• Int32 seconds• Int32 milliseconds
String• Char characterToRepeat• Int32 numberOfTimesToRepeat

2.7.2.1.16 Date and time operations

2.7.2.1.16.1 AddDays
SourceDateTime

Parameters

Double days

Result

DateTime

Description
Returns the source with the specified number of days added to it.

ExamplestartTime.AddDays(7)2.7.2.1.16.2 AddHours
SourceDateTime

Parameters

Double hours

Result

DateTime66

2.7 Query services

ECO Services

IOclService

Description
Returns the source with the specified number of hours added to it.

ExamplestartTime.AddHours(2.5)

2.7.2.1.16.3 AddMilliseconds
SourceDateTime

Parameters

Double milliseconds

Result

DateTime

Description
Returns the source with the specified number of milliseconds added to it.

2

ExamplestartTime.AddMilliseconds(100)2.7.2.1.16.4 AddMinutes
SourceDateTime

Parameters

Double minutes

Result

DateTime

Description
Returns the source with the specified number of minutes added to it.

ExamplestartTime.AddMinutes(45)2.7.2.1.16.5 AddMonths
SourceDateTime

Parameters

Int32 months

Result

DateTime

Description
Returns the source with the specified number of months added to it.

67

2.7 Query services

ECO Services

IOclService

ExamplestartTime.AddMonths(1)2.7.2.1.16.6 AddSeconds
SourceDateTime

Parameters

Double seconds

Result

DateTime

Description
Returns the source with the specified number of seconds added to it.

ExamplestartTime.AddSeconds(30)2
2.7.2.1.16.7 AddTicks
SourceDateTime

Parameters

Int64 ticks

Result

DateTime

Description
Returns the source with the specified number of ticks added to it. The Int64 parameter is the number of 100-nanosecond
ticks to add.

ExamplestartTime.AddTicks(1000)

2.7.2.1.16.8 AddYears
SourceDateTime

Parameters

Int32 years

Result

DateTime

Description
Returns the source with the specified number of years added to it.

ExampledateOfBirth.AddYears(100)68

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.9 Date
SourceDateTime

Parameters
Result

DateTime

Description
Returns only the date part of a specified DateTime value.

Exampleself.dateOfBirth.date

2

Notes
It is possible to obtain the current date using the following OCL
DateTime.Now.Date
or
DateTime.Today

2.7.2.1.16.10 Day
SourceDateTime

Parameters
ResultInt32

Description
Returns the day number of the date.

Example#2025-12-21.day

Returns 21.

2.7.2.1.16.11 Days
SourceTimeSpanParameters
ResultInt32

69

2.7 Query services

ECO Services

IOclService

Description
Returns the number of whole days in the time span.

ExampleTimeSpan.Create(99, 12, 00, 00).days

Returns 99.

2.7.2.1.16.12 DayOfYear
DateTime

SourceParameters

Int32

Result2

Description
Returns the day number of the year.

Example#2025-12-21.dayOfYear

Returns 355.

2.7.2.1.16.13 DaysInMonth
SourceParameters

DateTime• Int32 year
• Int32 month

ResultInt32

Description
Returns the number of days in the specified month.

ExampleDateTime.daysInMonth(2000, 2)

Returns 29.

70

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.14 Duration
TimeSpan

SourceParameters

TimeSpan

ResultDescription
Returns the duration of the source time span. The result will always be a positive.

ExampleTimeSpan.Create(-12, 00, 00).duration

Returns 12:00:00.

2

2.7.2.1.16.15 FormatDateTime
SourceDateTime

Parameters

String formatString

ResultStringDescription
Returns the source data formatted using the specified format string.

ExampleDateTime.now.formatDateTime('yyyy-MM-dd hh:mm:ss')

2.7.2.1.16.16 FromBinary
SourceDateTime

Parameters

Int64 serializedDateTimeValue

Result

DateTime

Description
Recreates a DateTime value from a serialized value.

ExampleDateTime.FromBinary(DateTime.Now.ToBinary)712.7 Query services

ECO Services

IOclService

2.7.2.1.16.17 FromDays
SourceTimeSpanParameters

Double numberOfDays

ResultTimeSpanDescription
Creates a new TimeSpan from the number of days specified.

ExampleTimeSpan.fromDays(99.5)

Returns 99:12:00:00.

2

2.7.2.1.16.18 FromFileTime
SourceDateTime

Parameters

Int64 fileDateTime

Result

DateTime

Description
Converts a file system date / time value to a DateTime.

ExampleDateTime.fromFileTime(self.fileDateTime)2.7.2.1.16.19 FromFileTimeUtc
SourceDateTime

Parameters

Int64 fileDateTime

Result

DateTime

Description
Converts the specified Windows file time to an equivalent UTC time.

ExampleDateTime.fromFileTimeUtc(self.fileDateTime)72

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.20 FromHours
SourceTimeSpanParameters

Double numberOfHours

ResultTimeSpanDescription
Creates a new TimeSpan from the number of hours specified.

ExampleTimeSpan.fromHours(36)Returns 1:12:00:00.

2

2.7.2.1.16.21 FromMilliseconds
SourceTimeSpanParameters

Double numberOfMilliseconds

ResultTimeSpanDescription
Creates a new TimeSpan from the number of milliseconds specified.

ExampleTimeSpan.fromMilliseconds(1000)

Returns 00:00:01.

2.7.2.1.16.22 FromMinutes
SourceTimeSpanParameters

Double numberOfMinutes

ResultTimeSpanDescription
Creates a new TimeSpan from the number of minutes specified.

ExampleTimeSpan.fromMinutes(1.5)
732.7 Query services

ECO Services

IOclService

Returns 00:01:30.

2.7.2.1.16.23 FromSeconds
SourceTimeSpanParameters

Double numberOfSeconds

ResultTimeSpanDescription
Creates a new TimeSpan from the number of seconds specified.

ExampleTimeSpan.fromSeconds(90)2Returns 00:01:30.

2.7.2.1.16.24 FromTicks
SourceTimeSpanParameters

Int64 ticks

ResultTimeSpanDescription
Creates a new TimeSpan from the number of ticks specified.

2.7.2.1.16.25 Hour
SourceDateTime

Parameters
ResultInt32

Description
Returns the hour part of the source.

2.7.2.1.16.26 Hours
SourceTimeSpanParameters
ResultInt32

74

2.7 Query services

ECO Services

IOclService

Description
Returns the hour part of the source.

2.7.2.1.16.27 InDateRange
SourceParameters

DateTime• DateTime firstDate• DateTime lastDate

ResultBooleanDescription
Returns True if the source DateTime is >= firstDate and <= lastDate.

Exampleself.appointments->select(a | a.dueDate.inDateRange(DateTime.today,
DateTime.today.addDays(1))

2

2.7.2.1.16.28 InTimeRange
SourceParameters

TimeSpan• TimeSpan firstTime• TimeSpan lastTime

ResultBooleanDescription
Returns True if the source TimeSpan is >= firstTime and <= lastTime.

Exampleself.todaysAppointments->select(a | a.startTime.inTimeRange(DateTime.now,
DateTime.now.addHours(1))

2.7.2.1.16.29 IsDaylightSavingTime
SourceDateTime

Parameters
ResultBooleanDescription
Returns a value indicating whether a specified date and time is within a daylight saving time period.

ExampleDateTime.now.isDaylightSavingTime75

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.30 IsLeapYear
Source<Static operation> DateTime

Parameters
ResultBooleanDescription
Returns a value indicating whether a specified year is a leap year.

ExampleDateTime.IsLeapYear(2008)

2.7.2.1.16.31 Millisecond
SourceDateTime

2

Parameters
ResultInt32

Description
Returns a number between 0 and 999 indicating the milliseconds part of the specified DateTime.

2.7.2.1.16.32 Milliseconds
SourceTimeSpanParameters
ResultInt32

Description
Returns a number between 0 and 999 indicating the milliseconds part of the specified TimeSpan.

2.7.2.1.16.33 MinuteSourceDateTime

Parameters
ResultInt32

Description
Returns a number between 0 and 59 indicating the minute part of the specified DateTime.

76

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.34 Minutes
TimeSpan

SourceParameters

Int32

ResultDescription
Returns a number between 0 and 59 indicating the minute part of the specified TimeSpan.

2.7.2.1.16.35 Month
DateTime

SourceParameters

Int32

Result2
Description
Returns a number between 1 and 12 indicating the month of the specified DateTime.

2.7.2.1.16.36 Negate
TimeSpan

SourceParameters

TimeSpan

ResultDescription
Returns the negated value of the source. A positive TimeSpan will be result in a negative, and a negative in a positive.

2.7.2.1.16.37 Now
Source<Static operation> DateTime

Parameters
Result

DateTime

Description
Returns the current date and time.

ExampleDateTime.now

772.7 Query services

ECO Services

IOclService

2.7.2.1.16.38 Second
DateTime

SourceParameters

Int32

ResultDescription
Returns a number between 0 and 59 indicating the second part of the specified DateTime.

2.7.2.1.16.39 Seconds
TimeSpan

SourceParameters

Int32

Result2
Description
Returns a number between 0 and 59 indicating the second part of the specified TimeSpan.

2.7.2.1.16.40 SumTime
SourceCollection(TimeSpan)

Parameters
ResultTimeSpanDescription
Produces a TimeSpan that is a sum of all entries in the collection.

2.7.2.1.16.41 Ticks
SourceDateTime

Parameters
ResultInt64

Description
Returns the total number of ticks that represent the source value.

Notes
An overload also exists for TimeSpan.

78

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.42 Time
SourceDateTime

Parameters
ResultTimeSpanDescription
Returns the time part of the source.

2.7.2.1.16.43 TimeOfDay
SourceDateTime

Parameters
ResultTimeSpan2
Description
Returns the time part of the source.

2.7.2.1.16.44 TimeStampToTime
SourceInt32

Parameters
Result

DateTime

Description
This object-versioning operation will take an object time-stamp number (integer) and return the date and time at which thetime stamp was created.

Exampleself.objectTimeStamp.timeStampToTime
This example retrieves the current time stamp for the object instance (the last time it was modified) and returns the date andtime of that time stamp.

2.7.2.1.16.45 TimeToTimeStamp
SourceDateTime

Parameters
ResultInt32

Description
This object-versioning operation will take a date and time and return the appropriate time-stamp number that corresponds.

Example792.7 Query services

ECO Services

IOclService

DateTime.now.addDays(-1).timeToTimeStamp

2.7.2.1.16.46 ToBinary
DateTime

SourceParameters

Int64

ResultDescription
Serializes the source to a 64 bit integer.

2.7.2.1.16.47 Today
Source<Static operation> DateTime

Parameters
Result

DateTime

Description

2

Returns only the date part of the current date.

2.7.2.1.16.48 ToFileTime
SourceDateTime

Parameters
ResultInt64

Description
Returns the source converted to a Windows file system date/time.

2.7.2.1.16.49 ToFileTimeUtc
SourceDateTime

Parameters
ResultInt64

Description
Returns the source converted to a Windows file system date/time.

2.7.2.1.16.50 ToLocalTime
SourceDateTime

Parameters
Result

DateTime

Description
Returns the source converted to a local date/time.

80

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.51 ToLongDateString
SourceDateTime

Parameters
ResultStringDescription
Uses the Windows regional settings to convert the source into a long date string.

2.7.2.1.16.52 ToLongTimeString
SourceDateTime

Parameters
ResultStringDescription
Uses the Windows regional settings to convert the source into a long time string.

2
2.7.2.1.16.53 ToShortDateString
SourceDateTime

Parameters
ResultStringDescription
Uses the Windows regional settings to convert the source into a short date string.

2.7.2.1.16.54 ToShortTimeString
SourceDateTime

Parameters
ResultStringDescription
Uses the Windows regional settings to convert the source into a short time string.

2.7.2.1.16.55 TotalDays
SourceTimeSpanParameters
ResultDoubleDescription
Returns the total number of days in the source, including the fractional part.

81

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.56 TotalHours
SourceTimeSpanParameters
ResultDoubleDescription
Returns the total number of hours in the source, including the fractional part.

2.7.2.1.16.57 TotalMilliseconds
SourceTimeSpanParameters
ResultDoubleDescription
Returns the total number of milliseconds in the source, including the fractional part.

2
2.7.2.1.16.58 TotalMinutes
SourceTimeSpanParameters
ResultDoubleDescription
Returns the total number of minutes in the source, including the fractional part.

2.7.2.1.16.59 TotalSeconds
SourceTimeSpanParameters
ResultDoubleDescription
Returns the total number of seconds in the source, including the fractional part.

2.7.2.1.16.60 ToUniversalTime
SourceDateTime

Parameters
Result

DateTime

Description
Returns the source converted to a coordinated universal time.

82

2.7 Query services

ECO Services

IOclService

2.7.2.1.16.61 UtcNow
Source<Static operation> DateTime

Parameters
Result

DateTime

Description
Returns the current date and time expressed as universal coordinated time.

ExampleDateTime.utcNow

2.7.2.1.16.62 Year
DateTime

Source2

Parameters
Int32

ResultDescription
Returns the whole number of years in the source.

2.7.2.1.17 EmptyList
Source<Type>

Parameters
ResultCollection(<Object>)Description
Returns a strongly typed collection of the specified type, the result will have no elements in it.

ExampleOCL for an association may look something like thisif (some condition) thenself.orders
else
Order.emptyList
endif

2.7.2.1.18 Existing
Source<Instance>

Parameters
ResultBoolean83

2.7 Query services

ECO Services

IOclService

Description
Returns true if the object still exists (and false if it has been deleted)

2.7.2.1.19 ExternalId<Instance>

SourceParameters

StringResultDescription
Returns the ExternalId of the source. See the External ID Service (see page 4).

2.7.2.1.20 If
Provides a way of providing conditional evaluation.

2
Exampleif (some condition)
self.orders
else
Order.emptyList
endif
Notes
Since every OCL expression needs to have a value, the "else-clause" of an if-statement is not optional as it is in mostprogramming languages. The OCL evaluator will only evaluate the result of either the "then-clause" or the "else-clause"
depending of the condition.

2.7.2.1.21 Let
The "Let" operation identifies the value of a variable in a statement.
The syntax for the let-statement is:
let <variablename> = <expression> in <expression>
The variable will be assigned the value of the first expression and can be referenced any number of times in the second
expression
For example
let nameToFind = 'Peter' in Person.allInstances->select(firstName = nameToFind)
is the equivalent of
Person.allInstances->select(firstName = 'Peter')This operator is useful for repeated use of a value.
let valueToUse = (some costly OCL evaluation) in
Company.allInstances->select((relevantMember > valueToUse) or (otherRelevantMember > valueToUse))
If the evaluation of the value is costly then using the "let" operation prevents you fromhaving to evaluate the value more than once.

842.7 Query services

ECO Services

IOclService

2.7.2.1.22 Mathematical operations
2.7.2.1.22.1 Abs
Source

Int16

Parameters
ResultInt16

Description
Returns the absolute value of a number.

Exampleself.debt.abs

2
Notes
Additional overloads exist for SByte, Int16, Int16, Single, Double, and Decimal.

2.7.2.1.22.2 Acos
SourceDoubleParameters
ResultDoubleDescription
Using the source as a Cosine value this operation will return its corresponding angle.

Exampleself.cosineValue.acos

2.7.2.1.22.3 Asin
SourceDoubleParameters
ResultDoubleDescription
Using the source as a Sine value this operation will return its corresponding angle.

85

2.7 Query services

ECO Services

IOclService

Exampleself.sineValue.asin

2.7.2.1.22.4 Atan
DoubleSourceParameters

DoubleResultDescription
Using the source as a tangent this operation will return its corresponding angle.

Exampleself.tangent.atan2
2.7.2.1.22.5 Atan2
SourceDoubleParameters

Double yCoOrdinate

ResultDoubleDescription
Uses the source as a relative X coordinate and the parameter as a relative Y coordinate. Using this coordinate the tangent iscalculated and its corresponding angle is returned.

Exampleself.xposition.atan2(yposition)

2.7.2.1.22.6 BigMul
SourceInt32

Parameters

Int32 factor

ResultInt64

Description
Multiplies the source by the factor and returns an Int64. This operation should be used when the source is an Int32 but theresult is expected to be too large to be represented by an Int32.

86

2.7 Query services

ECO Services

IOclService

2.7.2.1.22.7 Ceiling
DoubleSourceParameters

DoubleResultDescription
Rounds the source up to the closest whole number.

Notes
An overload also exists for Decimal.

2.7.2.1.22.8 Cos
DoubleSource2

Parameters
DoubleResultDescription
Using the source as an angle this operation will return its corresponding Cosine value.

Exampleself.angle.cos

2.7.2.1.22.9 Cosh
DoubleSourceParameters

DoubleResultDescription
Using the source as an angle this operation will return its corresponding hyperbolic Cosine value.

Exampleself.angle.cosh

2.7.2.1.22.10 Exp
SourceDoubleParameters

Double power87

2.7 Query services

ECO Services

ResultIOclService

DoubleDescription
Returns the source raised to the specified power.

Example2.Exp(8)Returns 256.

2.7.2.1.22.11 Floor
SourceDoubleParameters
Result2

DoubleDescription
Rounds the source down to the closest whole number.

Notes
An overload also exists for Decimal.

2.7.2.1.22.12 IsInfinity
SourceSingleParameters
ResultBooleanDescription
Returns a value indicating whether the specified number evaluates to negative or positive infinity.

Notes
An additional overload exists for Double.

2.7.2.1.22.13 IsNaN
SourceSingleParameters
ResultBoolean88

2.7 Query services

ECO Services

IOclService

Description
Returns a value indicating whether the specified number evaluates to not a number (NaN).

Notes
An additional overload exists for Double.

2.7.2.1.22.14 IsNegativeInfinity
SourceSingleParameters
ResultBooleanDescription

2

Returns a value indicating whether the specified number evaluates to negative infinity.

Notes
An additional overload exists for Double.

2.7.2.1.22.15 IsPositiveInfinity
SourceSingleParameters
ResultBooleanDescription
Returns a value indicating whether the specified number evaluates to positive infinity.

Notes
An additional overload exists for Double.

2.7.2.1.22.16 Log
SourceDoubleParameters
ResultDoubleDescription
Returns the natural (base e) logarithm of a specified number.

89

2.7 Query services

ECO Services

IOclService

SourceDoubleParameters

Double newBase

ResultDoubleDescription
Returns the natural (base e) logarithm of a specified number in a specified base.

2.7.2.1.22.17 Log10
DoubleSourceParameters

DoubleResult2
Description
Returns the base 10 logarithm of a specified number.

2.7.2.1.22.18 Max
SourceInt32

Parameters

Int32 comparison

ResultInt32

Description
Compares the source with the parameter and returns the greater of the two values.

Example1.max(2)Notes
Additional overloads exist for SByte, Byte, Int16, UInt16, UInt32, Int64, UInt64, Single, Double, and Decimal.

2.7.2.1.22.19 Min
SourceInt32

Parameters

Int32 comparison

ResultInt32

90

2.7 Query services

ECO Services

IOclService

Description
Compares the source with the parameter and returns the lesser of the two values.

Example2.min(1)Notes
Additional overloads exist for SByte, Byte, Int16, UInt16, UInt32, Int64, UInt64, Single, Double, and Decimal.

2.7.2.1.22.20 Negate
Decimal

SourceParameters

2

DecimalResultDescription
Returns the negated value of the source. A positive value will be result in a negative, and a negative in a positive.

2.7.2.1.22.21 Pow
SourceDoubleParameters

Double exponent

ResultDecimalDescription
Returns the source raised to the power of the specified exponent.

2.7.2.1.22.22 Remainder
SourceDecimalParameters

Decimal divisor

ResultDecimalDescription
Divides the source by the specified divisor and returns the remainder.

91

2.7 Query services

ECO Services

IOclService

2.7.2.1.22.23 Round
Decimal

SourceParameters

DecimalResultDescription
Returns the result of rounding the source to the nearest whole number.

SourceDecimalParameters

Int32 fractionalDigits

ResultDecimal2

Description
Returns the result of rounding the source to the specified number of fractional digits.

Notes
An overload also exists for Double.

2.7.2.1.22.24 Sin
SourceDoubleParameters
ResultDoubleDescription
Using the source as an angle this operation will return its corresponding Sine value.

Exampleself.angle.sin

2.7.2.1.22.25 Sinh
SourceDoubleParameters
ResultDouble92

2.7 Query services

ECO Services

IOclService

Description
Using the source as an angle this operation will return its corresponding hyperbolic Sine value.

Exampleself.angle.sinh

2.7.2.1.22.26 Sqrt
SourceDoubleParameters
ResultDoubleDescription
Returns the square root of the source.

2
2.7.2.1.22.27 Tan
SourceDoubleParameters
ResultDoubleDescription
Using the source as an angle this operation will return its corresponding Tangent value.

Exampleself.angle.tan

2.7.2.1.22.28 Tanh
SourceDoubleParameters
ResultDoubleDescription
Using the source as an angle this operation will return its corresponding hyperbolic Tangent value.

Exampleself.angle.tanh

93

2.7 Query services

ECO Services

IOclService

2.7.2.1.22.29 Truncate
Decimal

SourceParameters

DecimalResultDescription
Returns the whole number part of the source, any fractional part is discarded.

Notes
An overload exists for Double.

2.7.2.1.23 MaxLength2<String attribute>

SourceParameters

Int32

ResultDescription
Returns the maximum value length a string property may hold, as defined in the model.

ExamplePerson instance = new Person(EcoSpace);
int maxLength = ecoSpace.Ocl.Evaluate(instance.AsIObject(),"self.firstName.maxLength").GetValue<int>();

Notes
This OCL operation can be useful for ensuring maximum lengths are not exceeded in your UI. Simply add an additional
column to your ECO handle for each string attribute on your class with the expression "self.<member name>.maxLength" - itis then possible to databind the MaxLength of a TextBox for example to this value rather than hard-coding it and having to
manually update if the model changes.

2.7.2.1.24 ModifiedSinceTimeStamp
Source<Instance>

Parameters

Int32 timeStamp

ResultBoolean94

2.7 Query services

ECO Services

IOclService

Description
Returns True if the instance has been modified on or after the specified time stamp, otherwise False. This OCL operation
supports the object versioning mechanism.

2.7.2.1.25 NewGuid
Source<Static operation> Guid

Parameters
ResultGuid

Description
Returns a new Guid.

Example2'here is a new guid'+Guid.NewGuid.asString

2.7.2.1.26 ObjectFromExternalId
Source<Type>

Parameters

string externalId

Result<Instance>

Description
Returns the object with the given persistent ID. The ID consists of the class type and the primary key of the object, and may
be obtained using the ExternalId ( see page 84) operation. This operation is useful in ASP.NET applications where the IDof an object is passed as a query parameter in a URL.
ExamplePerson.objectFromExternalId(var_ExternalId)
This expression is very useful to use in an EcoDataSource on an ASP.Net page where the variable var_ExternalId can bebound to a parameter in the URL.

2.7.2.1.27 ObjectTimeStamp
Source<Instance>

Parameters
ResultInt32

Description
Returns the version time stamp of the source. This value will typically be Int32.MaxValue, except when you use a historicalobject version as the source.
95

2.7 Query services

ECO Services

IOclService

2.7.2.1.28 OclAsType
Source<Instance>

Parameters
Result<Instance>

Description
Casts the source to the specified sub-class.

ExampleBaseClass.allInstances->first.oclAsType(SubClass).attributeOnSubClass

Source2

Collection(<Instance>)

Parameters
ResultCollection(<Instance>)

Description
Casts the source collection to the specified sub-class.

ExampleBaseClass.allInstances.oclAsType(SubClass).attributeOnSubClass

Notes
An InvalidCastException will be thrown if the source cannot be cast to the specified type.

2.7.2.1.29 OclIsKindOf
Source<Instance>

Parameters<Type>

ResultBooleanDescription
Returns True if the source may be type cast to the type specified as a parameter.

962.7 Query services

ECO Services

IOclService

Example//True. SubClass may be cast to a BaseClass.
bool subClassIsKindOfBaseClass = EcoSpace.Ocl.Evaluate(
subClass.AsIObject(),"self.oclIsKindOf(BaseClass)").GetValue<bool>();//False.

BaseClass may not be cast to a SubClassbool baseClassIsKindOfSubClass = EcoSpace.Ocl.Evaluate(
baseClass.AsIObject(),"self.oclIsKindOf(SubClass)").GetValue<bool>();2.7.2.1.30 OclIsTypeOf
Source<Instance>

Parameters<Type>

ResultBoolean2

Description
Returns True if the source is an instance of the type specified by the parameter.

Example//True. subClass is an instance of the SubClass type.
bool subClassIsTypeOfSubClass = EcoSpace.Ocl.Evaluate(
subClass.AsIObject(),"self.oclIsTypeOf(SubClass)").GetValue<bool>();//False. subClass is not an instance of the BaseClass type, despite being descended
from BaseClass.
bool subClassIsTypeOfBaseClass = EcoSpace.Ocl.Evaluate(
subClass.AsIObject(),"self.oclIsTypeOf(BaseClass)").GetValue<bool>();2.7.2.1.31 Parse
Source<Static operation> <Type>

Parameters

String value

Result<Instance of Type>

Description
The Parse operation executes the static Parse(String) method on the specified type. This operation is available on all
standard .NET types that implement the method.

ExampleDateTime.Parse('2001-01-31')
DateTime.Parse('2001-01-31T12:30:59')
Byte.Parse('255')97

2.7 Query services

ECO Services

IOclService

2.7.2.1.32 SafeCast
Source<Instance>

Parameters<Type>

Result<Instance>

Description
Casts the source to the specified type. If the source is not compatible with the type the operation will silently fail and return
null/nil.

Examplevar baseClass = new BaseClass(EcoSpace);var subClass =EcoSpace.Ocl.Evaluate(
baseClass.AsIObject(),"self.safeCast(SubClass)").GetValue<SubClass>();2

Results in subClass being null.

SourceCollection(<Instance>)

Parameters<Type>

ResultCollection(<Instance>)

Description
Casts each instance in the source collection to the specified type. If the source is not compatible with the type the operation
will silently fail and add null/nil for the individual instance before continuing with the next instance in the source.

Examplevar baseClass = new BaseClass(EcoSpace);var subClass = new SubClass(EcoSpace);var subClassListCount =EcoSpace.Ocl.Evaluate(
baseClass.AsIObject(),"BaseClass.allInstances.safeCast(SubClass)").GetAsCollection().Count;
Results in subClassListCount holding the value 2, the resulting collection holds a valid object and a nil reference.

2.7.2.1.33 State machine operations
2.7.2.1.33.1 OclGetStates
Source<Instance>

Parameters
ResultCollection(String)982.7 Query services

ECO Services

IOclService

Description
Given a source instance this operation will return a string for each state the object is in.

Example2

Result• Authoring

Result• Reviewing.CheckingGrammar
99

2.7 Query services

ECO Services

IOclService

If the state machine contains parallell states, the result from this operation can contain more than one state.

2.7.2.1.33.2 OclGetTriggers<Instance>

SourceParameters

Collection(String)

ResultDescription
Given a source instance this operation will return a string for each available trigger depending on its current state. Guard
expressions are not evaluated in order to include/exclude triggers from the result.

Example2

Result• GrammarChecked• Reject

2.7.2.1.33.3 OclIsInState
Source<Instance>

Parameters

EnumLiteral StateName

ResultBooleanDescription
Given a source instance and the name of a state this operation will return True or False depending on whether or not the
instance's state machine is in that state.
1002.7 Query services

ECO Services

IOclService

Example2//Truebool isReviewing =EcoSpace.Ocl.Evaluate(
article.AsIObject(),"self.oclIsInState(#Reviewing)").GetValue<bool>();//Truebool isCheckingGrammar =EcoSpace.Ocl.Evaluate(
article.AsIObject(),"self.oclIsInState(#CheckingGrammar)").GetValue<bool>();//Falsebool isPublished =EcoSpace.Ocl.Evaluate(
article.AsIObject(),"self.oclIsInState(#Published)").GetValue<bool>();2.7.2.1.34 String operations
2.7.2.1.34.1 Chars
SourceStringParameters

Int32 index

ResultChar

Description
Returns the character at the specified index of the source. The index is zero based.

101

2.7 Query services

ECO Services

IOclService

Example'Hello'.chars(0)2.7.2.1.34.2 ClrSubstring
SourceStringParameters

Int32 firstIndex

ResultStringDescription
Uses the CLR String.Substring routine to return a substring of the source.

Example'Hello'.substring(2)2Returns "llo".

SourceStringParameters

Int32 firstIndex
Int32 length

ResultStringDescription
Uses the CLR String.Substring routine to return a substring of the source.

Example'Hello'.substring(2, 2)

Returns "ll".

2.7.2.1.34.3 Concat
SourceStringParameters

String value

ResultString102

2.7 Query services

ECO Services

IOclService

Description
Returns the result of appending the parameter to the source.

Example'Hello'.concat(' World')

Notes
Additional overloads exist to enable you to specify between one and three values to append to the end of the source.

2.7.2.1.34.4 Contains
SourceStringParameters

String value

ResultBoolean2

Description
Returns True if the text specified exists within the source.

Example'Hello'.contains('llo')

2.7.2.1.34.5 EndsWith
SourceStringParameters

String value

ResultBooleanDescription
Returns True if the source ends with the string specified.

Example'Hello'.endsWith('lo')

Returns True.

1032.7 Query services

ECO Services

IOclService

2.7.2.1.34.6 Format
SourceParameters

String• String source• Object value

ResultStringDescription
Uses the .NET String.Format method on the source to produce a new string. The "value" parameter may be repeatedmultiple times.

ExampleString.format('The time now is {0:g}', DateTime.Now)2

2.7.2.1.34.7 GetNumericValue
Char

SourceParameters

DoubleResultDescription
Converts the specified numeric Unicode character to a double-precision floating point number.

2.7.2.1.34.8 IndexOf

SourceStringParameters

Char character

ResultInt32

Description
Uses the CLR String.Substring routine. Returns the index of the first occurrence of the specified Unicode character in thesource string.

Example'Hello'.indexOf('e')

SourceStringParameters

String subString1042.7 Query services

ECO Services

IOclService

Int32

ResultDescription

Uses the CLR String.Substring routine. Returns the index of the first occurrence of the specified sub string in the source
string.

Example'Hello'.indexOf('llo')

SourceParameters

String• Char character• Int32 startPosition

ResultInt32

2

Description
Uses the CLR String.Substring routine. Returns the index of the first occurrence of the specified Unicode character in thesource string. The search starts at the specified zero-based character position.

Example'Hello hello'.indexOf('l', 4)SourceParameters

String• String subString• Int32 startPosition

ResultInt32

Description
Uses the CLR String.Substring routine. Returns the index of the first occurrence of the specified sub string in the source
string. The search starts at the specified zero-based character position.

Example'Hello hello'.indexOf('ll', 4)105

2.7 Query services

SourceParameters

ECO Services

IOclService

String• Char character• Int32 startPosition• Int32 subStringLength

ResultInt32

Description
Uses the CLR String.Substring routine. Returns the index of the first occurrence of the specified Unicode character in thesource string. The search starts at the specified zero-based character position and only inspects the specified number ofcharacters.

Example'Hello hello'.indexOf('l', 5, 3)

2
SourceParameters

String• String subString• Int32 startPosition• Int32 subStringLength

ResultInt32

Description
Uses the CLR String.Substring routine. Returns the index of the first occurrence of the specified sub string in the source
string. The search starts at the specified zero-based character position and only inspects the specified number of characters.

Example'Hello hello'.indexOf('ll', 5, 5)

2.7.2.1.34.9 Insert
SourceParameters

String• Int32 startPosition• String stringToInsert

ResultStringDescription
Returns a new string by inserting the parameter string into the source at the specified zero-based index.

Example'Helo'.insert(2, 'l')106

2.7 Query services

ECO Services

IOclService

2.7.2.1.34.10 IsControl
Char

SourceParameters

BooleanResultDescription
Indicates whether a specified Unicode character is categorized as a control character.

Exampleself.firstName.chars(0).isControl

SourceParameters<Static operation> Char2

• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a control character.

ExampleChar.isControl('Some string with a control character', 2)

2.7.2.1.34.11 IsDigit
SourceChar

Parameters
ResultBooleanDescription
Indicates whether a Unicode character is categorized as a decimal digit.

Exampleself.dateOfBirth.asString.chars(2).isDigit1072.7 Query services

SourceParameters

ECO Services

IOclService<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a decimal digit.

ExampleChar.isDigit('ABC9', 3)

2.7.2.1.34.12 IsHighSurrogate
Char

SourceParameters

BooleanResult2

Description
Indicates whether the specified Char object is a high surrogate.

Exampleself.firstName.chars(0).isHighSurrogate

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the Char at the specified position in a string is a high surrogate.

ExampleChar.isHighSurrogate('Hello', 0)

2.7.2.1.34.13 IsLetter
SourceChar

Parameters
ResultBoolean108

2.7 Query services

ECO Services

IOclService

Description
Indicates whether a Unicode character is categorized as an alphabetic letter.

Exampleself.firstName.chars(0).isLowSurrogate

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription

2

Indicates whether the character at the specified position in a specified string is categorized as an alphabetic character.

ExampleChar.isLetter('0123A', 4)

2.7.2.1.34.14 IsLetterOrDigit
Char

SourceParameters

BooleanResultDescription
Indicates whether a Unicode character is categorized as an alphabetic letter or a decimal digit.

Exampleself.firstName.chars(0).isLetterOrDigit

SourceParameters<Static operation> Char• String value• Int32 index

ResultBoolean109

2.7 Query services

ECO Services

IOclService

Description
Indicates whether the character at the specified position in a specified string is categorized as an alphabetic character or adecimal digit.

ExampleChar.isLetterOrDigit('-AB12-', 0)

2.7.2.1.34.15 IsLower
Char

SourceParameters

BooleanResultDescription
Indicates whether the specified Unicode character is categorized as a lowercase letter.

2

Exampleself.firstName.chars(0).isLower

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a lowercase letter.

ExampleChar.isLower('Lower', 1)

2.7.2.1.34.16 IsLowSurrogate
SourceChar

Parameters
ResultBooleanDescription
Indicates whether the specified Char is a low surrogate.

1102.7 Query services

ECO Services

IOclService

Exampleself.firstName.chars(0).isLowSurrogate

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the Char at the specified position in a string is a low surrogate.

Example2

Char.isLowSurrogate('Hello', 0)

2.7.2.1.34.17 IsNormalized
SourceStringParameters
ResultBooleanDescription
Indicates whether this string is in a particular Unicode normalization form.

2.7.2.1.34.18 IsNullOrEmpty
SourceStringParameters
ResultBooleanDescription
Indicates whether the specified String object is a null reference or an empty string.

2.7.2.1.34.19 IsNumber
SourceChar

Parameters
ResultBoolean111

2.7 Query services

ECO Services

IOclService

Description
Indicates whether a Unicode character is categorized as a number.

Exampleself.firstName.chars(0).isNumber

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription

2

Indicates whether the character at the specified position in a specified string is categorized as a number.

ExampleChar.isNumber('123', 0)

2.7.2.1.34.20 IsPunctuation
Char

SourceParameters

BooleanResultDescription
Indicates whether a Unicode character is categorized as a punctuation mark.

Exampleself.firstName.chars(0).isPunctuation

SourceParameters<Static operation> Char• String value• Int32 index

ResultBoolean112

2.7 Query services

ECO Services

IOclService

Description
Indicates whether the character at the specified position in a specified string is categorized as a punctuation mark.

ExampleChar.isPunctuation('Hello world!', 11)2.7.2.1.34.21 IsSeparator
Char

SourceParameters

BooleanResultDescription
Indicates whether a Unicode character is categorized as a separator character.

2
Exampleself.firstName.chars(0).isSeparator

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a separator character.

ExampleChar.isSeparator('\t', 0)

2.7.2.1.34.22 IsSurrogate
SourceChar

Parameters
ResultBooleanDescription
Indicates whether the specified Char is a low surrogate.

1132.7 Query services

ECO Services

IOclService

Exampleself.firstName.chars(0).isSurrogate

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the Char at the specified position in a string is a low surrogate.

ExampleChar.isSurrogate('Hello', 0)2
2.7.2.1.34.23 IsSurrogatePair
Char

SourceParameters

BooleanResultDescription
Indicates whether the two specified Chars form a surrogate pair. .

Exampleself.firstName.chars(0).isSurrogatePair(self.firstName.chars(1))

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether two adjacent Chars at the specified position form a surrogate pair.

ExampleChar.isSurrogatePair(self.firstName, 0)114

2.7 Query services

ECO Services

IOclService

2.7.2.1.34.24 IsSymbol
Char

SourceParameters

BooleanResultDescription
Indicates whether a Unicode character is categorized as a symbol.

Exampleself.firstName.chars(0).isSymbol

SourceParameters<Static operation> Char2

• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a symbol.

ExampleChar.isSymbol('1 + 1 = 2', 2)

2.7.2.1.34.25 IsUpper
SourceChar

Parameters
ResultBooleanDescription
Indicates whether the specified Unicode character is categorized as a uppercase letter.

Exampleself.firstName.chars(0).isUpper

115

2.7 Query services

SourceECO Services

IOclService<Static operation> Char

Parameters• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a uppercase letter.

ExampleChar.isUpper('Upper', 0)

2.7.2.1.34.26 IsWhiteSpaceChar

Source2

Parameters
BooleanResultDescription
Indicates whether the specified Unicode character is categorized as a white space letter.

Exampleself.firstName.chars(0).isWhiteSpace

SourceParameters<Static operation> Char• String value• Int32 index

ResultBooleanDescription
Indicates whether the character at the specified position in a specified string is categorized as a white space letter.

ExampleChar.isWhiteSpace('Hello world', 5)116

2.7 Query services

ECO Services

IOclService

2.7.2.1.34.27 LastIndexOf
SourceStringParameters

String value

ResultBooleanDescription
Reports the index position of the last occurrence of a specified string within the source string.

Exampleself.firstName.lastIndexOf('.')

StringSourceParameters

2• String value• Int32 position
BooleanResultDescription
Reports the index position of the last occurrence of a specified string within the source string. This overload searches a
sub-string of the source, the substring will be the first character (position 0) up to and including the position specified.

Exampleself.firstName.lastIndexOf('.', 5)SourceParameters

String• String value• Int32 startIndex• Int32 length

ResultBooleanDescription
Reports the index position of the last occurrence of a specified string within the source string. This overload searches a
sub-string of the source, the substring will be the character specified by the startIndex with the specified length.

117

2.7 Query services

ECO Services

IOclService

Exampleself.firstName.lastIndexOf('.', 3, 10)

2.7.2.1.34.28 Normalize
StringSourceParameters

StringResultDescription
Returns a new string whose binary representation is in a particular Unicode normalization form.

2.7.2.1.34.29 Pad
SourceParameters

String• Int32 newLength• String paddingString

Result2

StringDescription
Returns a new string based on the source with a guaranteed minimum length. If the source is shorter than the specified newlength it will be left-padded with the specified text, if it is already equal to or longer than the specified new length the result
will be the same as the source.

Example//returns 21212Hello'Hello'.pad(10, '12')2.7.2.1.34.30 PadLeft
SourceStringParameters

Int32 newLength

ResultStringDescription
Returns a new string based on the source with a guaranteed minimum length. If the source is shorter than the specified newlength the left of the string be padded with spaces, if it is already equal to or longer than the specified new length the result
will be the same as the source.

Example'Hello'.padLeft(6)Returns " Hello"'Hello'.padLeft(3)Returns "Hello"1182.7 Query services

ECO Services

IOclService

SourceStringParameters

Int32 newLength
Char paddingCharacter

ResultStringDescription
Returns a new string based on the source with a guaranteed minimum length. If the source is shorter than the specified newlength the left of the string be padded with the specified character, if it is already equal to or longer than the specified newlength the result will be the same as the source.

Example'Hello'.padLeft(10, 'a'.chars(0))
Returns "aaaaaHello"

2.7.2.1.34.31 PadRight2

SourceStringParameters

Int32 newLength

ResultStringDescription
Returns a new string based on the source with a guaranteed minimum length. If the source is shorter than the specified newlength the right of the string be padded with spaces, if it is already equal to or longer than the specified new length the result
will be the same as the source.

Example'Hello'.padRight(6)Returns "Hello "'Hello'.padRight(3)Returns "Hello"

SourceStringParameters

Int32 newLength
Char paddingCharacter

ResultStringDescription
Returns a new string based on the source with a guaranteed minimum length. If the source is shorter than the specified newlength the right of the string be padded with the specified character, if it is already equal to or longer than the specified newlength the result will be the same as the source.

1192.7 Query services

ECO Services

IOclService

Example'Hello'.padRight(10, 'a'.chars(0))

Returns "Helloaaaaa"

2.7.2.1.34.32 PostPad
SourceStringParameters

Int32 newLength
String paddingString

ResultStringDescription
Returns a new string based on the source with a guaranteed minimum length. If the source is shorter than the specified newlength the right of the string be padded with the specified padding string, truncating the result if necessary. If the source isalready equal to or longer than the specified new length the result will be the same as the source.

2

2.7.2.1.34.33 RegExpMatch
SourceStringParameters

String pattern

ResultBooleanDescription
Evaluates the specified pattern against the source and returns whether or not the source matches the pattern.
For information of the syntax for regular expression, please refer to the .net framework documentation.

2.7.2.1.34.34 Remove
SourceStringParameters

Int32 firstCharToRemove

ResultStringDescription
Removes the end of the source string from the firstCharToRemove onwards and returns the result.

Example'Hello World!'.Remove(5)Returns "Hello"

SourceStringParameters

Int32 firstCharToRemove
Int32 numberOfCharsToRemove120

2.7 Query services

ResultECO Services

IOclService

StringDescription
Removes the specified number of characters from the source string starting from firstCharToRemove.

Example'Hello World!'.Remove(5, 6)
Returns "Hello!"

2.7.2.1.34.35 Replace
SourceStringParameters

String textToFind
String textToReplace

ResultString2

Description
Returns a string based on the source but with all occurences of textToFind replaced with textToReplace.

Example'Hello World!'.Replace('World', 'mother')
Returns "Hello mother!"

2.7.2.1.34.36 StartsWith
SourceStringParameters

String value

ResultBooleanDescription
Returns True if the text specified starts with the value specified.

Example'Hello'.startsWith('He')

2.7.2.1.34.37 StrToDate
SourceStringParameters
Result

DateTime1212.7 Query services

ECO Services

IOclService

Description
Uses DateTime.Parse to convert the source to a DateTime.

2.7.2.1.34.38 StrToDateTime
StringSourceParameters

DateTime

ResultDescription
Uses DateTime.Parse to convert the source to a DateTime.

2.7.2.1.34.39 StrToInt
StringSourceParameters

2
Int32

ResultDescription
Uses Int32.Parse to convert the source to an Int32.

2.7.2.1.34.40 StrToTime
StringSourceParameters

TimeSpan

ResultDescription
Uses TimeSpan.Parse to convert the source to a TimeSpan.

2.7.2.1.34.41 SubString
SourceStringParameters

Int32 startIndex
Int32 length

ResultStringDescription
Returns a sub string of the source.

1222.7 Query services

ECO Services

IOclService

2.7.2.1.34.42 ToLower (Char)
Char

SourceParameters

Char

ResultDescription
Converts a char value to lower case.

Exampleself.productCode.chars(1).toLower

2.7.2.1.34.43 ToUpper (Char)
Char

SourceParameters

Char

Result2

Description
Converts a char value to upper case.

Exampleself.productCode.chars(1).toUpper

2.7.2.1.34.44 ToLowerInvariant
SourceStringParameters
ResultStringDescription
Converts a string to lower case using casing rules of the invariant culture.

Exampleself.productCode.toLowerInvariant

Notes
An overload exists for Char.

2.7.2.1.34.45 ToUpperInvariant
SourceStringParameters
ResultString123

2.7 Query services

ECO Services

IOclService

Description
Converts a string to upper case using casing rules of the invariant culture.

Exampleself.productCode.toUpperInvariant

Notes
An overload exists for Char.

2.7.2.1.34.46 Trim
StringSourceParameters

StringResultDescription

2

Returns the source with all leading and trailing white space characters removed.

Exampleself.productCode.trim

2.7.2.1.35 SuperTypes
Type

SourceParameters

Collection(String)

ResultDescription
Returns a list of class names, one for each supertype of the source.

2.7.2.1.36 TimeSpan operations
2.7.2.1.37 TaggedValue
Source<Instance>

Parameters

String taggedValueName

ResultStringDescription
Finds a tagged value with the specified name defined against the instance's class (or one of its super classes) and returns
the value defined in the model.
1242.7 Query services

ECO Services

IOclService

2.7.2.1.38 ToByte
SourceDecimalParameters
Result

Byte

Description
If the source is within the range Byte.MinValue .. Byte.MaxValue the result will be a byte, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.39 ToDouble
SourceDecimalParameters
ResultDouble2

Description
If the source is within the range Double.MinValue .. Double.MaxValue the result will be a Double, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.40 ToInt16
SourceDecimalParameters
ResultInt16

Description
If the source is within the range Int16.MinValue .. Int16.MaxValue the result will be an Int16, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.41 ToInt32
SourceDecimalParameters
ResultInt32

Description
If the source is within the range Int32.MinValue .. Int32.MaxValue the result will be an Int32, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

1252.7 Query services

ECO Services

IOclService

2.7.2.1.42 ToInt64
SourceDecimalParameters
ResultInt64

Description
If the source is within the range Int64.MinValue .. Int64.MaxValue the result will be an Int64, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.43 ToSByte
SourceDecimalParameters
ResultSByte

2

Description
If the source is within the range SByte.MinValue .. SByte.MaxValue the result will be a signed byte, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.44 ToSingle
SourceDecimalParameters
ResultSingleDescription
If the source is within the range Single.MinValue .. Single.MaxValue the result will be a Single, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.45 ToUInt16
SourceDecimalParameters
ResultUInt16

Description
If the source is within the range UInt16.MinValue .. UInt16.MaxValue the result will be an UInt16, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

126

2.7 Query services

ECO Services

IOclService

2.7.2.1.46 ToUInt32
Decimal

SourceParameters

UInt32

ResultDescription

If the source is within the range UInt32.MinValue .. UInt32.MaxValue the result will be an UInt32, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.47 ToUInt64
Decimal

SourceParameters

UInt64

Result2

Description
If the source is within the range UInt64.MinValue .. UInt64.MaxValue the result will be an UInt64, otherwise a
System.TargetInvovationException will be thrown (with an inner exception type of System.OverflowException). Any fractional
value will be discarded.

2.7.2.1.48 TypeName<Type>

SourceParameters

StringResultDescription
Returns the class name of the source type.

2.7.2.1.49 Xor
SourceBooleanParameters

Boolean comparison

ResultBooleanDescription
Returns the result of performing a logical exclusive OR on the source and parameter

Examplefalse xor false = false
1272.7 Query services

ECO Services

IActionLanguageService

false xor true = true
true xor false = true
true xor true = false

2.7.3 IActionLanguageService
The Action Language Service is an OCL based expression executor. As well as the OclPs and Ocl commands, which haveno side effects, this service is additionally capable of executing statements which alter object state. The Action Language
Service is particularly useful when designing state machines. For example when an Article enters a Published state it makes
sense to set the article's PublishedDate to the current date and time.

This would be achieved by setting an Entry Action on the Published state with a simple action language expression

2
Assignments in the action language service are made using the token :=2.7.3.1 Operations support by IActionLanguageService
The IActionLanguageService supports all of the IOclPsService operations (( see page 48) operations, and the following additional operations.

see page 28), all of the IOclServiceOperations

2.7.3.1.1 Clear
SourceCollection(<Any>)

Parameters
ResultDescription
Removes all elements from the source collection.

Notes
The source must be a member of a modeled class.
//Valid
self.orderLines->clear//Invalid
self.orderLines->select(value < 100)->clear

1282.7 Query services

ECO Services

IActionLanguageService

2.7.3.1.2 Add
SourceCollection(<Any>)

Parameters<Any> instanceToAdd

ResultCollection(<Any>)

Description
Adds the parameter to the source.

Notes
The source must be a member of a modeled class.
//Valid
self.orderLines->add(OrderLine.Create)//Invalid
self.orderLines->select(value < 100)->add(OrderLine.Create)2

2.7.3.1.3 Create<Type>

SourceParameters<Instance>

ResultDescription
Creates an instance of the specified modeled class.

ExamplePerson.Create

2.7.3.1.4 Delete<Type>

SourceParameters

BooleanResultDescription
Deletes the specified object instance.

2.7.3.1.5 Remove
SourceCollection(<Any>)

Parameters<Any> instanceToRemove

ResultCollection(<Any>)

Description1292.7 Query services

ECO Services

ITypeService

Removes the specified parameter from the source collection.

Notes
The source must be a member of a modeled class.
//Valid
self.orderLines->remove(self.orderLines->last)//Invalid
self.orderLines->select(value < 100)->remove(self.orderLines->last)

2.7.3.1.6 RemoveAt
SourceCollection(<Any>)

Parameters<Any> indexToRemove

ResultCollection(<Any>)

Description
Removes the item at the specified index from the source collection.

2

Notes
The source must be a member of a modeled class.
//Valid
self.orderLines->removeAt(1)//Invalid
self.orderLines->select(value < 100)->removeAt(1)2.7.4 ITypeService
The type service allows you to perform various functions on the OCL evaluator, such as determining the result type of an
expression or registering new OCL operations. The members of the ITypeService are implemented directly on each of thethree OCL services. So instead of obtaining an ITypeService from the EcoSpace you would use the equivalent method on
the OCL service.

Determining if the result of an expression is read-only
When evaluating an OCL expression the result of that evaluation is either mutable or read-only. A result is mutable if theexpression results in a domain member (a member of a class) and that member is not read-only.
//Mutable, this is a domain member (modeled)self.firstName//Read-only, because this is an expression derived from a domain member
self.firstName.toUpper//Read-only, because the value is assigned by the DB//self.uniqueID
To determine whether or not an expression is readonly use the ExpressionIsReadOnly method on the appropriate OCL
service.

1302.7 Query services

ECO Services

ITypeService

IClassifier classifier = EcoSpace.TypeSystem.GetClassByType(typeof(Person));bool readonly = EcoSpace.Ocl.ExpressionIsReadOnly("person", classifier,false).GetValue<bool>();Determining model information from an expression
If the expression results in a domain element (Person.FirstName for example) it is possible to get information about themember as follows
IClassifier classifier = EcoSpace.TypeSystem.GetClassByType(typeof(Person));
IStructuralFeature modelInformation = EcoSpace.Ocl.ExpressionModelInfo("self.firstName",classifier, false);
If the expression does not result in a domain element null will be returned. IStructuralFeature is described in the API help.

2.7.4.1 InstalledOperations
Each OCL service implements a different set of OCL operations. It is possible to discover a list of installed operations for agiven OCL service using the InstalledOclOperations property. This property will contain an entry for every installed operation,in addition it will contain operations with duplicate names when an operation has many overloads.

2

foreach (IOclOperation currentOperation in EcoSpace.OclPs.InstalledOclOperations)
Console.WriteLine(currentOperation.Name);

2.7.4.2 Creating a string operation
Custom OCL operations may only be registered in the IOclService and IActionLanguageService. This is due to the fact thatthese are evaluated in memory, to implement an IOclPsService operation would involve a way to translate the operation to
SQL, and this functionality is not currently supported in ECO. There is a class named OclOperationBase which may be used
to simplify the process of creating custom OCL operations, when descending from this base class you only need to override
two methods; Init and Execute.

The following example illustrates how to create an operation which reverses a string.
public class ReverseStringOperation : OclOperationBase{//Recommended approach. Have a static method that registers the operation into//the correct OCL services//(as some may be intended for the Action Language Service only)
public static void InstallOperation(IEcoServiceProvider serviceProvider){
if (serviceProvider == null)
throw new ArgumentNullException("ServiceProvider");
ReverseStringOperation impl = new ReverseStringOperation();serviceProvider.GetEcoService<IOclService>().InstallOperation(impl);
serviceProvider.GetEcoService<IActionLanguageService>().InstallOperation(impl);}//Called when the OCL operation is installed
protected override void Init(){//Define the operation namestring operationName = "reverse";//Specify the source must be a string
IOclType sourceType = Support.StringType;//Initialise using// Operation name// Parameters - The first is the source
1312.7 Query services

ECO Services

ITypeService// Return type - In this case the same as the source
InternalInit(
operationName,
new IOclType[] { sourceType },sourceType);}//Executed when the operation needs to be processed
public override void Evaluate(IOclOperationParameters oclParameters){//Get the first value (the source) as an element, and cast it to a string
string value = oclParameters.Values[0].Element.GetValue<string>();//Use a StringBuilder to reverse the string's characters
StringBuilder resultBuilder = new StringBuilder();for (int index = value.Length - 1; index >= 0; index--)resultBuilder.Append(value[index]);//Create a constant IElement representing the reversed string
IElement result =Support.VariableFactory.CreateConstant(resultBuilder.ToString());//Set the result of the operation
oclParameters.Result.SetOwnedElement(result);}}

2

This example would be used like so//You should install operations in the EcoSpace constructor!
ReverseStringOperation.InstallOperation(EcoSpace);
string reversed =EcoSpace.Ocl.Evaluate("Person.allInstances->first.firstName.reverse").GetValue<string>();

2.7.4.3 Creating a collection operation
The following operation shows how to create an operation based on a collection, and also how to specify additionalparameters. Unlike the ReverseStringOperation example the InternalInit method is called with an
OclResultTypeDeduceMethod parameter for the result type instead of an IOclType. This informs the evaluator to deduce theresult type from the source.
public class SampleCollectionOperation : OclOperationBase{//Register the operation
public static void InstallOperation(IEcoServiceProvider serviceProvider){
if (serviceProvider == null)
throw new ArgumentNullException("ServiceProvider");
SampleCollectionOperation impl = new SampleCollectionOperation();serviceProvider.GetEcoService<IOclService>().InstallOperation(impl);
serviceProvider.GetEcoService<IActionLanguageService>().InstallOperation(impl);}//Initialise the operation details
protected override void Init(){//The operation namestring operationName = "sample";//The source must be a list
IOclType sourceType = Support.ListType;//1st parameter must be an integer (number of samples)
IOclType numberOfSamplesType = Support.IntegerType;132

2.8 IPersistenceService

ECO Services//Return type is the same as the source, but allow duplicates.
//This is because the source might be a collection of strings
OclResultTypeDeduceMethod resultType = OclResultTypeDeduceMethod.SourceAsBag;//Register
InternalInit(
operationName,
new IOclType[] { sourceType, numberOfSamplesType },resultType);}
public override void Evaluate(IOclOperationParameters oclParameters){//Get the source as a collection
IElementCollection source =oclParameters.Values[0].Element.GetAsCollection();//Get the number of samples to takeint numberOfSamples = oclParameters.Values[1].Element.GetValue<int>();if (numberOfSamples > source.Count)numberOfSamples = source.Count;double currentIndex = 0;
double stepSize = source.Count / (double)numberOfSamples;//Create an element to hold the result
IElementCollection result =(IElementCollection)Support.CreateNewVariable(oclParameters.Result.OclType);//Add samples to the result
while (numberOfSamples > 0){
int readIndex = (int)Math.Round(currentIndex);
if (readIndex >= source.Count)readIndex = source.Count - 1;
result.Add(source[readIndex]);numberOfSamples--;currentIndex += stepSize;}oclParameters.Result.SetOwnedElement(result);}}

2.8 IPersistenceService
The persistence service is responsible for mediating between the EcoSpace and the data storage. Rather than acreate/retrieve/update/delete approach ECO's persistence service implements create/update/delete via a single instruction
to update the data storage, ECO's internal state management will track which type of operation is appropriate based on
actions performed against the local object cache (creating a new object, modifying an object, or deleting an object); freeing
the developer from having to concern themself with which type of call to make.

Persisting changes to the data storage
The most simple way to update an object to the data storage is to call the UpdateDatabase method with a single object.

//Create a new person and persist it
Person person1 = new Person(EcoSpace);
EcoSpace.Persistence.UpdateDatabase(person1);//Modify an existing person and persist the changes
person1.FirstName = "John";
133

22.8 IPersistenceService

ECO Services

EcoSpace.Persistence.UpdateDatabase(person1);//Delete an existing person and persist the deletion
person1.AsIObject().Delete();EcoSpace.Persistence.UpdateDatabase(person1);

Whenever an UpdateDatabase is performed ECO will additionally remove any undo or redo blocks held by the undo service( see page 9) which reference the object that is having its changes persisted. This is in order to prevent the consumer ofthe business model from making changes, persisting the changes, and then undoing those changes in the local cache;effectively making the local cache out of sync with the data storage.
Usually the application consuming the business model is solely responsible for deciding when to update the data storage,
but sometimes part of the business logic dictates that an update made by the model should be persisted immediately. For
example, if your business model implements a custom form of pessimistic locking

public bool AcquireLock(PessimisticLock lock){//01: Throw an exception if lock.LockedBy is not null//02: Unload the lock object to make it current//03: Set lock.LockedBy to the current user//04: Attempt to update the database//05: Catch any optimistic locking exception

2}

In the preceding pseudo code it is necessary to update the data storage immediately in order to ensure the custom
pessimistic lock is required. If there is an active undo block then the changes performed here will be recorded by that block,the call to UpdateDatabase will then remove the block from the undo service to prevent further use of it, which could cause aproblem for the application using the model as it would rightly expect the undo block to still be present. The correct approach
would therefore be

public bool AcquireLock(PessimisticLock lock){//01: Throw an exception if lock.LockedBy is not null//02: Unload the lock object to make it current//** Start a new undo block//03: Set lock.LockedBy to the current user//04: Attempt to update the database//05: Catch any optimistic locking exception//** In case of an exception remove the undo block//from the undo service}

Another operation performed by ECO is to ensure that any update to the data storage is logical. If we refer back to the drivervehicle model from earlier in this document we can see that the following source code is a complete logical operation

Driver driver1 = new Driver(EcoSpace);
Vehicle vehicle1 = new Vehicle(EcoSpace);
EcoSpace.Persistence.UpdateDatabase(driver1);1342.8 IPersistenceService

ECO Services

But the following example is not a complete logical operation

Driver driver1 = new Driver(EcoSpace);
Vehicle vehicle1 = new Vehicle(EcoSpace);//Create an association between the two new objects
driver1.CurrentVehicle = vehicle1;
EcoSpace.Persistence.UpdateDatabase(driver1);

This is code is not a complete logical operation because there is a reference from driver1 to vehicle1 which has not yet been
persisted. In order to ensure the update operation is a complete logical unit ECO will make a call to the EnsureEnclosure
method on the persistence service, which ensures that all objects required to make the update are also included in the
update.

In this example the enclosure occurs because the vehicle refers to an object that has not been persisted, but enclosure may
also occur when a reference changes. In the Driver/Vehicle model it is likely that one of the ends of the association ismarked as embedded. If the Driver end of the association is marked as embedded it means that the primary key of the
Driver is embedded into the table holding the Vehicle data, so the Vehicle table would have a column named "Driver".

DriverPrimary key

Name

1234

John Smith

VehiclePrimary key

RegistrationNumber

Driver

4321

DE 51 RED

1234

driver1.CurrentVehicle = null;
EcoSpace.Persistence.UpdateDatabase(driver1);

In the preceding data tables you can see that the driver "John Smith" is currently assigned the vehicle "DE 51 RED". In thecode snippet John Smith (a.k.a. driver1) has had his vehicle unassigned. The changes made to driver1 are then persisted
but in order to make the update complete "DE 51 RED" must also be updated as it is Vehicle's database table that actuallyholds the reference.

Note: It is possible that neither end of the association is marked as embedded, resulting in a "link table" in the databaseholding the primary key of each side of the association. This is common in many-to-many associations and quite rare in
one-to-one associations.

Persisting a collection of objects' changes
It is also possible to persist changes to multiple instances as a single database operation. The UpdateDatabaseWithList
method accepts an IObjectList parameter which may be obtained in a number of ways, the most commonly used are

135

2

2.8 IPersistenceService

ECO Services

Updating all changes
IObjectList dirtyObjects = EcoSpace.DirtyList.AllDirtyObjects();
EcoSpace.Persistence.UpdateDatabaseWithList(dirtyObjects);

Note: This code is the equivalent of EcoSpace.UpdateDatabase()

Updating changes captured by an undo block
string blockName = Guid.NewGuid().ToString();
EcoSpace.Undo.StartUndoBlock(blockName);//Make changes here
IUndoBlock undoBlock = EcoSpace.Undo.UndoList[blockName];
IObjectList dirtyObjects = undoBlock.GetChangedObjects();
EcoSpace.Persistence.UpdateDatabaseWithList(dirtyObjects);

Unloading object contents
Unloading an instance of a business class simply removes its loaded property values from the local EcoSpace cache. An
object cannot be unloaded if its class has been modeled as Transient, if the EcoSpace has no persistence (which effectively
makes all instances Transient), an instance can also only be unloaded if it is unmodified.

EcoSpace.Persistence.Unload(person1);

It is possible to query whether or not an object's contents have been loaded using the IsLoaded method:

IObjectList objects = ...........;
if (!EcoSpace.Persistence.IsLoaded(objects[0])).....;The IObjectList in this case would most likely be the result of evaluating an OCL expression. It would be pointless obtaining
the IObject reference using person1.AsIObject(), IObject is merely an object-locator whereas person1 would be of themodeled "Person" type, and in order to have a reference to the modeled type the object's contents would first need to beloaded into the local EcoSpace cache.

Efficiently retrieving lists of objects
Associations in ECO are lazy fetched, meaning that an associated object is only loaded if it is accessed.

Vehicle vehicle1 = {Some code to retrieve a single vehicle};//Accessing its driver will load the driver from the data storage
Driver driver1 = vehicle1.Driver;If the association is a multi-role then only the object locators are loaded when the property is first accessed, the contents of
the objects are loaded when an attempt is made to access an individual object in the collection.

Customer customer1 = {Some code to retrieve a single customer};1362

2.8 IPersistenceService

ECO Services//Accessing the customer's purchase orders will retrieve ID's only
if (customer1.Orders.Count > 0){//Accessing an order by index will load the single order's contents
PurchaseOrder order1 = customer1.Orders[0];}Iterating through a list of orders using a for loop would be inefficient as it would result in a single data storage fetch per orderin the collection. ECO has a number of techniques for improving the performance of fetching associated objects. The first,
and most simple, is to use an enumerator to loop through the elements.

//Efficient approach, using an enumerator indicates your intention//to use multiple orders in the collection. ECO will load the associated//objects in batches of 50.
foreach (var currentOrder in customer1.Orders)...
//Inefficient approach, using a specific index does not reveal any intention//to use multiple associated orders, so ECO loads only the order at the//specified index.
for (int i = 0; i < customer1.Orders.Count; i++){
PurchaseOrder currentOrder = customer1.Orders[i];}2The same applies when you evaluate an OCL expression to retrieve a collection of objects. ECO will firstly only retrieve
object IDs from the data storage, it will only load the objects' contents when you attempt to access them.

//Only ID's are retrieved from the data storage
IObjectList list = EcoSpace.OclPs("Customer.allInstances->select(isActive)");//The first customer in the list will have its contents loaded from the data storage
Customer customer1 = list[0].GetValue<Customer>();An IObjectList is merely a collection of object locators which may be converted to instances of modeled classes. Because ofthis when you use an enumerator you are iterating object locators and not instances of modeled classes; as a consequence
using an enumerator will not automatically fetch object contents because the step to retrieve the class instance is anadditional one://Retrieve object ID's
IObjectList list = ...;
foreach (IObject locator in list){//Single operation within the loop to convert to an//instance of a modeled class
Customer currentCustomer = locator.GetValue<Customer>();}If your intention is to iterate over the instances referred to by these object locators (rather than merely to use the list as thecontext for a second OCL evaluation) it is possible to instruct ECO to pre-load the objects in the locator list.

IObjectList list = ...;//Easiest way, if you intend to iterate over all instances.
1372.8 IPersistenceService

ECO Services

Multi user concurrency//This pre-loads the objects and returns an IList of the//relevant type.
IList<Customer> customers = list.GetAsIList<Customer>();Or alternatively to pre-load only a subset

IObjectList list = ...;//Pre-load a subset of the objects in the locator list.
int firstIndex = 0;
int lastIndex = list.Count / 2;
EcoSpace.Persistence.EnsureRange(list, firstIndex, lastIndex);

Efficiently loading related objects
The previous section shows how to efficiently load objects either in a locator list, or associated from a single object. Thereare circumstances where your code needs to perform a nested loop; for example to loop through every PurchaseOrder ofevery customer in a list.

//Example 1: Using OCL to eliminate the need for an outer loop
string ocl = "Customer.allInstances->select(isActive).orders";
var orders = EcoSpace.OclPs.Execute(ocl).GetAsIList<PurchaseOrder>();//Example 2: Pre-loading an association on a list of object locators
IObjectList list = EcoSpace.OclPs.Execute("Customer.allInstances->select(isActive)");EcoSpace.Persistence.EnsureRelatedObjects(list, "Orders");

Related Topics
For information regarding the GetAllWithCondition see the Version Service (see page 141).

1382

2.9 IExtentService

ECO Services

2.8.1 Multi user concurrency
TODO

2.9 IExtentService
Each instance of an EcoSpace contains an extent for every class in the model. If a request to IExtentService.AllInstances ismade, or if an OCL expression is evaluated that specifies "SomeClass.AllInstances" an object locator will be created forevery instance in the data storage and held in the extent service. Any subsequent request to the extent service for the same
information will return this cached collection rather than accessing the data storage again. Access is only made to the data
storage again if

1. The extent for the specific class has not yet been requested for this EcoSpace instance.
2. The extent service of the current EcoSpace has previously been instructed by the programmer to unload the extent for the
specified class.
Note that the extent service's state is unique per EcoSpace and not shared amongst multiple EcoSpaces. So if two
EcoSpace instances request the extent for the same class they will both make a request to the data storage.

The extend manages two pieces of information:Type

Description

AllInstancesThis is a list of object locators, one for each instance of the specified type.

AllLoadedInstances This again is a list of object locators. Instead of being one locator for every instance of the specified
type it contains a list of all previously retrieved customer locators.
For example, if you were to navigate to a customer via one of its purchase orders this would result in
the customer's locator being added to the AllLoadedInstances list for the Customer class.

//Get all previously loaded customers
int loadedCustomerCount = EcoSpace.Extents.AllLoadedInstances(typeof(Customer)).Count;//Get a locator list for all customers - data storage access is required
IObjectList customerLocators = EcoSpace.Extents.AllInstances(typeof(Customer));//Get a locator list for all customers - not data storage access required
IObjectList customerLocators2 = EcoSpace.Extents.AllInstances(typeof(Customer));

Unloading an extent
An EcoSpace instance should be thought of as a unit of work, or as the equivalent of a database transaction. It is for thisreason that the extents are cached, so that behaviour is predictable and to improve performance. There may sometimes becircumstances where you wish to invalidate the extent for a specific class.

139

2

2.10 IVersionService

ECO Services//Invalidate the extent for Customer
IClass classToUnload = (IClass)EcoSpace.TypeSystem.GetClassifierByType(typeof(Customer));
EcoSpace.Extents.Unload(classToUnload);

If for example you are writing a WinForm application which has an ExpressionHandle with the expression"Customer.allInstances", unloading the extent for the Customer class will cause the ExpressionHandle to reevaluate anddisplay any new instances that may have been created by other users. Unloading the extent does not unload any objects'
data contents.

Subscribing to changes
It is possible to subscribe to two events of the extent service. Using the SubscribeToObjectAdded method it is possible toregister an observer which will be called back each time a new locator is added to the extent service. This could be due tocreating a new instance of a class or due to loading an existing instance from the data storage.

//A simple class to show a message box containing the class name of//the locator added
public class NewObjectNotifier : SubscriberAdapterBase{
public NewObjectNotifier(object actualSubscriber): base (actualSubscriber){}

2

protected override void DoReceive(object sender, EventArgs e, object
actualSubscriber){var args = (ElementChangedEventArgs)e;
MessageBox.Show(args.Element.UmlType.Name);}}//Example usage//Create the subscriber
var subscriber = new NewObjectNotifier(this);//Subscribe to locators being added for any class, this is achieved//using the "ECOModelRoot" class, which is the superclass of all//classes within the model.
EcoSpace.Extents.SubscribeToObjectAdded(subscriber, "ECOModelRoot");

To subscribe to locators being removed you may use the SubscribeToObjectRemoved. This method is almost the mirror of
SubscribeToObjectAdded. Instead of triggering whenever an object is created or loaded the subscriber is triggered
whenever an object is deleted. The trigger is executed as soon as customer.AsIObject().Delete() is executed, rather thanwhen a deleted object is updated using the persistence service (which causes the object locator to be relinquished).

Note that the subscriber is not triggered when an extent is unloaded, nor when an object's contents are unloaded. Unloading
an object's contents merely causes the local cached data values to be unloaded, it does not result in the EcoSpace
relinquishing the locator for the object (the object's identity is still known).

1402.10 IVersionService

ECO Services

2.10 IVersionService
Using the version service via the IVersionService interface, the developer is able to retrieve historical information about
objects that have been identified as "Versioned" in the ECO model.

Object instances of classes that have been marked as Versioned are treated differently by the ECO persistence mechanism.
By default each object within the database will have two additional columns, "TimeStampStart" and "TimeStampStop".
These columns identify the life span of versioned objects.

Each time UpdateDatabase is executed a new integer timestamp is value allocated, and the current date/time recorded
against it. These integers are used to identify at which date/time a versioned object instance is created, modified, or deleted.
When a new object instance is created the current timestamp is entered into its TimeStampStart column, and 2147483647 isentered into its TimeStampStop column, this records when the object came into existence, and the high TimeStampStop
indicates that this row in the database is the current "live" data for the object.

2
TimeStampStart

TimeStampStop

ECO_ID

FullName

102147483647

5Miss Jane Smith

When a versioned object is modified the TimeStampStop column of the live row is updated to the current timestamp value,
and a new row is inserted into the table. This new row has the same ECO_ID (the unique identifier for an ECO object
instance), the current timestamp for TimeStampStart, and the new modified attribute values.

TimeStampStart

TimeStampStop

ECO_ID

FullName

10

10

5

Miss Jane Smith

11

2147483647

5

Mrs Jane Jones

Finally, when a versioned object is deleted, the TimeStampStop column of the live row is updated with the current timestamp- 1.

TimeStampStart

TimeStampStop

ECO_ID

FullName

10

10

5

Miss Jane Smith

11

11

5

Mrs Jane Jones

To enable versioning on a class you must

1. Set Versioned = True on the class in the modeler.
2. Set the following properties to True on the PersistenceMapper that your EcoSpace uses
• UseClockLog• UseTimestampColumn• UseTimestampTable
There is also a VersionGranularity property on the persistence mapper. If set to its default value time span of 00:00:00 a newversion will be created for every call to UpdateDatabase. If set higher it is possible to instruct ECO to consider changes to141

2.10 IVersionService

ECO Services

the same object within a specific window of time to be considered the same update, and not to create a new version of theobject being updated.

Retrieving a historical version of an object instance
To retrieve a historical version of an instance you will first need to convert a specific date and time to a version number.
Once you have the correct version number it is simple to retrieve the historical version of that object, all historical object
versions are read only.

//Specify the date and time
var pointInTime = new DateTime(...);//Convert the date/time to a version number
int versionNumber = EcoSpace.Versioning.VersionAtTime(pointInTime);
Person person1 = {Some code to get a customer instance};
Person historical =EcoSpace.Versioning.GetVersion(versionNumber,customer1.AsIObject()).GetValue<Person>();MessageBox.Show(string.Format("Changed from {0} to {1}",historical.FullName, person1.FullName));2

Showing all changes to an object
The GetChangePointCondition method creates an instance of AbstractCondition, which may then be used with thepersistence service to retrieve a full history of an individual object. Starting from an ECO WinForms application add thefollowing code to the constructor of your form.

//Create a person
Person person1 = new Person(EcoSpace);//Set the person's name and update the databaseperson1.FullName = "Miss Jane Smith";
EcoSpace.UpdateDatabase();//Sleep for 1 second
Thread.Sleep(1000);//Change the person's name and update the databaseperson1.FullName = "Mrs Jane Jones";
EcoSpace.UpdateDatabase();Assuming you have correctly versioned the Person class and set up versioning on the EcoSpace's persistence this code will
create two versions of a person when the application starts. To display this history in a WinForm DataGrid execute thefollowing steps

1. Set rhRoot.StaticValueTypeName to "Collection(Person)" - without the quotes.
2. Add a DataGrid to your form, and use rhRoot as its data source.
3. Add the following additional code to the bottom of your form's constructor
var condition =EcoSpace.Versioning.GetChangePointCondition(
rhRoot.Element, //Object to be retrieved
0, //Earliest version to retrieve1422.10 IVersionService

ECO Services

EcoSpace.Versioning.CurrentVersion //Latest version to retrieve);//Retrieve all versions of this person
IObjectList historicalVersions =EcoSpace.Persistence.GetAllWithCondition(condition);//Set the reference handle to hold the list of versions
rhRoot.SetElement(historicalVersions);

Running the application should give you a form that looks similar to the following illustration. Note that you cannot use theXML persistence for this example.

2

Adding each version's date and time
To show the date and time of each version we first need to add a code-derived column to the expression handle

1. Bring up the Columns editor on rhRoot.
2. Click the drop-down arrow to the right of the "Add" button and select "EventDerivedColumn".
3. Name the column VersionTimeStamp.
4. Set its TypeName property to System.DateTime.
5. Click OK.
Now that the code-derived column has been added add the following code to the DeriveValue event of rhRoot.

private void rhRoot_DeriveValue(object sender, DeriveEventArgs e){
switch (e.Name){case "VersionTimeStamp"://Get the current version number of the current row//Each row will have a different version number
int versionNumber = EcoSpace.Versioning.ElementVersion(e.RootElement);//Convert the version number to a date/time
DateTime timeStamp = EcoSpace.Versioning.TimeForVersion(versionNumber);//Set this date/time as the value to display in the data gride.ResultElement = EcoSpace.VariableFactory.CreateConstant(timeStamp);
break;
default:
throw new NotImplementedException(e.Name);
}
}

143

2.12 Subscriptions

ECO Services

Once you have added the new column to the data grid you should see something like the following when you run theapplication.

2.11 ICacheContentService2

2.12 Subscriptions
When creating a business model it is often necessary to create members that have no persistent value but are instead
calculated from other values. ECO supports this feature via "Derived" members. When creating a model it is possible tomark a property / association as Derived, indicating to ECO that the value needs to be calculated. When an attempt is madeto read the value of a derived member ECO will perform the necessary actions to calculate its value. The calculated value isthen stored away in the local cache, so that what it is read again no further calculations are required.

If calculating the value of the derived member is costly then storing the result in the cache will obviously save resources
whenever the value is read again. However, if the calculated value where just to be stored away indefinitely it could easily
become "stale." For example if Person.FullName were to be derived using the following OCL expression

salutation + ' ' + givenName + ' ' + familyName

If the value of any of these three members changes then rereading a stale cached value would result in an incorrect result.
This is where the ECO subscription mechanism comes in.

In the OCL derived member example above as ECO parses the OCL expression above in order to calculate the result it willneed to access various members of the model; in this case Person.Salutation, Person.GivenName, andPerson.FamilyName. Each derived member has its own "subscriber", as the ECO OCL evaluator accesses the value of amember it adds this subscriber to a list of parties interested in knowing when the member's value changes.

Once the subscriptions have been placed with the relevant members and the result determined the derived member's value
will be cached. Subsequent reads of the derived member will return the cached value. When one of the three members in
the expression change they will notify every subscriber that has been registered with them. In this case the only subscriber
will be the one owned by FullName, when this subscriber's Receive() method is executed it will invalidate its cached value.
144

2.12 Subscriptions

ECO Services

Any subsequent attempt to read the value of the derived member will see that there is no cached value for it and cause the
OCL evaluator to reprocess the expression and replace any required subscriptions.

Subscribing to derived members
A derived member may use any type of member as part of its calculated value. Associations, persistent members, transient
members, and also other derived members. Take a simple class as an example, consisting of only three members.

Name

Type

Derivation code

Transient1 Transient
Derived1

Codederivedprivate string Derived1Derive(){
System.Diagnostics.Debug.WriteLine(" Model: Calculating Derived1");
return "D1 + " + Transient1;}

Derived2

Codederivedprivate string Derived2Derive(){
System.Diagnostics.Debug.WriteLine(" Model: Calculating Derived2");
return "D2 + " + Derived1;}

2

now consider the following application code

private void ReadDerived1(Class_1 instance){
System.Diagnostics.Debug.WriteLine("App: Reading derived1");
System.Diagnostics.Debug.WriteLine(" Result = " + instance.Derived1);}
private void ReadDerived2(Class_1 instance){
System.Diagnostics.Debug.WriteLine("App: Reading derived2");
System.Diagnostics.Debug.WriteLine(" Result = " + instance.Derived2);}
private void ChangeTransient(Class_1 instance, string value){
System.Diagnostics.Debug.WriteLine("App: Changing transient1 to " + value);
instance.Transient1 = value;}

These instructions have only been made into methods in order to log how the application is using the domain object, and to
make the steps easier to demonstrate.

Step Instruction

Output1

ChangeTransient(instance,"Hello world");

App: Changing transient1 to Hello world

2

ReadDerived1(instance);

App: Reading derived1
Model: Calculating Derived1
Result = D1 + Hello world1452.12 Subscriptions

ECO Services

3

ReadDerived2(instance);

App: Reading derived2
Model: Calculating Derived2
Result = D2 + D1 + Hello world

4

ChangeTransient(instance,"Goodbye world");

App: Changing transient1 to Goodbye world

5

ReadDerived2(instance);

App: Reading derived2
Model: Calculating Derived2
Model: Calculating Derived1
Result = D2 + D1 + Goodbye world

1. The transient member has its value changed. This is just to start with a meaningful value. It has no effect on derived
members as none of them have been accessed yet and therefore have not placed any subscriptions.
2. The value of Derived1 is read. This calculation is based only on Transient1. The value is calculated and stored in the local
cache. Derived1 places a subscription on Transient1.
3. The value of Derived2 is read. This calculation is based only on Derived1. When the value of Derived1 is read ECO seesthat it has previously been calculated and cached, the calculation is not performed again, instead the cached value isreturned. Derived2 places a subscription on Derived1.

2

4. The transient member is modified. As a consequence a change notification is sent to all of its subscribers.
1. Derived1 receives a notification that one of the members it has subscribed to has been modified.
2. Derived1 invalidates its cached value.
3. Derived1 notifies all of its subscribers that its value has possibly changed.
4. Derived2 receives a notification that one of the members it has subscribed to has been modified.
5. Derived2 invalidates its cached value.
5. The value of Derived2 is read.
1. There is no cached value for Derived2 so its value is recalculated.
2. Derived2 reads the value of Derived1
3. There is no cached value for Derived1 so its value is recalculated.
4. Derived1's value is cached.
5. Derived2's value is cached.

Auto subscription
As mentioned previously the OCL evaluator will automatically subscribe to any members it accesses during evaluation of an
expression. In the previous example however the values were accessed via source code and not an evaluator, so how were
the subscriptions placed?

All member values are stored in a local cache. Whenever a read/write is performed on a .NET instance of a modeled ECOclass the property uses the local ECO cache to read/write the value. This means that ECO is fully aware of any time a value
is touched, and as a result is able to identify which elements make up a derived member. When an attempt is made toaccess a derived member ECO performs the following steps

1. If there is a cached value.
1. Return the cached value.
2. Finish.
2. If the member has an OCL expression.
1462.13 ITypeSystemService

ECO Services

1. Evaluate the expression.
2. Place subscriptions on accessed elements.
3. Store the result in the cache.
4. Return the cached value.
5. Finish.
3. Find a method named <MemberName>Derive.
1. The members subscriber is pushed onto the IAutoSubscriptionService's stack.
2. The <MemberName>Derive method is executed.
3. Any access to an element in the cache checks the ActiveSubscriber in the auto subscription service, and registers it asa party interested in being notified when the element's value changes.
4. The <MemberName>Derive method returns a result.
5. The member's subscriber is removed from the auto subscription service's stack.
6. Cache the result.
7. Finish.

2

2.13 ITypeSystemService
The type system services allows your application to inspect your model in great detail at runtime.

Short example - Identifying all classes used by an EcoSpace
The first and most simple example shows how to identify all classes used by the EcoSpace. If the EcoSpace uses multiple
class packages this list will include all classes of all packages used.

foreach (IClass c in ecoSpace.TypeSystem.AllClasses)
Trace.WriteLine(c.Name);

The output from the preceding source code (viewed in the Debug->Windows->Output window in Visual Studio) is as follows

ECOModelRoot
CustomerOrder147

2.13 ITypeSystemService

ECO Services

IModelElement

OrderLine
Product
ProductCategory
ProductCategoryProducts
In the output you will see five class names you would expect to see after looking at the UML diagram for the model but there
are two additional names you may not have expected, the first and last in the list. When ECO builds a runtime representationof your model it inspects it for a common superclass from which all classes ultimately descend, if no such class exists ECO
will add an ECOModelRoot class which acts as an equivalent of System.Object for your model.
The other unexpected class was ProductCategoryProducts. When you model a many to many association (in this casebetween Product and ProductCategory) ECO creates an implicit association class based on the name given to theassociation. You wont see a business class source file generated for this class, it is actually created as an embedded classof the package, this class exists only to provide additional meta-information to ECO at runtime and for creating databasestructures. Of course if you explicitly define an association class between Product and ProductCategory you will get a fullbusiness class generated in which you may define additional members.

Changing the source code as follows

foreach (IClass c in ecoSpace.TypeSystem.AllClasses)
Trace.WriteLine(string.Format("Name={0} Implicit={1} IsLink={2}",c.Name, c.IsImplicit, c.IsLinkClass));2

Will provide the following data, which I have formatted as a table.
Name

Implicit

IsLinkECOModelRoot

True

False

Customer

False

False

Order

False

False

OrderLine

False

False

Product

False

False

ProductCategory

False

False

ProductCategoryProducts

True

True

In the following UML diagrams I have used the following colour scheme.
1. Grey : Elements which are created once when the details of the runtime model is first established.
2. Purple : Elements which hold meta information about values which may be created at runtime, such as variables andconstants created by the IVariableFactoryService ( see page 18).

148

2.13 ITypeSystemService

ECO Services

IPackage

2.13.1 IModelElement

The IModelElement interface is the base interface for almost all ECO model meta information. This interface allows you todetermine the Name of the element, which Package it belongs to, and a collection of OCL constraints. The modeler also
permits IModelElement descendants such as classes and their members to have tagged values ( see page 151) assigned
to them which may be read at runtime.

2.13.2 IPackage

A list of packages used by an EcoSpace may be obtained using from the EcoSpace.TypeSystem.AllPackages property.
Each package contains a collection of classes and associations that were modeled within it.

foreach (IPackage package in ecoSpace.TypeSystem.AllPackages){
Debug.WriteLine(string.Format("ID={0} Classes={1} Associations={2}",package.Id, package.Classes.Count, package.Associations.Count));}

The output from the preceding code would be something like

ID={SomeGUID} Classes=5 Associations=4

Based on the simple Customer/Order model defined at the start of this section you will see there are five explicitly modeledclasses and four associations. If you think back to the early source code example there were two additional classes
ECOModelRoot and ProductCategoryProducts, these do not appear within the Classes collection because they were notexplicitly modeled. Adding the following code within the above loop149

2

2.13 ITypeSystemService

ECO Services

IPackageforeach (IPackage package in ecoSpace.TypeSystem.AllPackages){//Previous code omitted
foreach (IModelElement element in package.OwnedElements)
Debug.WriteLine(string.Format(" Element={0} Type={1}", element.Name, element.GetType().Name));}Will result in the following output

ID={SomeGUID} Classes=5 Associations=4
Element=Order Type=UmlClass
Element=ProductCategory Type=UmlClass
Element=OrderLine Type=UmlClass
Element=Product Type=UmlClass
Element=Customer Type=UmlClass
Element=OrderCustomer Type=UmlAssociation
Element=OrderLineOrder Type=UmlAssociation
Element=OrderLineProduct Type=UmlAssociation
Element=ProductCategoryProducts Type=UmlAssociation2

Note how ProductCategoryProducts is merely an association. This is because the information within an IPackage reflects
how exactly how you modeled it. When ECO initialises its runtime model it is necessary to implicitly create items in order tomake the model execute. For example I mentioned earlier how an ECOModelRoot class is created if there isn't a single
common super class for all classes in a package, if the EcoSpace were to consume two modeled packages which had nodependencies upon each other then there couldn't possibly be a common super class. In this case ECO would certainly
need to create an implicit super class (akin to a persistent System.Object), but which IPackage would this super class belong
to? The answer is that it wouldn't belong to either. The IPackages' meta-information remains unaltered, it is the EcoSpace's
run meta-information that hold this implicit class, along with implicit classes for associations.

The previous output lists five classes and four associations. The output from the following source code (which uses the
EcoSpace's TypeSystem)

foreach (IClass c in EcoSpace.TypeSystem.AllClasses)
Debug.WriteLine("Class=" + c.Name);

will show seven classes

Class=ECOModelRootClass=Customer
Class=OrderClass=OrderLine
Class=Product
Class=ProductCategory
Class=ProductCategoryProducts

ECOModelRoot was added to the EcoSpace's runtime TypeSystem to cater for not having a common super class. In
addition to having an association named ProductCategoryProducts there is also an implicit association class created. The
reason this association class needs to exist is quite obvious. When changes are made to objects' state within an EcoSpaceECO identifies which instances' changes need to be persisted to the datastore by identifying each modified instance as"Dirty" (modified). When it comes to modifying associations in a one-to-one or a one-to-many association the ID of the linked
1502.13 ITypeSystemService

ECO Services

IClassinstance is stored in a single end of the association (e.g. the Order identity is embedded into the OrderLine). So adding an
OrderLine to an Order will mark the OrderLine dirty and not the order, so ECO knows that only the OrderLine needs to bepersisted to the datastore.

There are two scenarios however where neither end of the association is considered dirty. In a many-to-many association
neither side of the association is considered dirty because a database table can typically only hold single values, so neither
side's database table can hold a collection of identities. In such a situation it is common practise when writing a databaseapplication to create a link table, consisting of two IDs (one for each side of the association). By creating the implicit class fora many-to-many association ECO is doing the same thing, not only does it identifying the fact that a link table is required
within the database but it also enables ECO to identify which parts of the association are dirty (A1--B1 was removed, A1--B2
was added). When modifying a many-to-many association only instances of this link-class are considered dirty.

The other scenario is the case where neither side of an association is embedded. This means that the identity of neither sideof the association is stored in the opposite side. In this case ECO will again create an implicit link class for storing theassociation.

2.13.3 ITaggedValue

2

ITaggedValue is a name/value pair of two strings. Anything implementing IModelElement has a collection of tagged values
which may be obtained via its TaggedValues property. Elements such as classes, properties, and methods may be
decorated with named values using the modeler during design time; these tagged values may then be read at runtime by theapplication.

For example if you select a class (Class1) in the model, click the TaggedValues editor, and then add a tagged value with thename "MyCompany.DisplayName" the tagged value could be read as follows
IClass c = EcoSpace.TypeSystem.GetClassByType(typeof(Class1));
ITaggedValue tv = c.GetItemByTag("MyCompany.DisplayName");SomeLabel.Text = tv.Value;

2.13.4 IStructuralFeature
A structural feature is anything on a class which holds state information. A method on a class holds no state and is therefore
not a structural feature. Any modeled element on a class which produces a property in the generated code is considered astructural feature, this could be either an IAttribute or an IAssociationEnd.

This interface provides information about the state holder such as whether it is persistent / derived / transient, if it is derived /derived settable, and the IClassifier which represents the .NET type of the member. The members of IStructuralFeature ,IAttribute, and IAssociationEnd are quite straight forward so wont be covered in this document, for more information about
these interfaces please read the API documentation.

151

2.13 ITypeSystemService

ECO Services

IClass2.13.5 IClassThe IClass interface holds meta-information about classes. As an IModelElement it is possible to identify all tagged values
assigned to it during modeling.

Hierarchy
IClass has properties named SuperTypes and SubTypes. Although in .NET you can only descend your class from a single
class the decision was made to make SuperTypes multiple in order to conform to the UML specification, SuperTypes will
always contain zero or one entries. SubTypes on the other hand may obviously contain any number of entries.

Given the previous model it is possible to map the structure at runtime using the following recursive source code.

public void OutputEcoSpaceHierarchy(){//Only if we have at least one classif (EcoSpace.TypeSystem.AllClasses.Count > 0)//Output the super class
OutputClassHierarchy(0, EcoSpace.TypeSystem.AllClasses[0]);}private void OutputClassHierarchy(int indent, IClass currentClass){string indentText = new string(' ', indent);
Debug.WriteLine(indentText + currentClass.Name);//Output each sub typeforeach (IClass childClass in currentClass.SubTypes)
OutputClassHierarchy(indent + 2, childClass);}

This code would result in the following ouptut

MyExplicitSuperClassChildClass1
152

2

2.13 ITypeSystemService

ECO Services

IClassGrandChildClass1
GrandChildClass2
ChildClass2
GrandChildClass3
GrandChildClass4

The AllClasses property is always sorted into a hierarchical order. A sub type will never have a lower index in the collection
than its super type, ultimately the IClass at index zero will always be the root class for the entire runtime model, whether it is
implicitly created or explicitly created as in this example.

Model hierarchy

2
In addition to being an IModelElement the IClass interface also descends from IFeaturedType. This type is used for modeledclasses and for adhoc query classes when executing a query in OCL or LINQ which returns a tuple (collection of data rather
than instances of modeled types).

Using the IFeaturedType.AllStructuralFeatures property and the IClass.AllMethods properties it is possible to determine thestructure of a class.

public void ShowOrderClassStructure(){
OutputClassStructure(EcoSpace.TypeSystem.AllClasses.GetItemByName("Order"));}private void OutputClassStructure(IClass currentClass){foreach (IFeature feature in currentClass.AllStructuralFeatures)153

2.13 ITypeSystemService

ECO Services

IClass{Debug.WriteLine(string.Format("Feature - {0} {1} {2}",feature.Visibility,
feature.Name,feature.FeatureType));}foreach (IMethod method in currentClass.AllMethods){
Debug.WriteLine(string.Format("{0} {1}",method.Visibility,
method.Name));}}

OutputFeature - Public_ Customer AssociationEnd
Feature - Public_ OrderLines AssociationEnd
Feature - Public_ Number Attribute
Feature - Public_ TotalValue Attribute
Method - Public_ AddProduct
Method - Public_ RemoveProduct2

Association classes
If the class reference is an association class, either implicit or explicit, its Association property will identify the association it
represents.

With a reference to the FoodAllergy's IClass it is possible to obtain the IAssociation reference it represents and obtain
additional information about the association; such as whether the association is derived / transient / persistent, or to obtain
information about the classes at either end of the association (Person / Food) and the multiplicity of the association ends.

1542.13 ITypeSystemService

ECO Services

IClassClass features

When accessing IClass.AllStructuralFeatures the result will contain structural features modeled in the class and all structuralfeatures it inherits from its ancestor classes. Iterating this collection of IStructuralFeature will give every structural feature
available to the class, if you wish to iterate only structural features introduced by the current you can use theFirstOwnStructuralFeatureIndex property.

2

IClass c = EcoSpace.TypeSystem.GetClassByType(typeof(BaseClass));for (int i = c.FirstOwnStructuralFeatureIndex; i < c.AllStructuralFeatures.Count; i++){
IStructuralFeature f = c.AllStructuralFeatures[i];Console.WriteLine(f.Name);}

Class meta-data example
This example uses the previous Customer/Order model.

public Form1(EcoProject20.EcoProject20EcoSpace ecoSpace){1552.13 ITypeSystemService

ECO Services

IClass//Code omitted
IClass c = EcoSpace.TypeSystem.GetClassByType(typeof(Order));
OutputClassInfo(c);}A class is considered to be either Persistent or Transient, c.Persistent reflects the persistence state of the class. The
DefaultStringRepresentation is the expression that is evaluated whenever the "asString" OCL expression is evaluatedagainst an object instance.

private void OutputClassInfo(IClass c){
Console.WriteLine("Name : {0}", c.Name);
Console.WriteLine(" Persistent : {0}", c.Persistent);
Console.WriteLine(" DefaultStringRepresentation : {0}",c.DefaultStringRepresentation);
Console.WriteLine(" Methods");
OutputClassMethods(c);Console.WriteLine(" Attributes");
OutputClassAttributes(c);2Console.WriteLine(" Association ends");
OutputClassAssociationEnds(c);}[Output]
Name : Order
Persistent : True
DefaultStringRepresentation : self.Number

IClass.AllMethods contains method information for each modeled method on the class. This will contain inherit methods, but
not methods which were not added via the modeler.

private void OutputClassMethods(IClass c){
foreach (IMethod m in c.AllMethods){
Console.WriteLine("
Name : {0}", m.Name);if (m.ReturnType != null)
Console.WriteLine("
Returns : {0}", m.ReturnType.Name);
else
Console.WriteLine("
Returns : void");
OutputMethodParameters(m);}}private void OutputMethodParameters(IMethod m){foreach (IParameter p in m.Parameters)
Console.WriteLine("
Parameter {0} of type {1} - direction {2}", m.Name,p.Type.Name, p.Kind);}[Output]
Methods
Name : AddProduct
Returns : void
Parameter AddProduct of type Product - direction In156

2.13 ITypeSystemService

ECO Services

IClassName : RemoveProduct
Returns : void
Parameter RemoveProduct of type Product - direction In

As with AllMethods only members added to the class via the modeler will appear in AllStructuralFeatures. The list willcontain both inherited and introduced members. The output of the following code is the result of inspecting the OrderLine
class rather than the Order class, because the OrderLine class has a derived member. The AllStructuralFeatures list willcontain both IAttributes and IAssociationEnds, the example code filters the list to show only IAttributes using the LINQ
"OfType" filter.

private void OutputClassAttributes(IClass c){
foreach (IAttribute a in c.AllStructuralFeatures.OfType<IAttribute>()){
Console.WriteLine("
Name : {0} of type {1}", a.Name, a.Type_.Name);
Console.WriteLine("
Persistent : {0}", a.Persistent);
if (a.IsDerived){
Console.WriteLine("
Derived and settable : {0}", a.IsReverseDerived);
if (a.DeriveAndSubscribeMethod != null)
Console.WriteLine("
Derived using method : {0}",a.DeriveAndSubscribeMethod.Name);
else
Console.WriteLine("
Derived using expression : {0}",a.TaggedValues.GetItemByTag("Eco.DerivationOCL").Value);
}
}
}

2

[Output]
Attributes
Name : ProductPrice of type System.Decimal
Persistent : True
Name : LineValue of type System.Decimal
Persistent : False
Derived and settable : False
Derived using expression : quantityOrdered * productPrice
Name : QuantityOrdered of type System.Int32
Persistent : True

The following code outputs meta information about IAssociationEnds on the Order class. As with the previous IAttributeexample the list is filtered using LINQ.

private void OutputClassAssociationEnds(IClass c){
foreach (IAssociationEnd a in c.AllStructuralFeatures.OfType<IAssociationEnd>()){
Console.WriteLine("
Name : {0}", a.Name);
Console.WriteLine("
Persistent : {0}", a.Persistent);
if (a.IsDerived){
Console.WriteLine("
Derived and settable : {0}", a.IsReverseDerived);
if (a.DeriveAndSubscribeMethod != null)
Console.WriteLine("
Derived using method : {0}",a.DeriveAndSubscribeMethod.Name);
else
Console.WriteLine("
Derived using expression : {0}",a.TaggedValues.GetItemByTag("Eco.DerivationOCL").Value);}157

2.13 ITypeSystemService
Console.WriteLine("
Console.WriteLine("a.Multiplicity.Upper);}}ECO Services

IClassNavigable : {0}", a.IsNavigable);
Multiplicity : {0}..{1}", a.Multiplicity.Lower,[Output]
Association ends
Name : Customer
Persistent : True
Navigable : True
Multiplicity : 1..1
Name : Lines
Persistent : True
Navigable : True
Multiplicity : 0..2147483647

If the association where derived the output would reveal how it is derived, either as an OCL expression or via a method call.
In addition it would also indicate whether the member is derived and settable (IsReverseDerived). This is also available on
IAttribute as it is inherited from IStructuralFeature.

2

158

3

ECO Services

3 Registering custom services

3159

4.2 A validating IPersistenceService

ECO Services

4 Replacing standard ECO services
4.1 Replacing the ExternalIdService
4.2 A validating IPersistenceService
Constraints in ECO are not enforced by default. This is because it is up to the application developer to decide when toevaluate constraints and also how to handle constraints when they are broken. One approach is to prevent the user fromsaving changes when there are broken constraints, however a sensible backup strategy is to ensure there are no broken
constraints when the datastorage is updated; this protects the persistent data from becoming corrupted if the application
developer neglects to enforce constraints at a single point in the application.

The easiest way to replace the IPersistenceService for an EcoSpace is to descend a new class from theChainedPersistenceServiceBase class. In this following code sample you will see the following

1. A constructor is added accepting the IEcoServiceProvider. This is done so that references to other services may beobtained where necessary.
2. The NextPersistenceService property is set. This ensures that all persistence requests are passed on to the real
implementer.
3. UpdateDatabaseWithList<T> is overridden. This is so that the objects being updated may be validated first.
This method uses the DroopyEyes.EcoExtensions.Validation.ModeledConstraintProvider class to obtain constraints for agiven instance, this class is used to simplify the example.

public class ValidatingPersistenceService : ChainedPersistenceServiceBase{
readonly IEcoServiceProvider ServiceProvider;public ValidatingPersistenceService(IEcoServiceProvider serviceProvider): base(){if (serviceProvider == null)
throw new ArgumentNullException("ServiceProvider");//Save the service provider reference, and set NextPersistenceService
ServiceProvider = serviceProvider;
NextPersistenceService = ServiceProvider.GetEcoService<IPersistenceService>();}4

public override void UpdateDatabaseWithList<T>(IEnumerable<T> list){
var constraintProvider = new ModeledConstraintProvider();
foreach (IObject instance in list){//Ignore deleted objects
160

4.2 A validating IPersistenceService

ECO Services

if (instance.Deleted)continue;//Get a list of IConstraint instances for the object being updated
var constraints = new List<IConstraint>();constraintProvider.GetConstraintsForObject(instance, constraints);//Find the first broken constraint
var brokenConstraint = constraints.FirstOrDefault(c => !c.IsValid);//If a constraint is broken throw an exception showing the//object class + the constraint nameif (brokenConstraint != null)
throw new Exception(instance.AsObject.GetType().Name + ":" +brokenConstraint.Name);}base.UpdateDatabaseWithList<T>(list);}}

To install the service register it when the EcoSpace becomes active by overriding the Active property.

public override bool Active{
get{return base.Active;}
set{base.Active = value;
if (Active)
RegisterEcoService<IPersistenceService>(newValidatingPersistenceService(this));}}When an attempt is made to call UpdateDatabase on an EcoSpace where one of the objects has a broken constraint an
exception will be thrown with a message similar to

Person : FullName required

4

161

5

ECO Services

Index

AllInstancesAtTime 50
AllLoadedObjects 51
AllSubClasses 51-

AllSuperClasses 51- 28, 49

Append 54
AsBag 54*
* 29

AsCommaList 54
Asin 85
AsSeparatedList 55

/
/ 29

+
+ 28, 50

AsSequence 56
AsSet 56
AssociationEnds 52
AsString 52
At 52
Atan 86
Atan2 86<

AtTime 53< 29

Attributes 53<= 30

Average 32

<> 30

B
=

BigMul 86

= 30

C
>

Ceiling 87> 31

Chars 101>= 31

Clear 128
ClrSubstring 102

A
A validating IPersistenceService 160
Abs 85
Acos 85
Add 129
AddDays 66
AddHours 66
AddMilliseconds 67
AddMinutes 67
AddMonths 67
AddSeconds 68
AddTicks 68

Collect 57
Collection operations 53
Compare 61
Concat 102
Constraints 62
Contains 103
Cos 87
Cosh 87
Count 57
Create 63, 129
Creating a collection operation 132
Creating a string operation 131

AddYears 68
AllInstances 31a5

ECO Services

D

H

Date 69

Hour 74

Date and time operations 66

Hours 74

Day 69
DayOfYear 70

I

Days 69

IActionLanguageService 128

DaysInMonth 70

ICacheContentService 144

Delete 129

IClass 152

Difference 32

IDirtyListService 8

Div 32

IEcoServiceProvider 1

Duration 71

IExtentService 139
IExternalIdService 4

E

If 84

EmptyList 83

IModelElement 149

EndsWith 103

Implies 34

Excluding 58

Includes 34

Existing 83

IncludesAll 59

Exists 33

Including 59

Exp 87

InDateRange 75

ExternalId 84

IndexOf 59, 104
Insert 106

F

InstalledOperations 131

FilterOnType 58

Intersection 34

First 58

InTimeRange 75

Floor 88

IObjectFactoryService 16

ForAll 33

IOclPsService 25

Format 104

IOclService 44

FormatDateTime 71

IPackage 149

FromBinary 71

IPersistenceService 133

FromDays 72

IsControl 107

FromFileTime 72

IsDaylightSavingTime 75

FromFileTimeUtc 72

IsDigit 107

FromHours 73

IsEmpty 35

FromMilliseconds 73

IsHighSurrogate 108

FromMinutes 73

IsInfinity 88

FromSeconds 74

IsLeapYear 76

FromTicks 74

IsLetter 108
IsLetterOrDigit 109

G
GetNumericValue 104

IsLower 110
IsLowSurrogate 110
IsNaN 88b

5

ECO Services
IsNegativeInfinity 89

ModifiedSinceTimeStamp 94

IsNormalized 111

Month 77

IsNull 35

Multi user concurrency 139

IsNullOrEmpty 111
IsNumber 111

N

IsPositiveInfinity 89

Negate 77, 91

IsPunctuation 112

NewGuid 95

IsSeparator 113

Normalize 118

IsSurrogate 113

Not 37

IsSurrogatePair 114

NotEmpty 38

IsSymbol 115

Now 77

IStateService 6
IStructuralFeature 151
IsUpper 115
IsWhiteSpace 116
ITaggedValue 151
ITypeService 130
ITypeSystemService 147
IUndoService 9
IVariableFactoryService 18
IVersionService 141

LO
ObjectFromExternalId 95
ObjectTimeStamp 95
OCL operations supported by IOclPsService 28
OCL operations supported by IOclService 48
OclAsType 96
OclGetStates 98
OclGetTriggers 100
OclIsInState 100
OclIsKindOf 96
OclIsTypeOf 97

Last 60

Operations support by IActionLanguageService 128

LastIndexOf 117

OrderBy 38

Length 36

OrderDescending 39

Let 84

OrderGeneric 39

Log 89Overview 1

Log10 90

MP
Pad 118

Mathematical operations 85

PadLeft 118

Max 90

PadRight 119

MaxLength 94

Parse 97

MaxValue 36

PostPad 120

Millisecond 76

Pow 91Milliseconds 76

Prepend 60

Min 90
Minute 76
Minutes 77
MinValue 36

Q
Query services 22

Mod 37c

5

ECO Services

R

T

RegExpMatch 120

TaggedValue 124

Registering custom services 159

Tan 93

Reject 40

Tanh 93

Remainder 91

Terminology 2

Remove 120, 129

Ticks 78

RemoveAt 130

Time 79

Replace 121

TimeOfDay 79

Replacing standard ECO services 160

TimeSpan operations 124

Replacing the ExternalIdService 160

TimeStampToTime 79

Round 92

TimeToTimeStamp 79
ToBinary 80

S

ToByte 125

SafeCast 98

Today 80

Second 78

ToDouble 125

Seconds 78

ToFileTime 80

Select 40

ToFileTimeUtc 80

Sin 92

ToInt16 125

Sinh 92

ToInt32 125Size 40

ToInt64 126

SqlLike 41

ToLocalTime 80

SqlLikeCaseInsensitive 41

ToLongDateString 81

Sqrt 93

ToLongTimeString 81

Standard ECO services 3

ToLower (Char) 123

StartsWith 121

ToLower (String) 42

StartTransaction, RollbackTransaction, andCommitTransaction 9

ToLowerInvariant 123

State machine operations 98

ToShortDateString 81

String operations 101

ToShortTimeString 81

StrToDate 121

ToSingle 126

StrToDateTime 122

TotalDays 81

StrToInt 122

TotalHours 82

StrToTime 122

TotalMilliseconds 82

Subscriptions 144

TotalMinutes 82

SubSequence 60

TotalSeconds 82

SubString 122

ToUInt16 126

Sum 42ToUInt32 127

SumTime 78

ToUInt64 127

SuperTypes 124

ToUniversalTime 82

SymmetricDifference 61

ToUpper (Char) 123

ToSByte 126

ToUpper(String) 43d

5

ECO Services
ToUpperInvariant 123
Trim 124
Truncate 94
TypeName 127

U
Undo blocks 10
Union 43
UtcNow 83

W
Working with an undo block 15
Working with the undo service 12

X
Xor 127

Y
Year 83e