Beruflich Dokumente
Kultur Dokumente
The following tutorials have been created in order to provide Borland Delphi 8 Architect customers with a step-by-step approach to how Borland Enterprise Core
Objects (ECO) works. The tutorials, beginning with Tutorial 1, should ease customers into the concepts of ECO and model-driven applications and advance to more
complicated features and capabilities. Please check back for more tutorial updates to this page.
Tutorial 1: Creating your first ECO application
Tutorial 2: Working with Associations
Tutorial 3: Using Borland Enterprise Core Objects (ECO) with Databases
Tutorial 1
Creating your first ECO application
By Anthony Richardson
Abstract - This article demonstrates building a simple application using the Borland Enterprise Core Objects (ECO) technology in Borland Delphi
Architect.
Introduction
What is Modeling?
The Example Application
Building the Application
CoreClassesUnit.pas
ContactManagerEcoSpace.pas
Building an ECO Model
Creating the Model
Adding a User Interface
Running the Application
Persisting the data
Auto Forms
Conclusion
About the Author
We are going to implement the following Unified Modeling Language model for our address book application:
If you are not familiar with UML, the diagram shows three classes, represented by the rectangles. The class 'Contact' although not marked in the above diagram, is
an abstract class. The lines with the arrowheads indicate that both the 'Company' and 'person' classes descend from the 'Contact' class. The model shows the
following business rules:
For this example I could have implemented the model using one class and a simple Boolean flag to indicate if the contact was a person. I chose the above model
because in the next tutorial we will extend the model and I also wanted this tutorial to demonstrate the basic generalization relationship.
Building the Application
Borland Delphi creates a bare ECO application. The "Project Manager" (View|Project Manager) shows the automatically generated files. There are two files that are
different from a normal Delphi Application. These are ContactManagerEcoSpace.pas and CoreClassesUnit.pas.
CoreClassesUnit.pas
This file contains an empty "UML package". This is where the classes for our ECO application will be created by default. It is possible to create multiple packages,
however, this tutorial will only use the default package.
ContactManagerEcoSpace.pas
This contains the "EcoSpace" for your application. An EcoSpace contains the objects of the application at runtime. It provides the link between the objects and the
persistence mechanism (XML or RDBMS). The EcoSpace can be queried using OCL to retrieve objects from the EcoSpace.
From the Borland Delphi help file:
"An ECO Space is a container of objects. At runtime, the ECO Space contains actual instances of the classes in your model. It is helpful to
think of the ECO Space as an actual instance of a model, much like an object is an instance of a class. The objects contained in the ECO
Space retain the domain properties (attributes and operations) and relationships defined in the model.
As a container of objects, an ECO Space is both a cache, and a transactional context. Within the ECO Space, another component called a
persistence mapper is used as the conduit between the persistence layer (either an RDBMS or XML file), and the instances of the classes
defined in the model. When you configure an ECO Space using the IDE, you will select those UML packages in your model that you wish to
persist in the ECO Space."
Building an ECO Model
ECO uses a subset of the Unified Modeling Language (UML) as its modeling language. The specific parts are the Class Diagram and Object Constraint Language
(OCL).
Creating the Model
From the "Model View" double-click the "CoreClasses" diagram item. This will open the modeling surface editor. When this opens, the "Tool palette" changes to
contain the tools for generating our model.
Begin by creating a new class. You can do this one of three ways.
1.
2.
3.
Select the "Class" tool in the "Tool palette" and click the modeling surface.
Right click the modeling surface and select Add|Class from the pop-up menu.
Right click the modeling surface and select "Add New Item..." from the pop-up menu. Select the Class Template and press OK.
Once the class is created, ensure it is selected so the properties of the class are visible in the "Object Inspector" Select the name property to "Contact" and the
Abstract property to "True" Borland Delphi 8 will create a corresponding Delphi abstract class called Contact.
Right-click the class and select "Add|Attribute". Select the attribute in the class with the mouse and the "Object Inspector" will display the properties for the attribute.
Set this attribute's name to "fullName" and type to "String".
Add the string attribute's address and phone to the Contact class. Add the class' person and Company to the model. Leave the abstract property false as these will
be the concrete classes we create instances of.
We now need to create a generalization relationship between the person class and the Contact class. A generalization relationship specifies that person is a type of
Contact, and inherits all the attributes and operations of Contact.
To create this relationship select the "Generalization/Implementation" tool and select the person class and drag to the Contact class.
Create the same generalization relationship for the Company Class. The completed model is below.
It is interesting to take a look at the "Model View" now that we have added some classes. The Model view shows the two models in the ECO Application. The
Domain Model is the model we have just created, the Implementation Model is auto generated by ECO. The Implementation Model is created as Delphi classes.
The code generated by ECO shows the large amount of implementation detail that ECO takes care of for the developer. This code allows for managing lists of each
domain class, enumerator classes, adapter and all the management code for maintaining the objects within a running application. Normally, the developer is
responsible for this code and it quickly turns a readable model from being a clear representation of the problem, into a mass of complex structures and architectural
elements unrelated to the real problem.
Adding a User Interface
Now that a model has been created, it is necessary to add a simple GUI to explore the runtime behaviour of ECO.
NOTE: At this point in the exercise it is important that you compile your application.
ECO needs to have the model compiled so the GUI designer can display the model information. The runtime system of ECO relies on the interrogation of the classes
using .Net Reflection. For Reflection to work you must compile the application.
Using the "Tool palette" add the follow controls to the WinForm.pas form.
Type: Button
Name: btnAddPerson
Type: Button
Name: btnAddCompany
Type: ExpressionHandle
Name: ehContacts
Root: rhRoot
Expression: Contact.allinstances
Type: ExpressionHandle
Name: ehPersons
Root: rhRoot
Expression: person.allinstances
Type: ExpressionHandle
Name: ehCompanies
Root: rhRoot
Expression: Company.allinstances
Type: DataGrid
Name: dgContacts
DataSource: ehContacts
Type: DataGrid
Name: dgPersons
DataSource: ehpersons
Type: DataGrid
Name: dgCompanies
DataSource: ehCompany
Enter the following code for the buttons OnClick event code:
EcoSpaceType = ContactManagerEcoSpace.TContactManagerEcoSpace
The ExpressionHandle is linked to another handle, either an ExpressionHandle or a Referencehandle, and evaluates an OCL expression to provide a resulting
object, collection or value. It is possible to daisy chain ExpressionHandles where each handle evaluates its result from the results of the previous handle.
Running the Application
Run the application and create some persons and Companies.
The grid set to show "person.allinstances" shows only the persons created. The grid set to show "Company.allinstances" show only the Companies created. The grid
set to show "Contact.allinstances" shows both the Persons and the Companies.
Persisting the Data
An application like in this example would generally be required to save the entered data between each invocation. With ECO there are two basic persistence
methods supported. Saving to a XML file or saving to a RDBMS. For this example an XML file will be used, as it's simpler to configure.
From the "project Manager" double click ContactManagerEcoSpace.pas to call up its Design Editor. From the "Tool palette" select the 'persistenceMapperXML" tool
and drop it on the designer. Set the following properties:
Type: persistenceMapperXML1
FileName: data.xml
Type: EcoSpace
persistenceMapper: persistenceMapperXML1
On the WinForm.pas form add a new button. Name it "btnSave" and set the text to "Save". Add the following code to btnSave OnClick event handler.
Auto Forms
ECO supports the automatic generation of forms for editing your ECO Objects at runtime. The default ECO enabled Win Form includes a ECOAutoForms
component. This component extends the data aware controls on your form to support double-click activation of the objects auto form. Set the auto form property of
all three grids to true.
At runtime you can activate the auto form by double clicking the fixed column of any grid.
Conclusion
ECO offers a way to develop applications where the developer spends more time coding the business logic and less time on architectural coding. This allows for the
code to be clearer and reduces the cost of change. ECO provides a distinct separation between presentation, logic, and persistence. This reduces the effect
changes have on an application.
Tutorial 2
Working with Associations
By Anthony Richardson
Abstract - This article demonstrates accessing Borland Enterprise Core Objects (ECO) objects in code via handles, implementing relationships and
using master/detail style user interfaces.
Introduction
Using the CurrencyManagerHandle
Adding a relationship
The relationship at work
Conclusion
About the Author
Introduction
In Tutorial 1 (Borland Delphi Architect ECO Tutorial 1), a simple contact manager application was described and constructed. That application will form the
basis for this tutorial.
Using the CurrencyManagerHandle
In Tutorial 1, ExpressionHandles were used to provide lists of the core objects in the ECOSpace. These lists were then connected to grids to display the
information. When it is necessary to determine which item in a list a user has selected, in an ECO application, you use a CurrencyManagerHandle.
The CurrencyManagerHandle is a component in the Enterprise Core Objects section of the toolbox. It will track the current selected item in a list. On the Win Form of
the application from tutorial 1, place a CurrencyManagerHandle and another button as per the following figure.
Type: Button
Name: btnDeleteContact
Text: Delete
Type: CurrencyManagerHandle
Name: cmhContact
RootHandle: ehContact
BindingContext: dgContact
In the Click event handler for the Delete button place the following code
end;
The conditional IF statement accesses the Element property of the CurrencyManagerHandle. The CurrencyManagerHandle.Element property is a reference to the
currently selected item in the list. The result is an IElement.
The IElement interface is the lowest common denominator for all possible objects that can be referenced by a handle. All ECO objects, collections, and attributes
must implement IElement. The AsObject method of IElement returns the Object that is currently implementing the IElement instance.
In the above example, the Object's class is being compared to the class Contact. This is done using the IS operator to ensure the CurrencyManagerHandle is
referring to an object of the correct type.
SelectedContact := Contact(cmhContact.Element.AsObject);
The remaining code is using a local variable of type Contact to hold a reference to the object referred to by cmbContact.Element.AsObject. The object is being cast
as Contact because the previous IF statement ensures only objects of class Contact will be executed by this code.
SelectedContact.AsIObject.Delete;
All classes generated by ECO implement the IObject interface. IObject gives you access to manipulate the object's state in the ECO Space. Specifically, the above
code calls the Delete method to remove the object from the ECO Space. Why not use the Destroy or Dispose methods? Simply because by calling Delete, the object
has all its relationships deleted and is removed from the list of normally accessible objects. However, the object has to stay in memory until the next save operation,
so ECO knows to remove it from the database. Unlike normal objects, which only have the one state (created), ECO objects need to maintain their state while in
memory and while they exist in the database.
NOTE: Never call Destroy or Dispose on an ECO-controlled object; always use Delete to remove objects you no longer require.
Adding a relationship
Up until now the application has been very basic. However, one of the most important aspects of ECO is its ability to manage relationships between objects. Using
the model surface, draw an association from Person to Company.
Type: Association
Name: Employment
End1.Multiplicity: 0..*
End1.Name: employees
End2.Name: employer
This association allows our contact manager application to record the relationship of a company and its employees. This simple process of drawing the association
has resulted in ECO generating all the code to manage the relationship from both ends. When you assign a company as an employer for a person, ECO will ensure
that the Person is automatically added to the employee collection for that company. Stop and think about this. ECO now handles all referential integrity aspects of
your application's relationships, both in memory and in the database. A fair amount of architectural coding of a non-ECO application has just been eliminated. The
intelligent runtime systems in ECO will handle relationship navigation, caching, retrieval, and persistence.
Multiplicity is the measure of the number of 'connections' at the association end-point. There are four basic options:
0..1:
1..1:
0..*:
1..*:
NOTE: At this point in the exercise it is important that you compile your application.
Add a CurrencyManagerHandle for tracking the selected company.
Type: CurrencyManagerHandle
Name: cmhCompany
RootHandle: ehCompany
BindingContext: dgCompany
Now modify the OCL expression and root handle used for defining the person list.
Type: ExpressionHandle
Name: ehPerson
RootHandle: cmhCompany
Expression: self.employees
Changing the root handle for ehPerson to reference the new cmhCompany handle causes the handle to evaluate its expression against the currently selected
Company. This is called chaining expressions. Each expression evaluates against the result of the previous expression. This is an extremely powerful capability of
ECO. The expression "employees" causes the ehPerson handle to now contain a list of all employees of the selected Company. The ECO runtime system
automatically handles retrieving the objects from the database without developers needing to concern themselves with how the data is retrieved. This simplifies the
code dramatically for complex applications.
Before running the application, we need a way to create the association between a Person and a Company at runtime. To do this, change the Click event for the
btnPerson button to the following code:
NewEmployee.employer := SelectedCompany;
The process of using relationships in ECO is as simple as assigning the Company object to the employer relationship of the Person object. "So what? That's how
Delphi handles all class relationships," you may say. Except in ECO, the relationship is bi-directional. Not only has the Company been assigned to the
Person.employer role, the Person has been added to the Company.Employees role. This relationship is enforced and maintained by the ECO runtime system.
Alternatively, the following code could have been used. The result is just the same, however the association is completed in code from the other end. The highlighted
line shows the changed code.
Tutorial 3
Using Borland Enterprise Core Objects (ECO) with Databases
By Anthony Richardson
Abstract - This article demonstrates connecting a Borland Enterprise Core Objects (ECO) application to a Relational Database Management System
(RDBMS) and managing changes to the object space.
Introduction
Using ECO with databases
Modifying the application to use Microsoft SQL Server 2000
Creating a blank database
Improving the database application
Working with transactions
Implementing Object Space transactions
Separating the Object Space and Database Transactions
Analyzing the SQL created by ECO
Conclusion
Delete the persistenceMapperXML component and place a persistenceMapperSqlServer component, from the Enterprise Core Component category, on the design
surface. Add a sqlConnection component from the Data Controls category.
Type: persistenceMapperSqlServer
Name: persistenceMapperSqlServer1
Connection: sqlConnection1
Type: sqlConnection
Name: sqlConnection1
ConnectionString: Initial Catalog=ContactManager;Data Source=localhost;Integrated
Security=SSPI;
Type: ContactManagerEcoSpace
PersistenceMapper: persistenceMapperSqlServer1
NOTE: Don't forget to set the ECO Spaces PersistenceMapper property to the new persistence mapper component. Access this by clicking in the blank area of the
design surface.
This completes the required programming changes necessary to change the application from using a XML file to using Microsoft SQL Server.
The connection string "Initial
Catalog=ContactManager;Data Source=localhost;Integrated
Security=SSPI;" instructs ECO to connect to the database "ContactManager" on Microsoft SQL Server running on the local machine. However, because
the database "ContactManager" does not exist, it must be created.
Creating a blank database
ECO can generate the database schema for the application, but it requires a blank database to which to connect. Using SQL Server Enterprise Manager, connect to
your local server, and create a new database called "ContactManager". (The tutorial assumes you are running Microsoft SQL Server on the same machine as
Delphi. If this is not the case, you must modify the connection string as appropriate).
On the bottom left corner of the design surface for the ECO Space, there is a Create Database button. Click this button. A dialog box appears showing a list of tables
that must be deleted and/or recreated. This list will be empty for the first schema generation. Click OK to proceed with the schema generation. If the database
contains any data, that data will be lost.
Use the in-place menu editor to add the following menu items:
Type: System.Windows.Forms.MenuItem
Name: mnuDatabase
Text: Database
Type: System.Windows.Forms.MenuItem
Name: mnuSaveChanges
Text: Save Changes
Type: System.Windows.Forms.MenuItem
Name: mnuCancelChanges
Text: Cancel Changes
Type: System.Windows.Forms.MenuItem
Name: mnuDivider1
Text: Type: System.Windows.Forms.MenuItem
Name: mnuGenerateSchema
Text: Generate Schema
Create the following "Click" events for each menu item.
The menuCancelChanges_Click() event adds support to discard changes that have occurred in the object space. It is possible in ECO to create multiple check
points for undo operations and the above method ensures that all changes since the last database update are undone. Undo will be covered in more detail in future
articles (although it is not difficult to work out by looking at the IUndoService interface).
It is also possible to generate the database schema using code. Add the following method to the ContactManagerEcoSpace class in EcoSpace.cs
procedure TContactManagerEcoSpace.GenerateSchema;
begin
self.persistenceMapperSqlServer1.CreateDataBaseSchema;
end;
Enter the following code in the click event handler for the "Generate Schema" menu item.
IUndoService.StartTransaction;
IUndoService.CommitTransaction;
IUndoService.RollbackTransaction;
If you are familiar with how database transactions work, then the operation of the above methods will be obvious. Use StartTransaction; to begin a
transaction. ECO specifically tracks all changes made to the object space from the start of transaction until a transaction is completed. A transaction can be
completed in one of two ways. By calling CommitTransaction; all the tracked changes will be applied to the object space as a single undoable event. If
RollbackTransaction; is called, then all the changes since the start of the transaction will be lost, and the object space reverts to how it was before
the transaction started.
It is important to use transactions in a way consistent with how Borland designed them. Transactions are designed to be short lived and should be used in a manner
similar to the following code.
EcoSpace.UndoService.StartTransaction;
try
// Code that does stuff here
EcoSpace.UndoService.CommitTransaction;
except
EcoSpace.UndoService.RollbackTransaction;
raise;
end;
ECO will raise an exception if you call commit or rollback without a transaction active.
The transaction API specifically does not include a method to check whether a transaction is active or not. This is by design; the state of a transaction should be
implicit in the code. The above example is like that; keeping the transaction methods calls together in an exception-handling framework implicitly controls the
transaction state.
In the previous paragraphs the ground rules for using transactions were laid out. Keep them short-lived and keep the state implicit by the design of the code. To keep
them short-lived means that the duration of the transaction must be relatively discernable from the code. To achieve both goals generally requires ensuring that no
user interactions occur while a transaction is active. Although ECO can handle user interactions during an active transaction, there are reasons to avoid them.
When a transaction is active, the undo mechanism of ECO is not used. This is an all- or-nothing approach to object space changes. If you commit a transaction, all
changes are applied. Conversely, if you roll back a transaction, all changes within the transaction are lost. You cannot undo individual changes within a transaction.
In fact, when you make changes outside of a transaction, ECO creates a small transaction for each of those changes. The result is that when you do bulk object
space changes, many transactions are being started and committed. If the bulk update is placed within a single transaction, these smaller transactions will not occur,
and ECO will perform changes to the object space much faster.
TIP: When performing bulk changes to the object space, use transaction to speed up the time it takes to apply the changes.
Separating the Object Space and database transactions
When you call "Commit Transaction", the transaction is committed to the ECO Space only. Calling "Cancel Changes" will rollback any changes that occurred in the
transaction, even if the transaction is committed.
At first this seems strange, but it is done for a reason. The concepts of the ECO Space and data storage are two separate concerns. Transactions concern
themselves purely with the object space and persistence operations concern themselves only with the database. The separation is necessary because transactions
work on all ECO objects, even transient objects. ECO applications have no specific requirement to even use a database.
Analyzing the SQL created by ECO
As mentioned, all update operations to the database are wrapped in database transactions. It is possible to monitor all operations the ECO application performs on
the database by using the SQL profiler application included with Microsoft SQL Server. The link to the Profiler application is in the Microsoft SQL Server folder of the
start menu.
The following screen shot shows a save of a new person object to the database. The analyzer application shows the transaction and SQL commands required to
perform the update.
Conclusion
The layered architecture of ECO makes it simple to change the storage mechanism used to persist data. The layered architecture also provides a separation of
concerns between changes made to the object space and changes made to the database data.
About the Author
Anthony Richardson is the director of Viewpoint (SA) Pty Ltd. Anthony has many years of experience working with Borland MDA technologies. Anthony's contact
details are available at http://www.viewpointsa.com.