Table of Contents

Events

Aurelius provides an event system which you can use to receive callback notifications when some events occur, for example, an entity update or a item is included in a collection. This chapter explains how to use this event system and what events are available.

Using Events

Subscribing from code

Events in Aurelius are available in the Events property of the TMappingExplorer object. Such property refers to a TManagerEvents (declared in unit Aurelius.Events.Manager) object with several subproperties, each to them related to an event. For example, to access the OnInserted event of the default TMappingExplorer:

uses {...}, Aurelius.Mapping.Explorer, Aurelius.Events.Manager;

TMappingExplorer.Default.Events.OnInserted.Subscribe(
  procedure(Args: TInsertedArgs)
  begin
    // Use Args.Entity to retrieve the inserted entity
  end
);

TMappingExplorer.Default.Events.OnUpdated.Subscribe(
  procedure(Args: TUpdatedArgs)
  begin
    // Use Args.Entity to retrieve the updated entity
  end
);

In a less direct way, using method reference instead of anonymous method:

uses {...}, Aurelius.Mapping.Explorer, Aurelius.Events.Manager;

procedure TSomeClass.MyInsertedProc(Args: TInsertedArgs);
begin
  // Use Args.Entity to retrieve the inserted entity
end;

procedure TSomeClass.MyUpdatedProc(Args: TUpdatedArgs);
begin
  // Use Args.Entity to retrieve the updated entity
end;

procedure TSomeClass.RegisterMyEventListeners;
var
  Events: TManagerEvents;
begin
  Events := TMappingExplorer.Default.Events;
  Events.OnInserted.Subscribe(MyInsertedProc);
  Events.OnUpdated.Subscribe(MyUpdatedProc);
end;

The events are available in the TMappingExplorer object so the listeners will receive notifications about any event fired by any TObjectManager created that references the specified TMappingExplorer object. In other words, the events are "global" for that mapping explorer.

Listeners are method references that receive a single object as a parameter. Such object has several properties containing relevant information about the event, and differ for each event type. Names of event properties,  method reference type and arguments follow a standard. The event property is named "On<event>", method reference type is "T<event>Proc" and parameter object is "T<event>Args". For example, for the "Deleted" event, the respective names will be "OnDeleted", "TDeletedProc" and "TDeletedArgs".

All events in Aurelius are multicast events, which means you can add several events handlers (listeners) to the same event. When an event occurs, all listeners will be notified. This allows you to add a listener in a safe way, without worrying if it will replace an existing listener that might have been set by other part of the application. You should use Subscribe and Unsubscribe methods to add and remove listeners, respectively. Note that since listeners are method references, you must sure to unsubscribe the same reference you subscribed to:

var
  LocalProc: TInsertedProc;
begin
  LocalProc := MyInsertedProc;
  Events.OnInserted.Subscribe(LocalProc);
  {...}
  Events.OnInserted.Unsubscribe(LocalProc);
end;

Passing just the method name doesn't work:

Events.OnInserted.Subscribe(MyInsertedProc);
{...}
// this will NOT unsubscribe the previous subscription:
Events.OnInserted.Unsubcribe(MyInsertedProc);

TAureliusModelEvents Component

An alternative, more RAD way to use events is the TAureliusModelEvents component. Just drop the component in the form and double click the desired event in the object inspector to create an event handler.

The events available are exactly the same ones that you can set from code, like OnInserting, OnSqlExecuting, etc. See all the available events in this chapter.

Key properties

Name Description
ModelName: string The name(s) of the model(s) to be used by the manager. You can leave it blank, if you do it will use the default model. Two or more model names should be separated by comma. From the model names it will get the property TMappingExplorer component that will be passed to the TDatabaseManager constructor to create the instance that will be encapsulated.

Note the events are "cumulative", the same way you do it from code. It means that if you add two or more TAureliusModelEvents in your application and set an event handler for the same event in all of them, all the handlers will be fired. That's very convenient to event handler code that is specific to each context in your app.

Using Attributes

A third and even more straightforward way to respond to events is to use attributes. You can simply add an attribute to a method of an entity class, and that method will be invoked when the event is triggered. Consider the following example:

type
  {$RTTI EXPLICIT METHODS([vcPrivate..vcPublished])}
  TCustomer = class
  strict private
    [OnInserting] procedure OnInserting(Args: TInsertingArgs);
    [OnInserted] procedure OnInserted;

    [OnUpdated, OnInserted] procedure AfterModification;
  {...}
  end;

When TCustomer entity is about to be inserted in the database, the OnInserting method will be invoked. After the record is inserted, the method OnInserted is invoked.

Warning

By default, Delphi doesn't generate RTTI for non-published methods. That's why you must add the directive {$RTTI EXPLICIT METHODS([vcPrivate..vcPublished])} to your class. If you don't do that, Aurelius won't know about the mentioned methods and they will not be invoked when the events are triggered!

Note that you can use the same method to handle more than one event. The method AfterModification will be invoked when event OnUpdated is fired, and also when OnInserted is fired.

Finally, you can use two different signatures for the methods: the method can receive a single Args parameters of the type of the event (see the available events below).

Alternatively, you can declare the method without specifying any parameter. This is useful if you don't need any information from event arguments and you want to keep your class as clean as possible (no dependency on a specific event type).

Available events

OnInserting Event

Occurs right before an entity is inserted (create) in the database. Note that the event is fired for every entity that is about to be inserted. For example, a single Manager.Save call might cause several entities to be inserted, due to cascades defined in the associations. In this case the event will be fired multiple times, one for each saved entity, even when the developer only called Save once.

Example:

TMappingExplorer.Default.Events.OnInserting.Subscribe(
  procedure(Args: TInsertingArgs)
  begin
    // code here
  end
);

TInsertingArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Entity: TObject The entity about to be inserted.
Master: TMasterObjectValue The parent object of the object being inserted. This property comes with a value in the case of list items (ManyValuedAssociation) that don't have a reference back to parent (unidirectional). TMasterObjectValue has two relevant properties: "MasterObject" which is the instance of parent object, and "MasterAssocMember" which is the name of the list property the item being inserted belongs to (for example, "InvoiceItems").

OnInserted Event

Occurs right after an entity is inserted (create) in the database. Note that the event is fired for every entity inserted. For example, a single Manager.Save call might cause several entities to be inserted, due to cascades defined in the associations. In this case the event will be fired multiple times, one for each saved entity, even when the developer only called Save once.

Example:

TMappingExplorer.Default.Events.OnInserted.Subscribe(
  procedure(Args: TInsertedArgs)
  begin
    // code here
  end
);

TInsertedArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Entity: TObject The entity that was inserted.
Master: TMasterObjectValue The parent object of the object being inserted. This property comes with a value in the case of list items (ManyValuedAssociation) that don't have a reference back to parent (unidirectional). TMasterObjectValue has two relevant properties: "MasterObject" which is the instance of parent object, and "MasterAssocMember" which is the name of the list property the item being inserted belongs to (for example, "InvoiceItems").

OnUpdating Event

Occurs right before an entity is about to be updated in the database.

Example:

TMappingExplorer.Default.Events.OnUpdating.Subscribe(
  procedure(Args: TUpdatingArgs)
  begin
    // code here
  end
);

TUpdatingArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Entity: TObject The entity that is going to be updated.
OldColumnValues: TDictionary<string, Variant> Represents the old object state using column name/value pairs. Don't confuse it with property names/values. For example, if the object has a property named "Name" that is mapped to a column database "CUSTOMER_NAME", the dictionary will contain "CUSTOMER_NAME" in the string key, and the respective value. Thus, associations are also represented by the foreign key column names/values.
NewColumnValues: TDictionary<string, Variant> Same as OldColumnValues, but contains the new state values. Comparing what has changed between NewColumnValues and OldColumnValues will give you the names of the columns that will be updated in the database.
ChangedColumnNames: TList<string> Contains a list of names of all columns that will be updated in the UPDATE statement.
RecalculateState: Boolean If you have changed any property value of the entity that is about to be updated, you need to set RecalculateState to True to force Aurelius to recalculate the columns that were modified and update the object state in the manager cache. For better performance, leave it false if you haven't modified any property.

OnUpdated Event

Occurs right after an entity is updated in the database.

Example:

TMappingExplorer.Default.Events.OnUpdated.Subscribe(
  procedure(Args: TUpdatedArgs)
  begin
    // code here
  end
);

TUpdatedArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Entity: TObject The entity that was updated.
OldColumnValues: TDictionary<string, Variant> Represents the old object state using column name/value pairs. Don't confuse it with property names/values. For example, if the object has a property named "Name" that is mapped to a column database "CUSTOMER_NAME", the dictionary will contain "CUSTOMER_NAME" in the string key, and the respective value. Thus, associations are also represented by the foreign key column names/values.
NewColumnValues: TDictionary<string, Variant> Same as OldColumnValues, but contains the new state values. Comparing what has changed between NewColumnValues and OldColumnValues will give you the names of the columns that will be updated in the database.
ChangedColumnNames: TList<string> Contains a list of names of all columns that were updated in the UPDATE statement.

OnDeleting Event

Occurs right before an entity is about to be deleted from the database. Note that the event is fired for every entity deleted. For example, a single Manager.Remove call might cause several entities to be deleted, due to cascades defined in the associations. In this case the event will be fired multiple times, one for each deleted entity, even when the developer only called Remove once.

Example:

TMappingExplorer.Default.Events.OnDeleting.Subscribe(
  procedure(Args: TDeletingArgs)
  begin
    // code here
  end
);

TDeletingArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Entity: TObject The entity about to be deleted.

OnDeleted Event

Occurs right after an entity is deleted from the database. Note that the event is fired for every entity deleted. For example, a single Manager.Remove call might cause several entities to be deleted, due to cascades defined in the associations. In this case the event will be fired multiple times, one for each deleted entity, even when the developer only called Remove once.

When the event is fired, the entity object is still a valid reference, but will be destroyed right after the event listener returns.

Example:

TMappingExplorer.Default.Events.OnDeleted.Subscribe(
  procedure(Args: TDeletedArgs)
  begin
    // code here
  end
);

TDeletedArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Entity: TObject The deleted entity.

OnCollectionItemAdded Event

Occurs when an item is added to a collection, at database level. In other words, when the foreign key of an item entity is set to point to the parent entity.

Example:

TMappingExplorer.Default.Events.OnCollectionItemAdded.Subscribe(
  procedure(Args: TCollectionItemAddedArgs)
  begin
    // code here
  end
);

TCollectionItemAddedArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Parent: TObject The parent entity which contains the collection where the item was added to.
Item: TObject The item entity added to the collection.
MemberName: string The member name (field or property) of the parent entity that holds the collection.

OnCollectionItemRemoved Event

Occurs when an item is removed from a collection, at database level. In other words, when the foreign key of an item entity is set to null (or to a different parent entity).

Example:

TMappingExplorer.Default.Events.OnCollectionItemRemoved.Subscribe(
  procedure(Args: TCollectionItemRemovedArgs)
  begin
    // code here
  end
);

TCollectionItemRemovedArgs Properties

Name Description
Manager: TBaseObjectManager The TObjectManager object which fired the event.
Parent: TObject The parent entity which contains the collection where the item was removed from.
Item: TObject The item entity removed from the collection.
MemberName: string The member name (field or property) of the parent entity that holds the collection.

OnSqlExecuting Event

Occurs right before an SQL statement is executed.

Example:

TMappingExplorer.Default.Events.OnSqlExecuting.Subscribe(
  procedure(Args: TSQLExecutingArgs)
  begin
    // code here
  end
);

TSQLExecutingArgs Properties

Name Description
SQL: string The SQL statement that will be executed.
Params: TEnumerable<TDBParam> A list of TDBParam objects used for the SQL statement execution. The TDBParam object has the properties ParamName, ParamType and ParamValue.