This article shows you how to handle schema change events correctly. The example
application contains a simple list of object names from the UName.Naming
schema. You should use this as an example of how to guarantee that your application
displays the correct state of any Ubisense schema.
The example also contains a list of the 10 most recent events that occur on the
UName.Naming schema and the ObjectName relation. You
can use this to aid your understanding of when events occur.
There are 3 types of events associated with every Ubisense schema class, for example the UName.Naming.Schema.
These are as follows:
Commit events: These occur when schema state changes.
Events are triggered whenever a transaction is committed on the schema state.
Establish events: These occur immediately after schema state is resynchronised with the underlying server schema state.
When this occurs, no assumptions can be made about the state of the schema based on what was in it before the event.
Invalid events: These occur just before establishing schema state.
This is provided so you can evaluate the differences between the previous invalid state and the established state.
The invalid event is rarely used and can be ignored by non-advanced users.
There are 3 types of events associated with every Ubisense relation class.
That is, every table such as ObjectName. These are as follows:
Insert events: These occur when a row is inserted into the table.
Update events: These occur when a row is updated.
Delete events: These occur when a row is deleted.
If your application displays a list with some schema state in, for example the list of
object names, the minimum requirements to ensure your list is up-to-date are as follows:
You handle either commit events of the schema OR you handle all insert, update and delete events of the relation.
You must always handle establish events.
You must populate your list when your program starts. Since connecting to a schema guarantees an establish event,
it is recommended that you connect after registering the event handlers to do this cleanly.
Handling schema events
You register schema event handlers by calling the appropriate members of your schema object:
// The example schema. Here we are using the UName.Naming schema, // but this example applies to all Ubisense schemas. Ubisense.UName.Naming.Schema schema = new Ubisense.UName.Naming.Schema(false);
// Register the event handler delegates. schema.AddCommitHandler(CommitHandler); schema.AddEstablishHandler(EstablishHandler); schema.AddInvalidHandler(InvalidHandler);
// Connect as a client to the naming schema. This guarantees that // an establish event will occur, so should be done *after* adding // the establish event handler. schema.ConnectAsClient();
The most straightforward approach is then to fully re-populate your list from the schema cache
in both the commit and establish event handlers:
// This will be called whenever a commit event occurs. privatevoid CommitHandler() { populateTable(); }
// This will be called whenever an establish event occurs. privatevoid EstablishHandler() { populateTable(); }
// This will be called whenever an invalid event occurs. privatevoid InvalidHandler() { // You can save the invalid state of the schema here. // This is provided so you can evaluate the differences // between the invalid state and the established state // and deal only with those differences. }
The populateTable() method for the object names example is as follows. Note in particular
that the event handlers are not run on the main thread, so BeginInvoke is required if
any Windows Forms components are being updated:
// Called from schema event handler delegates, so not from the main thread. privatevoid populateTable() { BeginInvoke(newMethodInvoker(delegate() { objectNameListView.Items.Clear(); using (Ubisense.UName.Naming.ReadTransaction xact = schema.ReadTransaction()) { foreach (Ubisense.UName.Naming.ObjectName.RowType row in Ubisense.UName.Naming.ObjectName.name_(xact)) { objectNameListView.Items.Add(row.name_); } } })); }
Handling relation events
You register relation event handlers by calling the appropriate static members of the relation class:
You now have much finer control because the relation event handlers have arguments specifying the
old and new rows in the table:
// Insert handler for the ObjectName relation. privatevoid InsertHandler(Ubisense.UName.Naming.ObjectName.RowType row) { }
// Update handler for the ObjectName relation. privatevoid UpdateHandler(Ubisense.UName.Naming.ObjectName.RowType old, Ubisense.UName.Naming.ObjectName.RowType row) { }
// Delete handler for the ObjectName relation. privatevoid DeleteHandler(Ubisense.UName.Naming.ObjectName.RowType old) { }
Summary
This is another example application that is not really useful as an application - it is intended that
you review the source code carefully and use it as a reference for writing your own code. The relevant
code in the
example material
is found in MainForm.cs.