You can use let in OCL and EAL expressions to name an intermediate result, but you must materialize a value before changing data that a derived association depends on.
Understand what let holds
A let expression gives a name to an expression result for use later in the same OCL or EAL expression.
let lp = self.lastSubPart in
lp.NameThis is useful when an expression is long or when you use the same result more than once. However, when the result is a derived association, the binding can retain a reference to the derivation rather than a fixed reference to the object currently returned by that derivation.
A derived association is an association whose value is calculated from an expression. Derived values track the domain-layer values used by their derivation so that they can be marked out of date and re-evaluated when those values change. Therefore, a let binding to a derived association can produce a different object after you change links or objects used by that association.
The problem: change data after binding a derived association
Assume that a class has an ordered subParts association and a derived single-link association named lastSubPart:
self.subParts->lastThe following EAL intends to add a part and name it after the part that was last before the addition:
let lp = self.lastSubPart in
(
self.subParts.add(newPart);
newPart.Name = 'copy of ' + lp.Name
)Do not rely on this expression to preserve the original last subpart. When self.subParts.add(newPart) adds newPart, it changes the collection used by lastSubPart. The derived association is then re-evaluated. At the later use of lp.Name, lp can refer to newPart, not to the subpart that was last when the expression began.
For example, if the existing ordered subparts are A and B, lastSubPart initially returns B. After adding newPart, it returns newPart. The name assignment can consequently use newPart.Name instead of B.Name.
Preserve the object before making the change
Convert the derived single-link result to a set and select its object before changing the source association:
let lp = self.lastSubPart->first in
(
self.subParts.add(newPart);
newPart.Name = 'copy of ' + lp.Name
)Here, ->first takes the first object from the set. The lp binding is the object itself, rather than the reference to the derived-association derivation. Adding newPart no longer changes which object lp identifies.
Use this pattern when modifying derivation inputs
Follow these steps when an OCL or EAL expression both reads a derived association and changes something that the association's derivation reads:
- Identify the derived association, such as
lastSubPart. - Identify the change that affects its derivation, such as adding to
subParts, removing an object, or changing a link used in the derived expression. - Before the change, materialize the object you need from the derived result. For a derived single link, use the pattern
let saved = self.derivedLink->first in (...). - Perform the change.
- Use the saved object, not the derived association, for values that must come from the pre-change result.
When to take care
This issue matters when all of the following are true:
- You bind a derived association in
let. - The expression later changes an object, attribute, or association that the derivation depends on.
- You expect the later code to use the object returned before that change.
The same reasoning applies when deleting objects. If deletion changes the result of a derived expression, do not use a let binding to the derived association as though it were a frozen object reference. Materialize the object required by the later expression before the deletion or other change.
Keep derived behavior separate from writable derived behavior
This page describes reading a derived association while performing changes in an expression. Derived attributes and associations are read-only calculations; see Documentation:Derived attributes & associations for their derivation and update behavior. If you need an association that accepts assignment and performs EAL when it is set, use a derived settable association instead. For equivalent behavior on an attribute, see derived settable attributes.
See also
- Documentation:Derivation expressions
- Documentation:Derived attributes & associations
- Documentation:Association
- Documentation:Derived settable associations
- Training:Bootcamp:Chapter 7
[[Category:Attributes and data types [Beginner]]]
