Delphi - ClientDataSet - validating data - delphi

I have an application in Delphi 7 which is using a clientdataset, and make several operations on it. ClientDataSet is linked to an Intraweb Grid.
I make an insert or an edit on the ClientDataSet. How can I verify the data introduced in the clientdataset for each field? I can not verify the input from the user on the webform, so I must make validation using ClientDataSet events.
LE: I want to validate data when the user make the input. Not at the onbeforepost event. So, I put the clientdataset in the edit/insert. User make an input in the grid, and I want to validate the data for that row in the clientdataset like in the image bellow:
first column is string, second is integer, the third one is also an integer. Now, I want to validate the third column after the user make the input. This validation must be done (if it is possible), only by using clientdataset events/hacks.

You should handle TClientDataset BeforePost event, and if data is not valid use abort method

TField has an OnValidate event for that purpose. It has also a CustomConstrain property that can use a SQL like syntax for constraints. DefaultExpression will let you select a value if no value is given. These are usuful for single-field validation. If you need more complex checks across more than one field, then you have to use dataset or datasource events.
Anyway, if the Intraweb grid sends the server whole records and not single fields edits, you may not validate fields as they are entered in the grid but adding code client side.

If you want a field based validation, you can handle the OnDataChange event of the TDatasource connecting the grid to the dataset. This will be triggered whenever a field has been changed by the user. You should be aware that it will also be triggered in some other situations.

Related

With delphi, i have a dbgrid. I want when user has filled the row and posted it, the line of grid is readonly

With delphi, i have a dbgrid. I want when user has filled the row and posted it, the line of grid is readonly
Do you have any suggestion ?
thank you
Long time since last Delphi dev but I would have aborted an OnBeforeEdit event if there is data in the buffer.
My two cents
I suggest to keep a list with all rows (All data or just the primary key or the row ID) already updated by the user. Before allowing an update, you check the list.
To prevent accidental / unwanted edits you can turn off the AutoEdit property of the TDataSource providing data to the DBGrid. Then be sure any controls like a DBNavigator do not have edit enabled.
From the Delphi Help:
Description
Specifies whether the data source should call the Edit method automatically.
AutoEdit is true by default. If AutoEdit is true, then when a user attempts to modify the data displayed by the control, the data source calls the underlying dataset's Edit method.
Set AutoEdit to false to protect data from unintentional modification. Even if AutoEdit is false, an application can explicitly call a dataset's Edit method to allow data modification.

How do I add data to a database in Delphi by using edits instead of a DBNavigator?

My interface is very basic. It just includes edits for the user to input data into a database, when they click the button i want it to add the data into my database.
You can easily do this.
Go to the Data Controls tab of the Component palette.
Select a TDBEdit and place it on the same form as your DBNavigator. The IDE will name this DBEdit1
Set the Datasource property of your DBEdit1 to the same datasource as your DBNavigator.
Set the DataField property of DBEdit1 to the name of a field in your dataset.
Compile and run.
That's it. Leave your DBNavigator on your form because you will find that when you make a change to the contents of DBEdit1, its Save and Cancel buttons automatically enable to let you save or cancel the change.
Also, you'll find that if you click your DBNavigator's '+' button, which begins the insertion of a new record into your table, you can then type the field values for the new record into your DBEdits.
Don't use normal non-DB-aware TEdit components and a dynamically-created Sql statement which concatenates the TEdits's contents with other Sql as suggested in the other answer which briefly appeared here and now seems to have been deleted - it is a waste of time, but much more importantly renders your app vulnerable to Sql-Injection - see https://en.wikipedia.org/wiki/SQL_injection. By sending the server an unverified Sql statement which includes what the user has typed into a TEdit, you're effectively providing the user with an opportunity to type additional Sql statements into the TEdit and that is exactly how Sql injection can occur. On the other hand, when you use TDBEdits, the Sql for updating the database record is automatically generated by Delphi's TDataSet framework in a way which does not provide a similar opportunity for Sql Injection.
If some reason you absolutely have to generate your own Sql Update statements, to minimise the risk of Sql Injection, make sure that you use a parameterised Update statement, that is, one where the changed field values are specified as values of parameters in your TDataSet-descendant's Parameters object, rather than in the Update Sql itself. An example of a parameterised Update statement might be:
Update MyTable set FieldA =:FieldA, FieldB=:FieldB where RowID =:RowID
where :FieldA, :FieldB and :RowID are the parameters.

How to retrieve information from a field that has the property Required set as false?

I have the following DBX structure in my software:
TSQLDataSet -> TDataSetProvider -> TClientDataSet
One of the fields from my TClientDataSet has the property Required set to false, because this field auto increments based on triggers and generators on the database (Firebird).
However, after configuring both TSQLDataSet and TClientDataSet with this field not being required, I'm getting really weird results when I try to read this field from my TClientDataSet. I suspect that I might need to do something extra to force my TClientDataSet to acquire the value of this field in this condition.
What am I missing here?
Thanks in advance.
EDIT
The help file for the Required property says something about this, but I couldn't quite understand what it want me to do.
Description
Specifies whether a nonblank value for a field is
required.
Use Required to find out if a field requires a value or if the field
can be blank.
If a field is created with the Fields editor, this property is set
based on the underlying table. Applications that set Required to true
for fields that must have values (for example, a password or part
number), but for which the underlying table does not require the
field, must write an OnValidate event handler to enforce the property.
When the Required property reflects a property of the underlying
database table, trying to post apply a null value causes an exception
to be raised. Applications that set the Required property to true when
the underlying table does not require the field, should raise an
EDatabaseError exception on null values in the OnValidate event
handler in order to achieve the same result.
EDIT 2
Forgot to mention: between the TDataSetProvider and the TClientDataSet, there is a DataSnap layer (the TClientDataSet connection is made with a DataSnap driver).
EDIT 3
I created a small test case with this DataSnap setup and it worked perfectly. The project is legacy, messy and I guess that either I have an obscure option configured somewhere that is biting me or I have stumbled in a DataSnap bug.
Haole, have you tried TClientDataset.RefreshRecord after inserting? Or even TClientDataset.Refresh?
Having generators, you can even get the generator in advance (before calling ApplyUpdates) in a query like select gen_id(generator,1) from RDB$Database (it's from memory, don't have Firebird here to test) and fill the PK field in advance.
EDIT: seems this is a heisenbug. I would try to remove the components and reconfigure them again from scratch (which means: after you remove, save and close Delphi).
Or even better, create an empty project with just that needed query configuration and try to view that data in a TDBGrid. If this problem still happens, maybe your FB installation have some component corrupted (or even Delphi installation)
Seems that the problem was an outdated field being read as an INTEGER and it was a SMALLINT in the database.
This problem was hard to debug and this question was misleading. Thanks for everyone that helped me debug this.

How to register events posted from Firebird with parameters in Delphi

I have an event posted from firebird database in a trigger after a new record is inserted like this: post_event 'SPOOL' + new.username;
I want to register this event with SIBfibEventAlerter (FIBPlus) in a Delphi application and run a procedure. Problem is that event name depends on user name added the record.
You could read the usernames from the user table (if new.username is actually a field and not some FB system value) and create the eventalerters components dynamically, one per user name.
Since events don't really support parameters, one way would be to add extra fields to table, which contain auto incremental id (or timestamp) and the data you need as a parameter.

Using a ClientDataSet in Delphi, are you able to display both Data & Delta records in a DBGrid?

I am making a app in Delphi 6 + MySQL database using standard data-aware components and dbExpress. The app allows a users to view records in a grid and edit data (insert and/or delete records) client side. These data edits are then only written to database on clicking the submit button. All of this works fine and has the following setup:
Controls:
1. DBGrid1 linked to a DataSource1 to display data visually.
2. DataSource1 is linked to ClientDataSet1 to offer data for DBGrid to display.
3. ClientDataSet1 is linked to DataSetProvider1 to provide client-side data for editing.
4. DataSetProvider1 is linked SQLDataSet1 which selects records from a single DB table.
5. SQLDataSet1 is linked to SQLConnection to provide connection to MySQL database.
Actions:
1. User inserts a record: I use ClientDataSet1.InsertRecord;
2. User deletes a record: I use ClientDataSet.Delete;
3. User submits data: I use ClientDataSet1.ApplyUpdates(-1);
This all works great in terms of handling data & posting data (with the inclusions of a small hack on DataSetProvider1BeforeUpdateRecord to delete records).
NOW FOR MY PROBLEM:
When the user first loads the form, the DBGrid1 displays all original records, removes all deleted records. But when the user inserts a new record in ClientDataSet1, a blank record is displayed in DBGrid1. The actual data is not lost or set as NULLS as when you ClientDataSet1.ApplyUpdates, this record is correctly written to the DB.
I know TClientDataSet has a data property for original data and a Delta property for edited data. Can these two properties with data by displayed in a single DBGrid at one time & still allowing the user to edit the data?
I have looked at 30+ resources and demo apps & all avoid this issue. Can this be done?
Ok...this question has been viewed a fair amount without much feedback. I did some research by downloading many tutorials, demo apps & read multiple articles/help info discussing the use of these controls.
The net result:
These controls are a bit buggy in general.
In specific reference to my question, it is safe to say the controls cannot do what I describe as a standard.
This is based on the 30+ demo apps, articles and tutorials I reviewed that either don't describe displaying both the original data with Delta data in a single data grid. Sure you could hack this outcome (which is what I did) using a Listbox or StringGrid, but that also indicates the control cannot or not usable at providing this functionality (on the slim chance it might).
This functionality is an obvious oversight in my opinion as a user wants to see the potential outcome of their actions, conveniently in a single data grid AND a developer wants to provide that in a simple & pain free way i.e. using a datagrid to display data!
Question Answered [if you can prove differently with a demo app that I want to review, I will remove this].
If you created a demo app and couldn't get it to work, up vote my answer. Thanks
The TDBGrid control provided with Delphi does not have the built-in functionality to display both the old and new values for fields. You are welcome, of course, to inherit from the Grid or create your own and add the functionality, or buy a third party component that accomplishes what you want. You are not limited to the standard controls, though they do provide the most commonly required functions.
You can also accomplish what you want by using calculated fields. For example, if you have a String field Name, add a new calculated String field to the dataset called OldName, with the same length as Name.
Then in your OnCalcFields event for the dataset, simply enter code like the following:
if DataSet.State = dsEdit then
begin
DataSet.FieldByName('OldName').Value := DataSet.FieldByName('Name').OldValue;
end
else
begin
DataSet.FieldByName('OldName').Value := Null;
end;
TClientDataset will handle itself the correct record status. Changes are logged, but unless you ask it explicity to show some other status (see StatusFilter property), it will show the actual state of a record.
It is possible InsertRecord bypasses some notification mechanism, so the field display is not updated. What if you perform simpy an Insert and the set field values?

Resources