So, I Googled the following, but cannot seem to find an answer.
I've got a few Access tables that I am trying to update from Delphi.
The one table updates perfectly, so I know my database connection is active (the connection is set when the main form loads).
The problem is the second table. I keep on getting the error: "Dataset not in edit or insert mode".
In the formshow procedure I've got the following code:
//Connect to the individuals details table
dmoDonations.tblInd.Connection:= dmoDonations.adoDonations;
dmoDonations.tblInd.ReadOnly:= False; //Enable read / write
dmoDonations.tblInd.Active:= False;
dmoDonations.tblInd.TableName:='Individuals';
dmoDonations.tblInd.Active:= True;
dmoDonations.tblInd.Append; //Insert new record
dmoDonations.cdsInd.Open;
dmoDonations.cdsInd.Active:= True;
dmoDonations.cdsInd.ReadOnly:= False;
dmoDonations.cdsInd.Refresh;
And the rest of the code to update the table is under the updateButtonClick procedure:
dmoDonations.tblInd.Insert; //Insert new record
dmoDonations.tblInd['Name']:= edtName.Text;
dmoDonations.tblInd['Address1']:= edtAddress1.Text;
dmoDonations.tblInd['Address2']:= edtAddress2.Text;
dmoDonations.tblInd['Address3']:= edtAddress3.Text;
dmoDonations.tblInd['Phone']:= edtPhone.Text;
dmoDonations.tblInd['Email']:= edtEmail.Text;
dmoDonations.tblInd.Post; //Saves new records
It's at the last line where the error pops up when I step through the code. Why would this error occur even though the table is open and active and I've inserted / appended the table (have tried both options in both sections of code)?
Related
I write this code for button click event . the first time I click the button every thing work correctly but when click for the second time on button it raise an error. whats the problem?
procedure TfrmMain.Button1Click(Sender: TObject);
var
B : Boolean;
begin
DM.tblTemp.DisableControls;
B:= DM.tblTemp.Locate('FoodName', DM.tblAsli.FieldByName('FoodName').AsString,[]) ;
if B then
begin
DM.tblTemp.Edit;
DM.tblTemp.FieldByName('Number').AsInteger:= DM.tblTemp.FieldByName('Number').AsInteger + 1;
DM.tblTemp.Post;
end
else
begin
DM.tblTemp.insert;
DM.tblTemp.FieldByName('FoodName').AsString := DM.tblAsli.FieldByName('FoodName').AsString;
DM.tblTemp.FieldByName('UnitPrice').AsInteger := DM.tblAsli.FieldByName('FoodPrice').AsInteger;
DM.tblTemp.FieldByName('Number').AsInteger := 1;
DM.tblTemp.Post;
end;
TotalPrice:= TotalPrice + DM.tblTemp.FieldByName('TotalPrice').AsInteger;
DM.tblTemp.EnableControls;
end;
the Error is
Row cannot be located for updating. Some values may have been changed
since it was last read
DM is data madual
tmbTbl is ADOTable
It is a pity you haven't said which DBMS you are using (e.g. Sql Server or MS Access,
nor told us a full list of the table's column types and indexes, if any.
The most likely answer to your q is that the variable B is set to False the first time
because the call to tblTemp.Locate fails to locate the Food name in question, so the Insert branch executes and the food's data is
added to the table but the second time the Edit branch executes and the error occurs,
though you have not said where, exactly. My guess would be on the call to .Post, because
the error message is one the ADO layer, which sits between the DBMS provider and your app,
emits when it attempts to post a change to the table but cannot identify which table row to update.
As I mentioned in a comment, the fix to this is usually at add a primary key index to the table, and I gather from your latest comment that this has succeeded for you. For the record and benefit of future readers it would be helpful if you could confirm which DBMS you are using and which Ole Driver you are using in your connection string.
Fwiw, I've tested your code using a table on MS Sql Server and MS Access and do not get the
error with either database.
Btw there is an obvious q, "How is it that tblTemp.Locate succeeds, but ADO is unable to
identify the correct record to post the update?" The answer is that tblTemp.Locate works in
a different way than identifying the relevant row to post the update.
Hi guys i've written a function which has to check the ID given to the function as iID and then output the name of the member of it is found, otherwise it must output that it is not found
A different table is active when this function is called so it must change the table to Members (to search the ID) and then back again afterwards (I have multiple tables)
function fCheckID(iID:integer):String;
var sTable:string;
begin
sTable:=datamoduleX.tableX.TableName;
datamoduleX.tableX.TableName:='Members';
if datamoduleX.tableX.Locate('RefNo',iID,[]) then
result:=dmRooiX.tblRooiX['Name']+' '+datamoduleX.tableX['Surname']
else
result:='ID: '+inttostr(iID)+' does not exist';
datamoduleX.tableX.TableName:=sTable;
end;
but the problem is every time I call this function I get an error that says "Cannot perform this operation on an open dataset"
if I close the dataset before I run the function I get "Cannot perform this operation on a closed dataset"
I know the error occurs when I try to access the table name or change it (the function does not give the error when those 3 lines are commented out)
I have no idea how to make this work
any help would be greatly appreciated
Example :
Table1.TableName := 'TABLE1';
Table1.Open;
Table1.TableName := 'TABLE2'; <-- Cannot perform this operation on Open data set. Because Table1 is open
Table1.Locate('ID',11,[]);
simple solution
Table1.TableName := 'TABLE1';
Table1.Open;
Table1.Close; <--Close table before change table name
Table1.TableName := 'TABLE2';
Table1.Open; <-- Open new table before do Locate
Table1.Locate('ID',11,[]);
I am currently creating an application but when running the following code:
ADOQRanking.Edit;
ADOQRanking['Bye']:=True;
ADOQRanking['TableAssigned']:=True;
ADOQRanking.Post;
... a new record with null values is created in ADOTPoints (the table that ADOQRanking sorts), with Bye and TableAssigned both set to true, rather than the intended record being edited. It should also be noted that there is no code in the entire procedure that creates a record in ADOTPoints.
try to use fields by it's names
ADOQRanking.FieldByName('Byte').Value := true ;
and so on ..
Recently, a program that creates an Access db (a requirement of our downstream partner), adds a table with all memo columns, and then inserts a bunch of records stopped working. Oddly, there were no changes in the environment that I could see and nothing in any diffs that could have affected it. Furthermore, this repros on any machine I've tried, whether it has Office or not and if it has Office, whether it's 32- or 64-bit.
The problem is that when you open the db after the program runs, the destination table is empty and instead there's a MSysCompactError table with a bunch of rows.
Here's the distilled code:
var connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=corrupt.mdb;Jet OLEDB:Engine Type=5";
// create the db and make a table
var cat = new ADOX.Catalog();
try
{
cat.Create(connectionString);
var tbl = new ADOX.Table();
try
{
tbl.Name = "tbl";
tbl.Columns.Append("a", ADOX.DataTypeEnum.adLongVarWChar);
cat.Tables.Append(tbl);
}
finally
{
Marshal.ReleaseComObject(tbl);
}
}
finally
{
cat.ActiveConnection.Close();
Marshal.ReleaseComObject(cat);
}
using (var connection = new OleDbConnection(connectionString))
{
connection.Open();
// insert a value
using (var cmd = new OleDbCommand("INSERT INTO [tbl] VALUES ( 'x' )", connection))
cmd.ExecuteNonQuery();
}
Here are a couple of workarounds I've stumbled into:
If you insert a breakpoint between creating the table and inserting the value (line 28 above), and you open the mdb with Access and close it again, then when the app continues it will not corrupt the database.
Changing the engine type from 5 to 4 (line 1) will create an uncorrupted mdb. You end up with an obsolete mdb version but the table has values and there's no MSysCompactError. Note that I've tried creating a database this way and then upgrading it to 5 programmatically at the end with no luck. I end up with a corrupt db in the newest version.
If you change from memo to text fields by changing the adLongVarWChar on line 13 to adVarWChar, then the database isn't corrupt. You end up with text fields in the db instead of memo, though.
A final note: in my travels, I've seen that MSysCompactError is related to compacting the database, but I'm not doing anything explicit to make the db compact.
Any ideas?
As I replied to HasUp:
According MS support, creation of Jet databases programmatically is deprecated. I ended up checking in an empty model database and then copying it whenever I needed a new one. See http://support.microsoft.com/kb/318559 for more info.
I am using a TClientDataset with the following options for the provider:
ResolveToDataSet = True
Options = [poPropogateChanges, poUseQuoteChar]
UpdateMode = upWhereKeyOnly
AfterUpdateRecord = DataSetProvider1AfterUpdateRecord
The provider is connected to a TIBCQuery which manages the generator for the NO_INVOICE key.
On AfterUpdateRecord the following code is done (as found in many places in groups to really propagate the key change when posting to the database)
DeltaDS.FieldByName(ClientDataSet1NO_INVOICE.FieldName).NewValue
:= SourceDS.FieldByName(ClientDataSet1NO_INVOICE.FieldName).NewValue
The following code is then used to add a record:
ClientDataSet1.Params[0].AsInteger := -1;
ClientDataSet1.Open;
ClientDataSet1.Edit;
ClientDataSet1NO_INVOICE.AsInteger := -1;
ClientDataSet1NO_STORE.AsInteger := 1;
ClientDataSet1.Post;
ClientDataSet1.ApplyUpdates(-1);
If I call ClientDataSet1.Refresh after the ApplyUpdate, the underlying TIBCQuery is reopened with the original param of -1 and not with the new key... even if the ClientDataSet1NO_INVOICE.AsInteger shows up the new value assigned after merging records...
The use of Refresh here is only to simplify this example... The problems happens when we insert a record, apply updates and edit the record again.
Do I miss something with the usage of the ResolveToDataset option or should I explicitly reopen the query with the new param?
I never had this problem before when using ResolveToDataset = False on other projects...