views:

489

answers:

2

So the main program is in C#. Inserting new records into a VFP database table. It was taking too long to generate the next ID for the record via

select max(id)+1 from table

, so I put that code into a compile dll in VFP and am calling that COM object through C#.

The COM object returns the new ID in about 250ms. I then just do an update through OLEDB. The problem I am having is that after the COM object returns the newly inserted ID, I cannot immediately find it from C# via the OLEDB

select id form  table where id = *newlyReturnedID*

returns 0 rows back. If I wait an unknown time period the query will return 1 row. I can only assume it returns 0 rows immediately because it has yet to add the newly minted ID into the index and therefore the select cannot find it.

Has anyone else ever run into something similar? If so, how did you handle it?

DD

A: 

VFP needs to FLUSH its workareas.

Remus Rusanu
+4  A: 

Warning: your code is flawed in a multi-user environment. Two people could run the query at the same time and get the same ID. One of them will fail on the INSERT if the column has a primary or candidate key, which is a best practice for key fields.

My recommendation is to either have the ID be a auto-incrementing integer field (I'm not a fan of them), or even better, create a table of keys. Each record in the table is for a table that gets keys assigned. I use the a structure similar to this:

       Structure for: countergenerator.dbf
       Database Name: conferencereg.dbc
     Long table name: countergenerator
   Number of records: 0
        Last updated: 11/08/2008
Memo file block size: 64
           Code Page: 1252
          Table Type: Visual FoxPro Table

Field  Name                  Type                 Size   Nulls       Next       Step  Default  
----------------------------------------------------------------------------------------------------------------
    1  ccountergenerator_pk  Character            36         N                        guid(36)  
    2  ckey                  Character (Binary)   50         Y                          
    3  ivalue                Integer               4         Y                          
    4  mnote                 Memo                  4         Y                        "Automatically created"  
    5  cuserid               Character            30         Y                        
    6  tupdated              DateTime              8         Y                        DATETIME()  

Index Tags: 
1. Tag Name: PRIMARY
 - Type: primary
 - Key Expression: ccountergenerator_pk
 - Filter: (nothing)
 - Order: ascending
 - Collate Sequence: machine

2. Tag Name: CKEY
 - Type: regular
 - Key Expression: lower(ckey)
 - Filter: (nothing)
 - Order: ascending
 - Collate Sequence: machine

Now the code for the stored procedure in the DBC (or in another program) is this:

FUNCTION NextCounter(tcAlias)

LOCAL lcAlias, ; lnNextValue, ; lnOldReprocess, ; lnOldArea

lnOldArea = SELECT()

IF PARAMETERS() < 1 lcAlias = ALIAS()

IF CURSORGETPROP("SOURCETYPE") = DB_SRCLOCALVIEW *-- Attempt to get base table lcAlias = LOWER(CURSORGETPROP("TABLES")) lcAlias = SUBSTR(lcAlias, AT("!", lcAlias) + 1) ENDIF ELSE lcAlias = LOWER(tcAlias) ENDIF

lnOrderNumber = 0 lnOldReprocess = SET('REPROCESS')

*-- Lock until user presses Esc SET REPROCESS TO AUTOMATIC

IF !USED("countergenerator") USE EventManagement!countergenerator IN 0 SHARED ALIAS countergenerator ENDIF

SELECT countergenerator

IF SEEK(LOWER(lcAlias), "countergenerator", "ckey") IF RLOCK() lnNextValue = countergenerator.iValue REPLACE countergenerator.iValue WITH countergenerator.iValue + 1 UNLOCK ENDIF ELSE * Create the new record with the starting value. APPEND BLANK IN countergenerator SCATTER MEMVAR MEMO m.cKey = LOWER(lcAlias) m.iValue = 1 m.mNote = "Automatically created by stored procedure." m.tUpdated = DATETIME() GATHER MEMVAR MEMO

IF RLOCK() lnNextValue = countergenerator.iValue REPLACE countergenerator.iValue WITH countergenerator.iValue + 1 UNLOCK ENDIF ENDIF

SELECT (lnOldArea) SET REPROCESS TO lnOldReprocess

RETURN lnNextValue ENDFUNC

The RLOCK() ensures there is no contention for the records and is fast enough to not have bottleneck the process. This is way safer than the approach you are currently taking.

Rick Schummer
VFP MVP

Rick Schummer