ElementDrivesElement Class
Beta
A Relationship indicating that one Element drives another Element. An ElementDrivesElement relationship defines a one-way "driving" relationship from the source to the target. When the source of an ElementDrivesElement relationship changes, the ElementDrivesElement itself can get a callback, and both the source and target elements can get callbacks. By inserting ElementDrivesElement relationships, an app can create and store an acyclic directed graph of dependencies between elements.
Defining dependencies
Create an ElementDrivesElement relationship to specify that the source element drives the target element. For example, to specify that element e1 drives element e2, create a relationship between them like this:
const ede = ElementDrivesElement.create(iModel, e1id, e2id);
ede.insert();
This creates a persistent relationship. The fact that e1 drives e2 is persisted in the iModel.
Defining dependency graphs
When you create multiple ElementDrivesElement relationships, you create a network of dependencies. The target of one may be the source of another. A change in the content of an DgnElement can therefore trigger changes to many downstream elements.
For example, to make element e1 drive element e2 and e2 drive another element, e3, create two relationships like this:
const ede12 = ElementDrivesElement.create(iModel, e1id, e2id);
const ede23 = ElementDrivesElement.create(iModel, e2id, e3id);
ede12.insert();
ede23.insert();
Those two relationships create this graph:
e1 --> e2 --> e3
Where the "-->" is meant to represent a driving relationship.
The order in which you create the relationships does not matter. The graph indicates that e3 depends on e2 and e2 depends on e1.
An ElementDrivesElement relationship is between one source element and one target element. Many ElementDrivesElement relationships can point to a given element, and many can point out of it. Thus, you can define many:many relationships. For example:
const ede12 = ElementDrivesElement.create(iModel, e1id, e2id);
const ede112 = ElementDrivesElement.create(iModel, e11id, e2id);
const ede23 = ElementDrivesElement.create(iModel, e2id, e3id);
const ede231 = ElementDrivesElement.create(iModel, e2id, e31id);
ede12.insert();
ede112.insert();
ede23.insert();
ede231.insert();
Creates this graph:
e1 e3
\ /
e2
/ \
e11 e31
e2 depends on both e1 and e11. e2 then drives e3 and e31.
In an ElementDrivesElement dependency graph, the relationships are the "edges" and the Elements are the "nodes". The following terms are used when referring to the elements (nodes) in a dependency graph:
- Inputs - The sources of all edges that point to the element. This includes all upstream elements that flow into the element.
- Outputs - The targets of all edges that point out of the element. This includes all downstream elements.
Subgraph Processing
When changes are made, only the part of the overall graph that is affected will be processed. So, for example, suppose we have this graph:
e1 --> e2 --> e3
If e1 changes, then the subgraph to be processed is equal to the full graph, as shown.
If only e2 changes, then the subgraph to be processed is just:
e2 --> e3
If only e3 changes, then the subgraph consists of e3 by itself.
Returning to the second example above, suppose we have this graph:
e1 e3
\ /
e2
/ \
e11 e31
If e1 is changed, the affected subgraph is:
e1 e3
\ /
e2
\
e31
If e2 is changed, the affected subgraph is:
e3
/
e2
\
e31
Callbacks
Once the affected subgraph to process is found, it propagates changes through it by making callbacks. Classes for both elements (nodes) and ElementDrivesElements relationships (edges) can receive callbacks.
ElementDrivesElement Callbacks
The following callbacks are invoked on ElementDrivesElement relationship classes (edges):
- onRootChanged
- onDeletedDependency
Note that these are static methods. Their default implementations do nothing. To receive and act on these callbacks, a domain should define a subclass of ElementDrivesElement and use that to create relationships. The subclass should then implement the callbacks that it would like to act on.
A ElementDrivesElement subclass callback is expected to make changes to the output element only!
Element Callbacks
The following callbacks are invoked on Element classes (nodes):
- Element.onBeforeOutputsHandled
- Element.onAllInputsHandled
Order
Callbacks are invoked by BriefcaseDb.saveChanges. They are invoked in dependency (topological) order: driving elements first, then driven elements.
Each callback is invoked only once. No matter how many times a given element was changed during the transaction, a callback such as ElementDrivesElement.onRootChanged will be invoked only once. In the same way, no matter how many of its inputs were changed, a callback such as Element.onAllInputsHandled will be invoked only once.
For example, suppose we have a graph:
e1 --> e2 --> e3
Suppose that e1 is directly modified. No callbacks are made at that time. Later, when BriefcaseDb.saveChanges is called, the following callbacks are made, in order:
- Element.onBeforeOutputsHandled e1
- ElementDrivesElement.onRootChanged e1->e2
- Element.onAllInputsHandled e2
- ElementDrivesElement.onRootChanged e2->e3
- Element.onAllInputsHandled e3
Suppose that e3 is modified directly and BriefcaseDb.saveChanges is called. Since no input to a relationship was changed, the sub-graph will be empty, and no callbacks will be made.
Returning to the second example above, suppose we have this graph:
e1 e3
\ /
e2
/ \
e11 e31
If e1 is changed and BriefcaseDb.saveChanges is called, the subgraph is:
e1 e3
\ /
e2
\
e31
The callbacks are:
- Element.onBeforeOutputsHandled e1
- ElementDrivesElement.onRootChanged e1->e2
- Element.onAllInputsHandled e2
- ElementDrivesElement.onRootChanged e2->e3
- Element.onAllInputsHandled e3
- ElementDrivesElement.onRootChanged e2->e31
- Element.onAllInputsHandled e31
(The ElementDrivesElement.)
#Errors Circular dependencies are not permitted. If a cycle is detected, that is treated as a fatal error. All ElementDrivesElement relationships involved in a cycle will have their status set to 1, indicating a failure.
A callback may call txnManager.reportError to reject an invalid change. It can classify the error as fatal or just a warning. A callback make set the status value of an ElementDrivesElement instance to 1 to indicate a processing failure in that edge.
After BriefcaseDb.saveChanges is called, an app should check db.txns.validationErrors and db.txns.hasFatalError to find out if graph-evaluation failed.
Extends
Methods
Name | Description | |
---|---|---|
toJSON(): ElementDrivesElementProps | ||
create<T extends ElementDrivesElement<T>>(iModel: IModelDb, sourceId: string, targetId: string, priority: number = 0): T Static |
Inherited methods
Name | Inherited from | Description |
---|---|---|
collectReferenceIds(referenceIds: Set<string>): void Protected | Relationship | Collect the Ids of this entity's references at this level of the class hierarchy. |
delete(): void | Relationship | Delete this Relationship from the iModel. |
forEachProperty(func: PropertyCallback, includeCustom: boolean = true): void | Relationship | Call a function for each property of this Entity. |
getReferenceIds(): Set<string> | Relationship | Get the Ids of this element's references. |
insert(): string | Relationship | Insert this Relationship into the iModel. |
update(): void | Relationship | Update this Relationship in the iModel. |
getInstance<T extends Relationship<T>>(iModel: IModelDb, criteria: string | SourceAndTarget): T Static | Relationship | |
is(otherClass: undefined): boolean Static | Relationship | return whether this Entity class is a subclass of another Entity class |
onDeletedDependency(_props: RelationshipProps, _iModel: IModelDb): void Static | Relationship | Callback invoked by saveChanges on an ElementDrivesElement relationship when the relationship instance has been deleted. |
onRootChanged(_props: RelationshipProps, _iModel: IModelDb): void Static | Relationship | Callback invoked by saveChanges on an ElementDrivesElement relationship when its input has changed or is the output of some upstream relationship whose input has changed. |
Properties
Name | Type | Description | |
---|---|---|---|
priority | number | Affects the order in which relationships are processed in the case where two relationships have the same output. | |
status | number | Relationship status |
Inherited properties
Name | Type | Inherited from | Description |
---|---|---|---|
classFullName Accessor ReadOnly | string | Relationship | Get the full BIS class name of this Entity in the form "schema:class". |
classFullName Accessor StaticReadOnly | string | Relationship | Get the full BIS class name of this Entity in the form "schema:class" |
className Accessor ReadOnly | string | Relationship | The name of the BIS class associated with this class. |
id | string | Relationship | The Id of this Entity. |
iModel | IModelDb | Relationship | The IModelDb that contains this Entity |
isInstanceOfEntity Readonly | "true" | Relationship | An immutable property used to discriminate between Entity and EntityProps, used to inform the TypeScript compiler that these two types |
schema Static | undefined | Relationship | The Schema that defines this class. |
schemaName Accessor ReadOnly | string | Relationship | The name of the BIS Schema that defines this class |
sourceId Readonly | string | Relationship | |
targetId Readonly | string | Relationship |
Defined in
- backend/src/Relationship.ts Line 383
Last Updated: 30 November, 2023