Today I stumbled over the adt field types in Delphi Datasets. We use custom client datasets to bind objects to datasets in our views. With that dataset and our custom derivation of TDbGrid and enabled FastMM4 full debug mode we get an access violation with the following stack trace:
System.Generics.Collections.TListHelper.DoIndexOfFwd4((kein Wert))
System.Classes.TComponent.FreeNotification($7F961CE0)
Vcl.DBGrids.TColumn.SetField($7F971700)
Vcl.DBGrids.TColumn.GetField
Vcl.DBGrids.TCustomDBGrid.Notification($7F971220,???)
System.Classes.TComponent.RemoveFreeNotifications
System.Classes.TComponent.Destroy
Data.DB.TField.Destroy
System.TObject.Free
Data.DB.TFields.ClearBase(True)
Data.DB.TFields.ClearAutomatic`
Data.DB.TDataSet.DestroyFields
Datasnap.DBClient.TCustomClientDataSet.InternalClose
Data.DB.TDataSet.CloseCursor
Datasnap.DBClient.TCustomClientDataSet.CloseCursor
Data.DB.TDataSet.SetActive(???)
After some debugging fun, i found out that the internal FieldList of the Dataset contains a already destroyed field. After doing some more reasearch, I found out that disabling property
DataSet.ObjectView
disables the internal FieldList and the access violation goes away.
As we don't use ADT field types, I suspect disabling it does no harm, but one never knows.
Background
I've had already a lot of problems with TDataSet and TDbGrid, starting with the inherent problems of the flickering scollbar in the grid and ending in corrupt BCD-Parameters for oracle databases. We updated from XE3 to DX10, the access violation did not occur before.
I can not post the code of our custom dataset / custom grid
we are freeing the dataset before the grid
Related
I'm using a module that contains some TfrxDBDataset connected to TFDQuery to build a report. In the construction of the report there are parameters that must keep the queries closed. But, TfrxDBDataset objects are opening queries even with properties like Enabled = false, OpenDataSource = False. Apparently these are old backward compatibility properties
A workarround is not assigning the dataset property to TfrxDBDataset, but it is not very elegant. Does anyone know if there is a option in FastReport that prevents this behavior?
I want to load the content of of dbctrlgrid at runtime (from database). So I encountered several challenges:
How to detect if the dbctrlgrid is empty and/or how to clear it.
How to put Tlabel and Tdbtext on the panel. The main problem seems to be to find the right parent. dbctrlgrid doesn't work. There is an object called Tdbctrlpanel which should work, but I don't know how to access it. I could not find it in properties or methods of Tdbctrlgrid.
Any code snipplet is welcome
To answer 1)
You don't query the TDBCtrlGrid, you query the underlying dataset; if it .IsEmpty the grid is empty.
When people start using data aware (grid) components they have the tendency to see that as the 'data container' that you can query and modify, but that is not the case. See it as a view on your underlying data with some built-in editors that modify that data. Then the 'same rules' apply to you as to these editors: update the underlying dataset.
To add controls to a TDBCtrlGrid you have to set the controls parent to the Panel property of the TDBCtrlGrid. The problem is that this property is protected. There are several ways to overcome this limitation. One is shown at Delphi About: Accessing protected members of a component
This is a common technique known to Delphi programmers as the 'protected hack'.
When I use a TClientDataSet which is connected to a TxxxQuery component, I can add TFields to both components at design time. I recognized, when I don't specify the TFields in the TxxxQuery component, they are retrieved when the query is executed at runtime.
My questions is: Is there a performance difference when I add the TFields at design time to the TxxxQuery component?
When you add the fields at design time you get the strongly typed QueryName_FieldName fields you can use directly from code, skipping the name-based QueryName["FieldName"] lookup required if you don't have them.
From a performance stand-point the difference is most likely insignificant; From a language perspective having the fields added at design time provides better type safety, but only if you access the fields from code, and only if you use the QueryName_FieldName.Value syntax, not the named-based QueryName["FieldName"] syntax. If you use data-bound controls there's no difference.
I personally only add fields to TClientDataSet at design time when I need to use the client dataset without binding it to an other data source (ie: use it as a temporary table for reporting).
I'm building an application using IW 8 and Delphi 7. Application is 3-tier.
1) on the app's datamodule I have several TClientDatasets and TDataSources associated(set on master-detail relationship)
2) on an IW form I have several TIWDBLookupComboBoxes with datasets pointed to datamodule datasources.
The problem I'm facing: when I select a value from one of the TIWDBLookupComboBoxes, the datasets don't react(I'm changing the index of the master dataset, so the detail dataset should also change). So I saved the clientdatasets to xml files and imported the data into a win32 application, set all the master details in the same manner, and voila - everything is ok.
So my question is: it seems that TIWDBLookupComboBoxes don't move the internal cursor of the datasets? If so, on the OnChange event of the TIWDBLookupComboBoxes, if I set the recno to what I want I'll have problems with forms rendering?
how can I solve this?
I resolved this by using simple IWComboBox components, and on the OnChange event setting up the RecNo property to the combobox's index+1(itemindex is 0 based). Other solution is to make a filter on the dataset with the combobox's value.
alt text http://img29.imageshack.us/img29/825/simplemodel.jpg
How you can see above, it's a kind of subclass a table in a RDBMS (in this case, my favorite: MySQL) so I handle it with Visual Subclassing a base form for tb_order_base with validating field data, etc.
This way, I'm free of repeating code and some other bothering problems, well, it seems a true OO aproach. But ...
Now I got a Big problem with subclassed form i.e. tb_order_service with a master/detail approach, when I post the tb_order_base dataset, instead of Delphi first post it and get the PK ID from RDBMS and then post the TB_ORDER_PRODUCT with id filled, it does the opposed, posting the detail tb_order_product dataset first then master tb_order_base, so I get a big foreing key constraint error.
Does anyone has any idea to how to by-pass this amazing problem?
I have asked it before, but with few details in The Master/Detail Behavior
One thing I have done in such a condition was to break away from doing DBMS direct calls, and to instead use a memory dataset (or client dataset) for the form. When the user presses the save button, I would validate my edits and return any errors. If no errors then I would begin a database transaction, commit the master record (and then read the master record key back if its an insert), then run thru each table commiting the child records, followed by a commit of the transaction (or rollback if there were any problems saving child data).
I also stay away from using data-aware components. They work great for simple utility type programs, but when you start creating a complex system using them you will find little gotchas along the way that are easily solved by using a standard edit and a function to push/pull data to/from the database. The only exception I generally make would be for grids, but I only use the grid for selection...the actual editing is done using non data-aware components.