Until now, we have used our classes’ definitions just to hold data, but a complete object model not only has data but behavior.
You can enrich your object model by adding Methods and Events to your classes. You are free to add all the methods your classes need, because FoxDataObjects does not imposes any restriction to your business classes definitions, and you do not need to inherit your business classes from over-charged and over-engineered classes as other frameworks impose.
As a basic example, in the Category class we have a PictureBitMap property that holds a picture for the category instance in binary string format. We can add a couple of methods to the Category class that allows loading and saving such picture bitmap from/to a disk file. This way, a Category instance can work with BMP files, no matter if it is instantiated by a Visual FoxPro program, an ASP web page, a VB or .Net program, etc.
Let us add the LoadPic and SavePic methods to the Category class:
· Clear your objects instances by typing this in the Command window:
CLEAR ALL
· Open your Classes.PRG file to edit:
MODIFY COMMAND classes
· Edit the Category class definition. It should end in something like:
DEFINE CLASS Category AS Custom
CategoryName = ""
Description = ""
PictureBitMap = ""
PROCEDURE LoadPic (tcFileName as String)
IF VARTYPE(tcFileName)="C" AND FILE(tcFileName)
This.PictureBitMap = FILETOSTR(tcFileName)
ENDIF
ENDPROC
PROCEDURE SavePic (tcFileName as String)
IF VARTYPE(tcFileName)="C"
STRTOFILE(This.PictureBitMap,tcFileName)
ENDIF
ENDPROC
ENDDEFINE
This is just basic code. Best practices suggest that we should control and protect file operations within a Try/Catch block and return a logical value indicating whether the file was created or read successfully.
Your business objects will be plenty of methods encapsulating all the behavior your real life entities expose.
As with any other class, you can expose those methods by leaving them PUBLIC (as the sample) or, if they will be used only within your class or inherited class methods you can protect or hide them by adding the HIDDEN or PROTECTED keyword to the Method declaration (see DEFINE CLASS in the Visual FoxPro language reference).
You can freely use Assign_ and Access_ methods for your object instances to catch and react to changes on special properties, map stored values to a different subset or domain, etc. However, we will see such methods implemented in more advanced tutorials.
When your applications use Persistent Services to save, retrieve or remove objects instances from the database, FoxDataObjects generates a rich set of events that can fire methods on your classes’ definitions.
It opens a new world of possibilities to your business objects.
Persistence Events give you the perfect space to implement Business Rules, whenever your objects are retrieved, removed or saved to the database.
In order for your objects instances to react to a persistence event, you just need to define a method in the class definition for the event you want to implement.
As an example, when an object instance is going to be saved to the database, FoxDataObjects generates an OnSave event. If your class has a method named OnSave, it is fired automatically by FoxDataObjects.
You could use the OnSave event on your class definition to execute validation code to enforce Business Rules, prepare or change data to be saved, update, remove other objects, send/retrieve data directly from the data store, etc.
When FoxDataObjects executes a Method in your class for a given Persistence Event, it passes one or more parameters to the Class method depending on the event fired.
The first parameter passed is a pointer to the Session object involved in the persistence event. This way you can access all the persistence services within your Method code.
Most events are conditional; it means that if the method implementing the event returns a False value, the whole operation is cancelled and the active transaction is rolled back.
Please check the Persistence Events section on the API Reference guide for detailed information.
Let us try some basic usage for Persistence Events. We will use the OnSave method on the Product class to check some conditions and impose a minimal set of business rules.
· Edit the Product class definition. It should end in something like:
DEFINE CLASS Product AS Custom
Code = 0
ProductName = ""
EnglishName = ""
Category = .Null.
QuantityInUnits = 0
UnitPrice = 0.0000
UnitCost = 0.0000
UnitsInStock = 0
UnitsOnOrder = 0
ReorderLevel = 0
Discontinued = .F.
PROCEDURE OnSave(oSession as Object)
IF EMPTY(This.Code)
oSession.Errors.Set("Product Code cannot be empty.")
RETURN .F.
ENDIF
IF This.UnitsInStock < This.ReorderLevel
This.AddToReorderList()
ENDIF
ENDPROC
HIDDEN PROCEDURE AddToReorderList
ENDPROC
ENDDEFINE
This is just basic code. We have added a method named OnSave that is fired by the Persistence Engine when the instance is going to be saved (Inserted/Updated) into the database. As any other persistence event, the method receives as a parameter a pointer to the current Session object, that we can use to access the whole set of functions and services exposed by this object.
In the example, we check for a not empty value present in the Code property. Otherwise, we use the session’s Errors structure to log a message and then return a False value. If the OnSave event returns a False value, the complete SaveObject persistence service is canceled and the underlying database transaction is rolled back.
We also test to know if the Product stock has dropped below the reorder value. In such case, we fire an internal method (hidden) that adds the product to a list of products to be re-ordered (we do not include that code her just to keep this basic sample clear).
Let us test our classes!
· Close your Classes.PRG saving your changes
And try to save an empty Product instance:
|
Command |
Result |
|
SET PROCEDURE TO FDO, Classes ADDITIVE
|
|
|
oServer=CREATEOBJECT(“fdoServer”)
|
|
|
oSession=oServer.NewSession(“Tastrade.FDO”)
|
|
|
oPrd=CREATEOBJECT(“product”)
|
|
|
oPrd.ProductName=”Tropical Soda”
|
|
|
? oSession.SaveObject(oPrd)
|
.F. |
|
? oSession.Errors.ToString()
|
Product Code cannot be empty.
|
|
oPrd.Code=2
|
|
|
? oSession.SaveObject(oPrd)
|
.T. |
When we tried to save the Product instance, FoxDataObjects fired the OnSave method on the product class and got a false value as return value. Therefore, it canceled the operation and rolled back the transaction.
We used the Errors collection object to check what happened and we found our custom message logged. See the Errors collection section on the API Reference guide for a complete description on how you can use this powerful technique as the central communication mechanism among your application layers.
| Home |
Send feedback on this topic to RunAhead Technologies
For Technical support and product issues please contact us at support@foxdataobjects.com or visit http://www.foxdataobjects.com
Copyright (c) 2000-2005 RunAhead Technologies