Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Using The Data Environment

Status
Not open for further replies.

AndrewMozley

Programmer
Oct 15, 2005
621
1
18
GB
I have an application which in its main program opens a database MyData. While I am developing the application the database will perhaps be in my development folder, C:\Dev\Data. At run time it will be in whatever folder the user chooses to use, so perhaps E:\CompanyX\Datafiles. My application handles this fine.

At present, however I tend to open a particular table, say Customers, with an instruction like this in the Init() method :
USE Customers INDEX Customers ALIAS Cus IN 0
. . . and at the end I close the table with
SELECT Cus
USE
There are probably better ways, but this is the way that I know !

I feel that I should be using the Data Environment. To that end, during development, I can View the Data Environment, and can open the relevant file, c:\Dev\Data\Customers.dbf. This seems to work OK, and when I run the form I see that fields which I have dragged onto the form do indeed display the relevant values.

However, when I examine the .scx file (by browsing it as a table), I see that the record defining the cursor has its properties field containing :
Alias = “Customers”
CursorSource= “..\..\data\customers.dbf”​

Am I doing this right? When the application is installed on the customers site it will presumably try to find a data folder, and this will fail. How can I get round this?

A few further questions.
1. When I am developing the form and go the Data Environment and do Right-Click Add, I thought this might have offered the option of opening a table or a database container. However VFP only appears to offer the option of putting a single table into the environment. I don’t really mind in this case, because I am only concerned with one table, but I wonder whether there is the more general option of including a database within the data environment for a form.

2. When I exit from the form, is the table automatically closed? And if so, where is the code that does that.

3. Although I have a database open within my application, when I open a table, I always refer to it by its .dbf filename. Presumably VFP works out that the table is part of a database and opens that database if necessary. Is there a way of referring to the table by its own name within the database, if that name is different to the name of the .dbf file within the folder?

Sorry for the rather basic questions.

Andrew
 
However, when I examine the .scx file (by browsing it as a table), I see that the record defining the cursor has its properties field containing :
Alias = “Customers”
CursorSource= “..\..\data\customers.dbf”
Am I doing this right? When the application is installed on the customers site it will presumably try to find a data folder, and this will fail. How can I get round this?

Hello Andrew,

Yes, basically you are doing it right. The application will first look for ../../data. It won't find it, so it will look along the normal search path for your table, that is, it will look in the default directory, then the directory specified by SET PATH. Or, if the containing DBC is already open, it will use that to locate the table.

So provided your table is in that path (and assuming it is not in ../../data) VFP will find it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Yes, basically you are doing it right. The application will first look for ../../data. It won't find it, so it will look along the normal search path for your table, that is, it will look in the default directory, then the directory specified by SET PATH. Or, if the containing DBC is already open, it will use that to locate the table.

So provided your table is in that path (and assuming it is not in ../../data) VFP will find it.

Are you sure, Mike? My understanding is that references in the DE need to have the same relative path at runtime.

But since I always work that way, I've never tested any other structure, so maybe I've been wrong about this all this time.

Tamar
 
Are you sure, Mike? My understanding is that references in the DE need to have the same relative path at runtime.

Tamar, yes I am sure that what I said is correct. It's a principle I have always applied, and I just tested it to double-check.

That said, I was mistaken about the property we are discussing. I see now that Andrew was referring to the CursorSource property. As you know, the CursorSource would be the name of the actual table, and does not necessarily have a path (or has a relative path).

What I was thinking of was the Database property. That's where an (absolute) path might be specified. But what I said in my post would still apply.

So, assume the CursorSource is Customers, and the Database property is c:\dev\testdata\accounts.dbc. Now, move the application to a system that does not have a c:\dev\testdata directory. Put the accounts.dbc in, say, f:\livedata. Make sure f:\livedata is on the search path. My assertion is that VFP will correctly find the database, and therefore the table, even though the DE is pointing to the "wrong" path.

Andrew, if you are in any doubt about this, is should be easy enough to concoct a test.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Tamar, I can confirm this.

This also always has been a tip from wOOdy instead of doing using DE code from your book to change all cursorsource or database properties to the correct path.

It's also normal the SCX shows a relative path and the visual DE shows an absolute path, that way you can install to everywhere and just ensure your data is in the same relative position to the EXE, as it is to the SCX during development. Otherwise you would be locked to a location you might not be able to ensure at customers, especially for off the shelf or downloadable software. I just refer to system directories here, for the installation destination of your database and table files.

Also Mike, the controlsource can absolutely have a path in it (no matter if relative or absolute, no pun intended), when it references a free table.

Bye, Olaf.
 
MK. this goes into the direction of some hackers guide code. It's not needed, if you add a path to the dbc via SET PATH. The error "Cannot find ..<database>" mentioned in the KB article will only occur, if the database also can't be found along all paths in SET PATH. The code provided there will help with the faster finding of files, though, that's it's advantage.

Andrew, I always found it better to not use the DE because of several things there being suboptimal. For one thing it doesn't give you an overview of all the details. Of course you can see tables and views used, but you have to go into properties to see how they are ordered by indexes, how they are related (if at all, etc.) Also the SCX DE can't be reused. I'm not a big fan of drag&drop visual development, it has it's pros, but also cons. Using application frameworks they have data access classes not based on data environments. We now have the separate data environment class, but that was only introduced with VFP9, I think, maybe VFP8. Form classes had no data environments back then.

If you are like most developers developing in OOP, which has the better overall reusability and therefore productivity than procedural, you also rather not use visual drag&drop development. It can be a part of it, but I'd rather program some code or form generator code than a builder, which a) takes more time to create than generator code, as it has to have a UI and only partially helps me to develop faster using an assistant.

As OOP developer you prefer classes over anything else, so you'd not use SCX at all, not even for their slight advantage of easily starting them from the standard toolbar ! button. You can easily create a three line doform function starting a dorm class, and will have the same ease rhen. And there is no other advantage I see for scx forms, only more disadvantages. You can't subclass them, before you save them as class. You can only have formsets with forms, but that's not an advantage, because formsets are defect in themselves.

Now, that there is a dataenvironment class you could really make good use of that, but who will ever create a new VFP application framework based on the newest VFP stuff, if VFP is deprecated? Also the DE is not really good compared to one advantage you have with any non visual classes based on container: You can stack containers into each other, you can't do that with the DE class.

Bye, Olaf.
 
Olaf said:
We now have the separate data environment class, but that was only introduced with VFP9, I think, maybe VFP8. Form classes had no data environments back then.

This is incorrect. You've been able to subclass the DE (in code) all the way back to VFP3. The Codebook framework for VFP made use of it.

What's new in VFP9 is the ability to directly use your own DE subclasses without resorting to the weird trickery we had to do in earlier versions.
 
>You've been able to subclass the DE (in code) all the way back to VFP3.
How? As a nonvisual class? I know there is the nonvisual SESSION class, but that is a datasession, not a data environment.

Bye, Olaf.
 
Same way you subclass any other class:

DEFINE CLASS myDE As DataEnvironment

You can also subclass Cursor and Relation. Always have been able to. Session is the one they didn't expose initially.

We placed a "loader" as the only control on our base form class so it was the first thing instantiated. It released the native DE (if any) and called CreateObject() on the subclassed DE.
 
Thanks Dan,

so you indirectly answered my question with yes. DataEnvironment was one of the native base classes you couldn't pick from the visual class designer.

The help topic "Objects, Collections, and Classes" lists several Classes and Objects - and perhaps could be enhanced, as some obvious classes like "Custom" are listed as Object. I would assume Objects are things in place already, not able to subclass, like the Application object is. Anyway, Dataenvironment is listed there, too, I see. Thanks.

Bye, Olaf.
 
Right. I started with this product before it even *had* a visual class designer. In those heady days the only way to subclass was in code. I view the visual class designer as a limited wrapper around that. [pipe]
 
Mike, Tamar, Olaf, MK and all

Thank you for your replies. You have answered my question that, if I use the Data Environment, the application will be able to find the data files, if they are on the current path. This is true even though the path in the .scx is an absolute path.

Olaf. I will certainly consider your approach to development of not using visual development of forms. I had however generally followed the guidance in most of the books that I have of creating a form visually. It would be too much for this forum, but if you have any notes on your recommended style of development, I would be grateful to receive them. My address is andrew .mozley and I am with btinternet.

A few other fairly basic questions

1. If I do not use the Data Environment and have a database currently open, I take it that I would say
USE mydatabase!mytable INDEX mytable IN 0

2. I am still interested to learn how tables are closed when I release a form. That is, if a table has been automatically opened because I included it in the Data Environment, how does it get closed - by what code?

3. If I use the data environment to open a table, but wish to use a different ALIAS within the screen, can I do that? For example can I get the same effect as if I had included code to say :
USE mytable INDEX mytable ALIAS mine IN 0

Thanks as ever. Andrew
 
>visual development of forms
No, I wasn't talking about visual form designer, but the visual dataenvironment.

When I said I'm not a big fan of drag&drop development, I meant putting together tables in the DE and dragging them or fields of them into the form. That is easy, but doesn't work out with better business logic and data access classes available in frameworks. I absolutely have nothing against visually designing UI, but data access is not UI. It only has it's endpoint in UI.

The major thing is you get a 2 tier architechture, and not a more ideal 3 tier, if you simply bind controls to tables the way it's done by the DE. If you instead make use of Views you're one step ahead, when using Cursoradapters you can make use of standard classes, that would go in the right direction. But data access classes also do verify validity of data, en-/decrypt data, check user permissions etc and the DE doesn't provide features for that, you may put this in other places, but then have not a single instance responsible for the handling of data in all aspects of it. That's the big minus of the DE in comparsion to what frameworks offer.

Bye, Olaf.
 
Having thought a bit, there's one aspect of "drag&drop development" (as I called it) in regard to forms, that's not ideal in my opinion, too:

It's quite common to develop components (or buy a component framework) as building blocks of the forms you have in mind and then you combine these components on empty forms. That can work for small apps, but the real gain is having a class hierarchy of more and more specialised forms. Eg the base form has nothing on it, but could already have general code in regard to checking permissions to start the form, to use it for reading only or allow data modifications, etc. The form classes can among other things also define a template of methods to be implemented later in specialised forms, eg the base form can already have a method "hasChanges", you will need different code depending on what defines Changes and which workarea or areas are relevant.

A second level form class deriving from the empty base class might be a form to enter filter criteria for any data, or a form to list any data. All these forms do the general stuff that doesn't depend on what table is used later.

If you develop in that OOP way you will be able to just set a bunch of properties with a table name, a field list, some meta data about what fields are optional and what obligatory and such things and the rest is already there. In the end you will not put together building blocks again and again. Most of your form classes are just there to define base behaviour and UI, but are not used themselves. The application forms are at the end of the form class hierarchy. If you work this way you may also drag&drop controls onto the form of course, but repeated things will be inherited, which means you only made a generic form design once and reuse it as larger building blocks.

And just to clarify further: I also don't have anything against drag&drop in the meaning of OLE drag&drop, it's surely a thing, which can make your users productive with the app. The only thing is, what you can drag fro where to where is sometimes not very obvious.

Bye, Olaf.
 
2. I am still interested to learn how tables are closed when I release a form. That is, if a table has been automatically opened because I included it in the Data Environment, how does it get closed - by what code?

Set AutoCloseTables=.t. (the default) in the DE's property sheet, then code in the DE class closes the tables when the DE is released.

3. If I use the data environment to open a table, but wish to use a different ALIAS within the screen, can I do that? For example can I get the same effect as if I had included code to say :
USE mytable INDEX mytable ALIAS mine IN 0

Every table in the DE is represented by a Cursor object in the property sheet. Set its Alias property.
 
Dan has already addressed your other questions, but let me, too:

1. Yes, but I'd rather SET ORDER TO TAG NAME than specify an INDEX in a use, just because the main CDX file can contain all indexes on the table and does automatically update with the data in the dbf/fpt, IDXes need to be opened to update, otherwise get out of sync. This can sneak up on you as a hard to find error in not finding data in indexes.
If you use the DE you have the ORDER property, which also needs an index tag name, not an idx file name.

2. Additional to what Dan said: Even if you would like to leave tables open, if you can only do that, if the form doesn't have a private datasession, because the end of a datasession also closes all data files opened in it, no matter whether how AutoCloseTables is set. So this is the least important thing to worry about. Even with QUIT VFP flushes all caches and closes all file handles, but file corruptions are a topic on it's own.

3. Sufficiently answered.

OK, so overall you have several properties of each of the table objects of the DE, with which you can do the same as with all the USE options, but they aren't visualised. The only thing you can see visually is relations. And despite what I said not long ago, that this just denotes what the database knows about the tables primary and foreign keys and indexes, I just tried it out and the relations are really set and active. That's kind of OK, but if you create grids by drag&drop what's not set is the grid's ChildOrder, LinkMaster and Relationalexpr, which can do the same thing.

What you see is the table name, a field list and index list. In fact that is an advantage, as you don't see that list of field and tag names with USE. That's part of my earlier criticism. Isn't it simpler to have code USEing the tables as needed? Also overall DE properties like the InitialSelectedAlias are simply done in code with a SELECT ALIAS. I don't see any big plus aside of having some visual diagram of data involved in the form and the drag&drop mechanism creating a control bound to the data. But that's always very form specific, as Dan explained earlier you couldn't inherit that, despite of defining a DE class in a PRG, but you can't drag&drop tables and fields from there into your form. That disadvantage is now partly gone with the DE class and the form properties DEClass and DEClassLibrary. Giving a form a DE with these properties you lose the drag&drop of the DE into the form. I haven't tried if you open both a DEClass and a form class in two designers, whether you can drag&drop tables to the form, then, but if you can and then don't set the form properties to the right DE you're as bitten as you are manually setting controlsources wrong.

Last but not least: You might want to address the DE objects and properties with form code, but there is no intellisense for THISFORM.DATAENVIRONMENT. This is the name of the dataenvironment, but intellisense doesn't know anything about it.

As I said earlier application frameworks (like codebook, mere mortals) have data access classes based on container, they are kept invisible, but you can put them on your form, you can address them in code and they offer more in comparison the the DE cursor objects, eg embedding business logic, a save method with cascading save to child data, wrapping the overall save operation into a transaction, etc. etc. The DE merely handles opening tables, relating them, closing them. What you do with them is up to you, it's not managed by the DE or the cursor objects. Adding cursoradapters you have the chance to change that, but the value then is not the DE.

You typically will have two, three or even more tables belonging to each other in a hierarchy, eg 1:n:m or more complex. You will have rules that apply to data in relation to each other and you want to keep all that business logic encapsulated in one place, the DE only offers you to encapsulate how the group of tables are opened. That's not good enough to be of a real value.

Bye, Olaf.
 
I use the following code in the dataenvironment.BeforeOpenTables() event:

SetDBCPath(thisform.Dataenvironment, _VFP.DataPath, "Membership")

This calls the UDF:

************************************************************************************************
*
* Routine for setting the database directory location for the form
*
FUNCTION SetDBCPath
LPARAMETERS toFormDE, tcDBCPath, tcDBCName
LOCAL lcDBCName, lcFilePath, lnNumCursors, lnNdx
lcDBCName = FORCEEXT(tcDBCName, "DBC")
lcFilePath = ADDBS(tcDBCPath) + lcDBCName
lnNumCursors = AMEMBERS(lcProperties, toFormDE, 2)
FOR lnNdx=1 TO lnNumCursors
IF UPPER(LEFT(lcProperties[lnNdx], 6)) = "CURSOR"
IF ATC(lcDBCName, toFormDE.&lcProperties[lnNdx]..Database) > 0
toFormDE.&lcProperties[lnNdx]..Database = lcFilePath
ENDIF
ENDIF
ENDFOR
ENDFUNC

 
I use the following code in the dataenvironment.BeforeOpenTables() event

I've seen similar code elsewhere. Although I'm sure it works OK, I really don't think it's necessary. The approach we discussed earlier is preferable in my opinion, for the reasons we discussed.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top