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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Moving Cursor from 1 Datasession to another w/o losing long field name 1

Status
Not open for further replies.

MrDataGuy

Programmer
Oct 18, 2010
231
US
I have a number of business objects each with their own private datasession. Sometimes I need to create a cursor that needs to be used by the object that has instantiated the biz object. Solution: pass in the number of the datasession being used by the ‘main’ object to the biz object, and have the biz object temporarily use the passed in datasession when creating the cursor.

However sometimes there are situations that after the BIZ Object cursor is created in the Biz Object datasession that the cursor must be ‘moved’ into the main data session. Solution ( This is a common solution ) Copy the Cursor to an actual temp table on the hard drive and Select it into a cursor in the main datasession.

The issue with the common cursor-table-cursor move is that if the original cursor has long field names by the time the new cursor is created the long field names have been reduced to standard 10 characters.

So here are the core questions:

1) Does anyone know of a solution that allows cursor to new datasession copying that does not lose long field name information?

2) If #1 is no, what about the idea of somehow using a VFP Database? I really am not a fan of the VFP Database, I use VFP free tables, but maybe a special Database that is just used for creation of temp tables for Cursor Copying might work.

3) If I come up with a solution (I have some ideas in mind ) should I post it here? Or is this not an issue for most developers.


Lion Crest Software Services
Anthony L. Testi
President
 
Your only lack a deisgn that is prepared to create the cursor in the datasession, where it's needed.

If eg two forms should access the same cursor, then make them use the default datasession instead of a private one.

Also there's nothing wrong in requerying the same data into several datasessions as needed.

So a long term solution is to rethink your application design.

I do sometimes have the same issue and rather use the method of requerying the data or putting two forms together into one session.

Your business object could be based on session to be the session your form uses, then you can set all your forms to default datasession and let the business object encapsulate the form set.

As a direct answer, additional to a temp table you have cursortoxml and xmltocursor, which can pass a cursor as an xmlstring in memory without any file on disc. Otherwise yes, use a database, it doesn't have to be special, simply create database temp then cop to some.dbf database temp and you keep long field names and other cursor properties.

Another solution would be select into array and append from array.

Bye, Olaf.
 
Cursortoxml/XmlToCursor

That looks like an excellent solution, thanks for the idea.

I know you pounded on the my architecture, but at this time I do not agree that it is poor (my summary of what you said.) IMO I have Biz Classes who have the responsibility to create/store/serve to/ etc. etc. other objects (like forms, but not limited to such ) information (properties, cursors etc ) and the using objects responsibility to use that information in appropriate ways. Again IMO information hiding ( including datasessions ) is important and a Biz object should not share the datasession of a Form ( or any other object. ) I have found that following these guide lines has been extremely beneficial, e.g. the benefits far outweighs the cons (like troubles passing cursor information )

Now why I think that I am currently ‘right’ in my design choice, if history is any guide, I may someday come to understand and appreciate what you are recommending and change to your ideas, with the result I will become “more right” <wink>
Thanks again for the reply you made some good suggestions.


Lion Crest Software Services
Anthony L. Testi
President
 
Well, if you pass whole cursors, shouldn't the bisuness logic simply create the cursor in the right datasession. You say you do so and you say you pass in the datasession to do so sometimes. And if you have the need to access the same cursor in two datasessions then either do create the data in the two session or put the using objects to the same session. Wouldn't that be a cleaner solution?

Information hiding is a good concept, but meant in the first place for the inner structure of classes. Data is something coming from outside and being processed. Surely the access to data directly shoudl be limited to the data acess tier objects only, but once you have a cursor it's an internal data object in my eyes and you can share it among all the objects that need to process it without breaking the information hiding.

Other means of sharing data in all or partially would be by passing object created wvia SCATTER NAME. You can also create collections of such objects instead of arrays. Especiall if forwarding data to foreign lanaguage components that might be a way. XML is also universlaly good, but slow, if there is much data in a cursor.

Bye, Olaf.
 
I would stay with the free table concept and copy the cursor to a free temporary table and pass the name of the the free temporary table to the other business object. The other business object could then load it's own cursor from the free temporary table and erase it to clean up.

Andy Snyder
SnyAc Software Services Hyperware Inc. a division of AmTech Software
 
Andy,
your suggestion is what I am doing at the moment ( Well OK I think my solution is a little cleaner. ) You know what, I am going to post the code (See below ). Anyhow the issue with the solution is that long field names get truncated, e.g. free tables only allow 10 character field names. So I was looking for a better solution. Olaf mentioned using XML which I am going to try soon.

Here is the current solution for those that care:
Code:
&&-- Kludge Alert!  Kludge Alert!
&&-- Copying the cursor 
&&-- Via writing then reading from the disk
PROCEDURE Copy_Cursor_To_DataSession( pCursor_Name, pDS_From, pDS_To )
	&&-- Save the starting session to 
	&&-- return to at the end of this code
		LOCAL DS_AT_Start
		m.DS_AT_Start = SET( [DataSession] )
	
	&&-- Delete any current Temp.DBF on disk
	&&-- Note there should not be one actually there to delete
 		DELETE FILE Temp.dbf


	&&-- Copy the cursor to disk
		SET DATASESSION TO m.pDS_From
 		SELECT (pCursor_Name)
 		COPY TO Temp.DBF
 	
 		&&-- Copy from Disk to the new datasession
		SET DATASESSION TO m.pDS_To
		
		LOCAL m.xCommand 
		m.xCommand = [SELECT * FROM Temp.dbf INTO CURSOR ] ;
		           + pCursor_Name + [ READWRITE ]
		&xCommand 
	
	&&-- Clean up code
		SELECT Temp
		USE			
		DELETE FILE Temp.dbf

		SET DATASESSION TO m.DS_AT_Start
ENDPROC


Lion Crest Software Services
Anthony L. Testi
President
 
Just one thing about the xml solution: Use the option to create an inline schema and xmltocursor will produce the right field types and lengths again.

It's very simple indeed:

Code:
Local lcXML
CursorToXML(cAlias,"lcXML",3,1+2+4+8+16,0,"1")
* pass on xml

Code:
* receive xml
XMLToCursor(lcXML,cAlias)

Using the flags 1+2+4+8+16 is a suggestion, you might fit to your situation, but it should work fine, creating RAW xml keeps it shorter than pretty formatted xml, it only needs to be machine readable. It's still very verbose as xml always is.

Bye, Olaf.
 
Using the suggestions that Olaf gave (Thank you again Olaf) the following is the code that I am now using, and so far is working great.

(Side note out of 100,000 of records 3 had CHR(2) in the middle of some text that caused this code to crash. How the Chr(2) which are Start of Text characters I do not know, the data is 10 years old. Be aware of that issue when you use this code.)

Code:
&&-------------------------------------------------
&& Note:  If there are CHR(2) in the data 
&& XML errors may happen.  CHR(2)+CHR(1) seem to be ok
PROCEDURE Copy_Cursor_To_DataSession( pCursor_Name, pDS_From, pDS_To )
	LOCAL XML_TEXT, DS_AT_Start

	m.DS_AT_Start = SET( [DataSession] )
	
	TRY
		SET DATASESSION TO m.pDS_From
		CURSORTOXML( m.pCursor_Name, [m.XML_TEXT], 3, 1+2+4+8+16,0, [1] )

		SET DATASESSION TO m.pDS_To
		XMLTOCURSOR( m.XML_TEXT, m.pCursor_Name )

	CATCH TO oException
		_ClipText = oException.Message
		SET STEP ON 
			
	ENDTRY
		
	SET DATASESSION TO m.DS_AT_Start
EndProc

Lion Crest Software Services
Anthony L. Testi
President
 
What codepage is the dbf in? I assume 1252, that would be normal for most western european languages including english, no matter if UK or US.

Seems chr(2) is not an allowed character in that codepage. XML, or better the XML parser is sensitive to such codepage errors.

This can happen with 10 year old or new data, as vfp is forgiving about the values of dbf fields. You could check out, if a conversion to UTF-8 would help, that is done, if you add 48 in the nFlags parameter instead of 16, so make that 1+2+4+8+48.

Also XML is not th way to go to transfer 100000 records from one session to another. Even though it's done in memory, XMLTOCURSOR is using MSXML parser, which is creating a huge DOM model in memory, checking the XML integrity (eg codepage errors) and parsing is very slow, you actually might even be better off when retrieving the 100,000s of records from the server again in the other datasession. The XML method is good for say up to 10,000 records.

It's really better to process that much records in one datasession only from the perfromance perspective, and in cases it takes much too long that weighs out any concerns about OOP encapsulation. It's not meant to encapsulate data, not table data, it's own properties might be private, but as soon as you have a chain of objects crunching on the same data I wouldn't keep their sessions seperate. That's more the principle of an assembly line. You surely have seperate objects working on the product in several stations, but it would hurt, if the assembly line would not be one line and instead you'd work in several rooms and would need to transport the product you work on from room to room instead.

Either you add a setdatasession method to your classes to change datasession for processing the data already opened or if you don't want that use two arrays: AFIELDS for the cursor structure and SELECT ... INTO ARRAY for the data and put that together again in the other datasession. The only limitation being, that APPEND FROM ARRAY does not support appending into memo fields. Even though that is 4 lines of code instead of 2 it surely is faster.

Last not least I'd also even try with a temporary DBC and COPY TO ... DATABASE. Remember both vfp and OS do caching, so it's not necessarily writing to disc. And that method is compatible with any cursor structure including memos again.

Bye, Olaf.
 
Olaf,

Thanks for the continuing feedback!

"The XML method is good for say up to 10,000 records."

In real use the data is 1- ~1,000 records, but for testing I was hitting with some larger cursors.

The 'Code Review' feedback is excellent, and yes like most programmers I suffer some times from the 'how dare you my code is perfect' issue <smile>. But I try, like in this case, get over it and learn from the well written/given feedback. So thank you.

I think I will use the code as is, as it is better then it was before, does what I need it to, meets performace levels etc, and I need to complete 10 other projects. But I know where to go to improve this in the future. In fact I am going to put the URL into my code to this thread.


Lion Crest Software Services
Anthony L. Testi
President
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top