![]() Last edit: 2008-07-11 Graham Wideman |
ModelRight |
Document Status | ||
Date (reverse) | Who | Description |
2008-07-10 | GW | Original |
This document provides my draft suggestions regarding the scope, sequence and structure of content for introducing the programmability area of ModelRight. |
Outline of sections below
As delivered, ModelRight provides a comprehensive environment for forward and reverse engineering databases. In addition, the environment provides a great deal of control over the graphical appearance of the model views, including the capability to add annotations and control style features.
To this foundation, ModelRight adds programmability features which provide opportunities to access and manipulate the database model and the diagram graphic appearance in powerful ways. Many of ModelRight's built-in capabilities use this programmability: during the forward-and reverse engineering processes, when ModelRight generates SQL to read or manipulate the database, it uses scripts to compose the SQL statements. You can modify these SQL-generating scripts to suit your own special purposes, different dialects of SQL and so on.
But that is just one use for ModelRight's programmability; here are some other scenarios that position ModelRight as a key component in a larger application development and maintenance process.
To gain traction on ModelRight programmability, there are two main themes to master:
These topics are described in more detail below.
ModelRight provides two main programmability approaches:
ModelRight includes Microsoft's Visual Basic Scripting (VBS) environment, which is at version 5.6 at this writing. Scripts can be created, edited and run within ModelRight, and are generally used to manipulate the current database model, or to inspect the database model and produce some kind of output, often SQL statements.
Scripts defined by the user are associated with a particular model and stored in the currently-open ModelRight model file (".wer" file).
You may already be familiar with the Visual Basic for Applications (VBA) environment found in Microsoft products like Access, Excel and Word. The VBS environment is similar in concept:
- the language is recognizably Visual Basic
- you write and execute code within the environment of a host application, like Excel
- code you write is stored in the "document" file (in this case "model" file) that it's associated with
- your code can interact with the data of the "document", by working with an object model made available by the application. For Excel those are objects like WorkSheet and Cell. For ModelRight the objects represent tables and columns, for example.
However, there are significant differences between VBA and VBS. In general VBS is suited to smaller-scaled functionality for less-demanding tasks. For example, variables in VBS are all "variant" -- they don't need to be declared as a particular data type. This is good for small tasks, but less suited to large and complicated bodies of code.
There are a variety of ways that the integration of VBS into ModelRight has been made specific to the needs of ModelRight. However the language itself, its general functions and features (not specific to ModelRight) are as documented here at Microsoft Developer Network: VBScript.
In addition to the built-in VBS programmability, ModelRight also provides an API through which to manipulate ModelRight models/diagrams from an external program, and this API is accessed via COM Automation. Consequently, such external programs can be written in your choice of language, including VBA from Access or Excel; VB, C# or C++ from .NET; Delphi, Python, PHP or any language which can create an Automation client.
This approach is especially powerful where ModelRight modeling activities are part of a larger database application workflow, as suggested in the introduction. For example:
In other scenarios, automation using an external program may simply be a better choice for accomplishing tasks that are beyond the complexity readily handled within the VBS environment.
ModelRight provides your code with the capability to read and manipulate its model of the database itself (tables, columns, relationships etc), and its model of the diagrams of the database (particular layouts of table graphics and relationship graphics on a page; annotations; and formatting of these).
- VBS scripts running within ModelRight have access to the model that's currently loaded. Hence, changes that your code makes to that model appear immediately on screen, possibly for use by subsequent steps.
- External programs (using automation) can load ModelRight files, read and manipulate that data and save it to a new file.
- VBS scripts and external automation programs have equivalent capabilities to access ModelRight's models -- the difference is only that VBS scripts access the model within the ModelRight program while external programs operate on ModelRight files independent of the ModelRight program.
Programmability not currently available:
- VBS scripts within ModelRight cannot automate the application's user-interface itself (such as commands on the menus) : (... unlike VBA in Excel or Access, for example). For example scripts cannot initiate reverse-engineering a database into a new model.
- Similarly, external programs cannot "remote-control" the ModelRight program itself (again unlike Excel/Word/Access automation).
To write useful code for ModelRight, you will need to become familiar with ModelRight's object model -- that is to say the objects that ModelRight exposes for your code to interact with. The general strategy is very similar to working with VBA to manipulate objects in Word or Excel, and in ModelRight goes something like this:
- Your code gets a reference to an initial object, such as Model
- Your code navigates to Model's children (for example Tables), and to their Children (such as columns), and is able to create, delete and modify them, and read and write their properties (names, data types and so on).
Exact details (or how to find out the exact details) are described in sections below. But first, a look at the user interface for programming, so that you can use it to explore ModelRight's objects.
In the
ModelRight application, you can browse, create or select scripts in the Script Explorer panel. If
this is not visible, you can summon it from the main menu: Window > Script Explorer.
Script Explorer shows three or more top-level folders, which entail three different kinds of scripts:
As you select (or create) scripts in the Script Explorer, the text of the script appears in an editor in the Property Browser panel. (To open: Window > Property Browser).
Main points:
To manually run a script (ie: cause it to load), click on the leftmost button (hint says: "Execute the script").
Pressing the "Execute the script" button also causes the display to change like this:
Here you see:
(Side note: Obviously with features to handle scripts in two languages -- VBS and SQL -- there's some room for ambiguity and confusion.)
Having covered some basic skills in operating ModelRight's programming environment, we can
proceed to discuss the objects that are available for your scripts to interact with: ModelRight's object
model. To learn what to expect in ModelRight's data structure, we will first look at various
browsers. After that we will make the jump to ModelRight's actual programming object model.
You no doubt already expect to find ModelRight's data to be organized like you see in the Model Explorer, pictured here, and as you see in the properties that display in the Property Browser as you select each item in the Model Explorer (or on the diagram).
The branches shown in the "Model Objects" panel are obviously of great interest, as these describe the database objects themselves: Tables, columns etc.
Of interest in other scenarios are the diagrams, appearing in the upper panel. Here the structure is Model > Model Subsets > Diagrams. Again, related properties appear in the Properties Browser as you select them in Model Explorer.
Less obvious is the fact that "drilling down" into a diagram brings you to the graphics for tables, views and so on that appear on the actual diagram page. You can make these appear in the Model Explorer by setting Tools > Options > Model Explorer > Diagrams: Display Graphics Objects. Whether you select graphics objects in the Model Explorer or on the diagram page, their properties again appear in the Property Browser.
We can follow tree branches like:
Model > Model Subsets > Diagrams > Table Graphics > Column Graphics.
So, the Model Objects branches correspond recognizably to our knowledge of databases and their parts, and the Diagrams branches correspond to the visual diagrams and graphics we see in ModelRight.
So far so good, but the Model Explorer, the diagrams themselves and the Property Browser are all intended for the end user (ie: database designer/analyst), and hide the technical details of the actual script-accessible objects. More investigation is needed.
The Model Browser (Model > Model Browser, and not to be confused with Model Explorer) displays the entire tree of objects and properties that constitute a ModelRight model, and shows it in a single tree view. Here in one view we can dig into all the collections, objects and properties that we previously saw in the two panes of the Model Explorer, and on the diagram, and in many panes-worth of Property Browser.
You may want to dig into the "Model > Table" branch, and the "Model > Model Subset...Diagram...etc" branches of an example model to reassure yourself that it's all there to be found, and to be manipulated by your scripts, if only you knew how to navigate to the data you want.
Given all the build-up above, you might have expected that objects that ModelRight exposes to your script would work something like:
'--- NOT how it works! --- |
But this is not how ModelRight works. ModelRight has instead implemented the model using multi-purpose "MRObjects" from which to assemble a tree, and on which to hang names and database properties. An MRObject could hold data for a table, or for a column, or an index etc. In short it's a general purpose object that holds a collection of properties and a collection of children (themselves MRObjects), and can itself be a child of some other MRObject.
This allows ModelRight to make the tree adapt to different vendors' databases (and versions thereof) -- where different database may have different complements of features and properties. This adaptability is defined by ModelRight in a separate structure called the MetaModel, which guides what ModelRight may or may not assemble into an MRModel for a particular database, and helps define what operations it can perform.
In more detail, most of the database model structure is built from five (VBS) classes of objects:
MRObject | Contains properties and collections of child MRObjects. Used for Table, View, Index, Column etc |
MRObjectCollection | Attached to an MRObject, this Implements the collections of child MRObjects. So an MRObject holding Table data, would have an attached MRObjectCollection which contains MRObjects describing columns, indexes etc. |
MRPropertyCollection | Attached to an MRObject, this contains the set of properties of whatever it is that the MRObject represents |
MRProperty | Contains a single property -- ie: a single member item in an MRPropertyCollection |
MRPropertyValue | Within an MRProperty, the value itself is contained within an MRPropertyValue object. (A property could be a vector, in which case MRVectorPropertyValue gets involved) |
In practice, the Collections objects aren't accessed directly by your script statements, as MRObject includes methods to retrieve child MRObjects and MRProperties from the collections it owns.
Combining the above-described classes with a couple of additional classes gives us the following sketch of a typical database model, as contained in ModelRight objects:
Things to note:
Set MyColumn = MyTable.AllChildren(1) |
Retrieves a single child, but it might be a column, an index, or possibly something else |
Set MyColumns = MyTable.Children("Column") For each AColumn in MyColumns ' do something with AColumn Next |
First retrieves a collection of children of type "Column", then iterates through them. |
S = Column.Property("Datatype").AsString | Obtains string telling the value of this property. The Datatype property reports the datatype of the column to which it's attached. |
To consolidate this knowledge, here is a sample procedure which navigates a ModelRight model, picking out some basic info:
Sub Evaluate_OnLoad |
The lines involving Context and Model are often-used preliminary steps to gain initial access to the ModelRight programming interface. For more details on this see the ScriptContext and MRModel sections in the ModelRight Objects Reference
Now that the concept of multi-purpose MRObjects has been covered, we can see how to use the MRXxx object model to navigate the database model, but since MRObjects are so general all we know so far is that MRObjects have generic properties and generic children, and we don't know precisely what properties and what kind of children.
For that purpose we need to return to the ModelRight environment and its browsers/explorers.
If you open the Model Browser (Model > Model Browser) for a typical database, you can readily follow the tree to familiar looking features and attributes, but how do these relate to the objects that your script has to work with? The basic relationships are as follows:
Item in Model Browser: | What it is | Example of how to access it |
Has no icon. Example: Model Character Set : latin1 |
MRProperty |
Model.AsObject.Property("Character Set").AsString |
Has icon; If it has children it shows an expand box. Example: Model Table Table: MyTable (418) |
MRObject (In this case, for a table) |
Set Tables = Model.AsObject.Children("Table") |
What if you want to know the structure of parts of the ModelRight model that you don't happen to have an example for, and so can't view in Model Browser? This is one of the purposes of the MetaModel Browser (Tools > MetaModel Browser). In addition, in MetaModel Browser the tree of objects and properties is captioned in a manner somewhat closer to what you need to know as a programmer.
One minor issue with the MetaModel Browser is that both it and the Model Browser pop up in modal dialogs, so you can only open one of them at a time, when it would be useful to compare them side-by-side. You can work around this problem by opening a second instance of ModelRight (File > New) and opening MetaModel Browser from there.
Here are some examples comparing what you see in Model Browser vs the same branch of the tree in MetaModel Browser.
Model Browser | MetaModel Browser |
Model Character Set 1: latin1 |
Model Properties Character Set Name: Character Set Definition: a set of symbols... Property Class: 8 MySQL 5.0: True |
Model Table Table: MyTable (418) [...properties...] Column Column: MyKey (420) [...properties...] Check Constraint |
Model Children Table [...metamodel items...] Properties [...many...] Name Children Column [...metamodel items] Properties [...many...] Datatype [...details...] Name [...etc...] Children Check Constraint [...etc...] |
The MetaModel browser makes the Children and Properties collections explicit. Like the Model Explorer and Model Browser, it shares the idiosyncrasy of captioning collections with singular nouns rather than plural.
Suggestion for amplifying the value of the MetaModel Browser and Model Browser:
It would be a great help to script programmers if the Browsers could provide an "example code" feature:
When clicking on an item in the browser tree, have the browser show a sample statement to access that item, perhaps in a text box at the bottom of the browser dialog.
When clicking on a column in Model
Browser, that aid would show something like:
Model.AsObject.AllChildren("MyTable").AllChildren("MyColumn")
When clicking on the corresponding node in MetaModel browser, a similar statement would appear, but with fake names filled in.
If the user clicks on a collection, perhaps the example code would show how to set up a For Each for this collection.
Below are a number of additional topics, many of which touched on in ModelRight Evangelist's basic tutorial of 2008-07-09
How are IDs useful?
Local, vs inherited, vs calculated
This is an issue that non-programmer power-users also work with within the UI, so presumably it's discussed somewhere on that level? At any rate, probably does need a programmer-perspective discussion too.
modelright evangelist wrote:
Any object and any property have a type. For instance, we have objects of type “Table”, “View” or “Column”; we have properties of type “Name”, “Datatype”, etc.
I agree on MRObject, however I don't see how MRProperty has a "type" of Name, Datatype.
Looks to me like MRProperty has:
type: integer
Name: String
Required
Note: As of 2008-07-11, in a number of cases I'm just guessing on the descriptions.
Classes are described in approximate order of top-to-bottom of the overall model tree, starting with the two possible gateways to the ModelRight data: ScriptFramework and ScriptContext.
Category | Member | Args | Return Type |
Description |
Utility | ||||
Initialize | In automation clients, must be called right after obtaining a ScriptFramework object. | |||
Evaluate | Property as String | String | Calculate a property formula? | |
MetaModel | ||||
MetaModel | IDispatch | |||
Model | ||||
LoadModel | ModelPath as String | IDispatch | Load model from file (xxx\xxx.wer) | |
SaveModel | ModelPath as String, Model as IDispatch |
Save model to file | ||
Model | ModelId as String | IDispatch | ||
ActiveModels | IUnknown | |||
CreateModel | IDispatch | |||
CurrentModel | IDispatch | |||
UI selection? | ||||
CurrentSelection | IUnknown | |||
CurrentDrawableSelection | IUnknown | |||
(MR?)Object | ||||
CreateObject | type as String, Owner as IDispatch |
IDispatch | ||
DeleteObject | Object as IDispatch | Boolean | ||
CreateObjectById | type as Integer, Owner as IDispatch |
IDispatch | ||
Property | ||||
CreatePropertyValue | ObjType as String, PropType as String |
IDispatch | Use MetaModel to create a MRPropertyValue of the appropriate datatype. | |
CreatePropertyValueById | ObjType as Integer, PropType as Integer |
IDispatch |
Category | Member | Args | Return Type |
Description |
Gateway to other objects | ||||
Object | IDispatch | For ModelRight's database-manipulation scripts,
ScriptContext.Object returns the "target" object, that is the object that ModelRight wants the
script to operate upon. For example, for the Column/Alter Script, ScriptContext.Object
provides an MRObject of type Column, that pertains to a particular column. For User Defined scripts, ScriptContext.Object provides an MRObject pertaining to the script itself. In either case, the returned MRObject can be used to get to the Model, etc. |
||
ScriptDocument | IDispatch | Returns a ScriptDocument object to which to write | ||
Options | IDispatch | Returns an MRObject, I think mostly as a convenient container for Properties, used by ModelRight to pass options to the script. | ||
Database | ||||
CurrentDatabase | String | |||
PrevDatabase | String | |||
Model Subset | ||||
SelectedModelSubset | IDispatch | |||
HasSelectedModelSubset | Boolean | |||
ContainedInSelectedModelSubset | Obj as IDispatch | Boolean | ||
Migration | ||||
MigratingRelation | IDispatch | |||
MigratingColumn | IDispatch |
Category | Member | Args | Return Type |
Description |
Write | Val as String | Writes a string to the script's "output document". Does NOT add a final line break. If linebreak is needed add it to the end of the string -- in VB using "& vbCRLF". | ||
WriteLine | Val as String | Conspicuously missing procedure to write a string to the script's output document, including a terminating linebreak. |
Note that an MRModel also includes the features of an MRObject. See the MRModel.AsObject method.
Category | Member | Args | Return Type |
Description |
Identity, Structure | Name | String | ||
Id | String | ??? | ||
AsObject | IDispatch | An MRModel object also includes the features of an MRModel (properties and children etc). The AsObject member gives access to that part of MRModel. | ||
MetaModel | IDispatch | Returns a reference to a (the?) MRMetaModel that describes the allowable structure of a ModelRight database model. | ||
Action management | BeginTransaction | pVal as String | ||
EndTransaction | ||||
RollbackTransaction | ||||
Undo | ||||
Redo |
Category | Member | (Args) | Return Type |
Description |
Identity, Type | ||||
Id | Long | |||
Name | String | |||
type | Long | Type within the ModelRight world: Table, View, Column etc. | ||
TypeName | String | |||
SetDatatype | Datatype as String | |||
IsValid | Boolean | When is an MRObject invalid? | ||
Equals | Obj: IDispatch | Boolean | Does this calculate object identity or value equality? | |
Children-related | ||||
Children | type as String | IUnknown | Returns MRObjectCollection of Children that match type Type | |
AllChildren | IUnknown | Returns MRObjectCollection of all children? | ||
OriginalChildren | type as String | IUnknown | Don't know | |
ChildrenById | type as Long | IUnknown | Presumably returns an MRObjectCollection of child objects, matching a type number. | |
ChildByOrder | type as String, Order: Long |
IDispatch | Access children of a particular type, in order. Returns single child object? | |
ChildOrder | Child as IDispatch | Long | Tells order number of a child object within the children collection of the current object | |
ModifiedChildrenPosition | IUnknown | Don't know | ||
Properties-related | ||||
Property | type as String | IDispatch | Return the Property named by string type | |
Properties | - | IUnknown | returns collection of all properties on this MRObject | |
PropertyById | type as Long | IDispatch | Return the property selected by integer type | |
OriginalProperty | type as String | IDispatch | Don't know | |
ModifiedProperties | IUnknown | Don't know | ||
HasProperty | PropType as WideString | Boolean | Tell whether property exists | |
HasPropertyById | PropType as Long | Boolean | Tell whether property exists | |
SetProperty | type as String, Prop: IDispatch) |
Boolean | Not sure | |
SetPropertyById | type as Long; const Prop as IDispatch |
Boolean | ||
DeleteProperty | type as String | Boolean | ||
DeletePropertyById | type as Long | Boolean | ||
Inheritance | ||||
InheritsFrom | IUnknown | |||
HasLocalProperty | PropType as string | Boolean | ||
HasLocalPropertyById | PropType as Long | Boolean | ||
Ancestors-related | ||||
Owner | IDispatch | |||
OldOwner | IDispatch | |||
Model | IDispatch | |||
SQL-related | ||||
CreateStatement | String | |||
AlterStatement | String | |||
DropStatement | String | |||
Misc | Evaluate | Property as string | String |
In general it's not necessary to interact with an MRObjectCollection directly, as the MRObject that owns it provides methods for accessing the collection's items.
Category | Member | Args | Return Type |
Description |
All | Item | Index as Long | Variant | Returns an item. This is the default property of this object, so
that code for accessing collections: MyTable.AllChildren.Item(3) can be compressed to: MyTable.AllChildren(3) |
Count | Long | |||
_NewEnum | IUnknown | Used by VB "for each... in..." statements |
Category | Member | Args | Return Type |
Description |
All | type | Long | Is this the data type of the property, or a code for the property name? | |
Name | String | |||
Value | IDispatch |
In general it's not necessary to interact with an MRPropertyCollection directly, as the MRObject that owns it provides methods for accessing the collection's items.
Category | Member | Args | Return Type |
Description |
All | Item | Index as Long | Variant | Returns an MRProperty object (default member of MRPropertyCollection) |
Count | Long | |||
_NewEnum | IUnknown | Used by VB "for each... in..." statements |
Category | Member | Args | Return Type |
Description |
Set | FromString | Val as String | ||
FromBoolean | Val as Boolean | |||
FromInteger | Val as Long | |||
FromDouble | Val as Double | |||
FromObject | Val as IDispatch | |||
Get | AsString | String | ||
AsBoolean | Boolean | |||
AsInteger | Long | |||
AsDouble | Double | |||
AsObject | IDispatch | |||
AsVector | IUnknown | |||
Test | IsStringProperty | Boolean | ||
IsBooleanProperty | Boolean | |||
IsIntegerProperty | Boolean | |||
IsDoubleProperty | Boolean | |||
IsObjectProperty | Boolean | |||
IsVectorProperty | Boolean | |||
IsNull | Boolean |
Category | Member | Args | Return Type |
Description |
All | Item | Index as long | Variant | Returns an MRPropertyValue? (Default property of this VBS object) |
Count | Long | |||
_NewEnum | IUnknown | Used by VB "for each... in.." statements | ||
AddValue | Val: IDispatch |
Active Call Center programmability: Integrating COM Applications