ElementDrivesElement Class

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:

  1. Element.onBeforeOutputsHandled e1
  2. ElementDrivesElement.onRootChanged e1->e2
  3. Element.onAllInputsHandled e2
  4. ElementDrivesElement.onRootChanged e2->e3
  5. 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:

  1. Element.onBeforeOutputsHandled e1
  2. ElementDrivesElement.onRootChanged e1->e2
  3. Element.onAllInputsHandled e2
  4. ElementDrivesElement.onRootChanged e2->e3
  5. Element.onAllInputsHandled e3
  6. ElementDrivesElement.onRootChanged e2->e31
  7. 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
constructor(props: ElementDrivesElementProps, iModel: IModelDb): ElementDrivesElement Protected    
collectReferenceIds(referenceIds: EntityReferenceSet): void Protected Collect the Ids of this entity's references at this level of the class hierarchy.  
toJSON(): ElementDrivesElementProps Obtain the JSON representation of this Entity.  
create<T extends ElementDrivesElement<T>>(iModel: IModelDb, sourceId: string, targetId: string, priority: number = 0): T Static    

Inherited methods

Name Inherited from Description
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(): EntityReferenceSet Relationship Get the set of this entity's entity references, EntityReferenceSet.
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

Last Updated: 15 March, 2024