TClientDataSet OnNewRecord vs. AfterInsert - c++builder

My question is about inserting records into a table on a firebird database. The table is very simple - it has only 2 columns:
CREATE TABLE myTable
(
COL_ID CHAR(36) NOT NULL CONSTRAINT PK_COL_ID PRIMARY KEY USING INDEX IX_COL_ID,
COL_ACRONYM VARCHAR(255)
);
In my application (c++ Builder XE10) I have the following constellation:
A TDataSource connected to
A TClientDataSet connected to
A TDataSetProvider connected to
A TFDTable connected to
A TFDConnection connected to
A Firebird Database
The application does the following:
Insert a new row using TClientDataSet.Append();
Edit the newly inserted record.
Save this record using TClientDataSet.ApplyUpdates(-1);
Everything is working as expected as long as I do the 2nd step manually or by editing the data within the AfterInsert event:
__fastcall TFormMain::TFormMain(TComponent* Owner)
: TForm(Owner)
{
ClientDataSet1->Active = true;
}
UnicodeString TFormMain::GenerateGuid( void )
{
// ...some fancy code creating and returning a GUID...
}
void __fastcall TFormMain::ButtonAppendClick(TObject *Sender)
{
ClientDataSet1->Append();
}
void __fastcall TFormMain::ButtonSaveClick(TObject *Sender)
{
ClientDataSet1->ApplyUpdates(-1);
}
void __fastcall TFormMain::ClientDataSet1AfterInsert(TDataSet *DataSet)
{
DataSet->FieldByName( "COL_ID" )->AsString = GenerateGuid();
DataSet->FieldByName( "COL_ACRONYM" )->AsString = "Whatever: this works!";
}
This works good... so far...
Due to some other changes I decided to move the the auto-creation of data into the OnNewRecord event of the TClientDataSet:
void __fastcall TFormMain::ClientDataSet1NewRecord(TDataSet *DataSet)
{
DataSet->FieldByName( "COL_ID" )->AsString = GenerateGuid();
DataSet->FieldByName( "COL_ACRONYM" )->AsString = "Not too good...";
}
For the first moment it looked good, because the DB-controls on the GUI have been filled with the correct data. But as soon as I hit the Save Button the data disappeard and the new record has not been stored to the database - as if I cancelled the process.
Secondly I noticed, that if I change one of the columns MANUALLY before executing ApplyUpdates(),... then the record is stored.
So I simply added the following line for automatic posting:
void __fastcall TFormMain::ClientDataSet1NewRecord(TDataSet *DataSet)
{
DataSet->FieldByName( "COL_ID" )->AsString = GenerateGuid();
DataSet->FieldByName( "COL_ACRONYM" )->AsString = "Not too good...";
DataSet->Post();
}
This minor change did its job.
My question now is: WHY?
Does AfterInsert automatically post the new record?
Are records that were added by Append() automatically cancelled, when they are left unchanged after the OnNewRecord event?
regards and thanx
Herwig

As far as I know, the difference between TDataSet's OnNewRecord and AfterInsert event handlers is:
Editing fields values from the OnNewRecord will NOT flag the record as modified
Editing fields values from the AfterInsert will flag the record as modified
I guess that this is the cause of the problem

Related

Adding new line to top of Grid with data does not display prefilled values in non-editable columns (vaadin7)

Let's say I load data into a Grid. That works perfectly, everything is displayed. I can see it just fine, even after I call editItem(objectId); to edit data for any given line in the Grid.
Then let's say I have a button that adds a new line with mostly empty elements. In other words, the corresponding bean is mostly empty except for some default values. For some reason this behaves weird when I call editItem(objectId);. Here are the situations and their results:
If column is editable (column.setEditable(true);), my default data displays just fine
If column is not editable (column.setEditable(false);), my default data does NOT display. It is definitely in the bean, just not displayed. I see it once I click "Save" or "Cancel" in the edit form.
If I just show the line, do NOT enter the edit form (don't call editItem(objectId);), it display the default data just fine. I mention this just to point out what happens in the "display only" situation.
I even tried making the editField read only, and even that hid the data. So what is happening?
Example of data being displayed (see circled red):
Figure 1: Non-Empty data, but editable column
Example of data NOT being displayed (see circled red):
Figure 2: Empty data inside edit form
Figure 3: Non-Empty data after clicking save.
Note that it does not matter if the column is a ComboBox or just a regular text element, if I make it non-editable, it will not show the value I put in that column on this new line until after I click Save or Cancel.
Here is how I load the list of beans initially, where gridContainer is defined as BeanItemContainer<T> gridContainer:
public void updateList( List<T> dataList, T defaultData ) {
updateList( dataList, defaultData, new GridLoader<T>() {
#Override
public void loadGrid(List<T> dataList) {
gridContainer.removeAllItems();
if( dataList instanceof List && !dataList.isEmpty() )
gridContainer.addAll(dataList);
}
});
}
This works fine for non-editable columns, all data being displayed as expected. My pictures sort of hide it, but it is displaying just fine, and works in edit mode just fine as well.
And here is how I add a new line:
private void addRouting() {
Routing emptyData = Routing.create();
emptyData.setKey(null);
if( facilityId instanceof String && !facilityId.trim().isEmpty() )
emptyData.setFacilityId(facilityId.trim());
if(wmsItem instanceof String && !wmsItem.trim().isEmpty())
{
emptyData.setWmsItem(wmsItem);
// gridComponent.hideColumn("wmsItem", true);
// gridComponent.hideColumn("wmsItemDisplay", false);
}
else
{
// gridComponent.hideColumn("wmsItem", false);
// gridComponent.hideColumn("wmsItemDisplay", true);
}
if(workCenter instanceof String && !workCenter.trim().isEmpty())
{
emptyData.setWorkCenter(workCenter);
// gridComponent.hideColumn("workCenter", true);
// gridComponent.hideColumn("workCenterDisplay", false);
}
else
{
// gridComponent.hideColumn("workCenter", false);
// gridComponent.hideColumn("workCenterDisplay", true);
}
gridComponent.addItemAt(0, emptyData);
gridComponent.select(emptyData);
if(gridComponent.isEditorEnabled())
gridComponent.editItem(emptyData);
}
And in GridComponent, we have addItemAt defined as follows (BTW, GridComponent just wraps a layout with a toolbar at top and a Grid for the data, and so exposes various methods I need from the toolbar and Grid):
public BeanItem<T> addItemAt(int index, T bean) throws IllegalStateException {
BeanItemContainer<T> gridContainer = getGridContainer();
if(gridContainer instanceof BeanItemContainer)
{
/* Clear filter first because adding an item will break this.
* TODO: Maybe restore prior filter with "saveLastFilter();" and then "reapplyLastFilter();" after "add"
*/
saveLastFilter();
clearFilter();
BeanItem<T> newItem = gridContainer.addItemAt(index, bean);
reapplyLastFilter();
return newItem;
}
else
throw new IllegalStateException("Missing bean grid container");
}

How to assign userId on serenity MVC framework

I want to save CreatedBy and LastModifiedBy field on every table. Is there base resulution on serenity?
I am getting error below when i set fld field:
Severity Code Description Project File Line Suppression State
Error CS0029 Cannot implicitly convert type 'int' to 'Serenity.Data.Int32Field'
private static MyRow.RowFields fld { get { return MyRow.Fields; } }
protected override void SetInternalFields()
{
int userId = ((UserDefinition)Authorization.UserDefinition).UserId;
fld.LastModifiedBy = userId;
fld is a reference to your entity fields (metadata), not the entity instance itself.
In SaveHandler, this.Row references to created/updated entity with new values, while this.Old references entity with old values for update (kinda similar to a SQL trigger).
So you should write Row.LastModifiedBy = userId;
FYI, instead of doing it this way in every repository, implement IUpdateLogRow (and/or InsertLogRow) interfaces in your entity and default save behaviors will fill Insert/Update UserId/Date fields automatically.
Define a base row like LoggingRow sample in Serene to avoid having to implement this interface in every entity.

How to dynamically call a form in C++ Builder XE3?

I'm building an application in which Im populating menus using DB. I can create menu items but im having trouble linking "On Click" event to particular forms. I have stored names of the forms classes in my DB and trying to use RTTI to bind them at runtime. Following is the snippet of my code that Im trying to run.
__fastcall TfrmMainMDI::TfrmMainMDI(TComponent *Owner)
: TForm(Owner)
{
// Register 2 form classes
RegisterClass(__classid(TfrmSecurity));
RegisterClass(__classid(TfrmPassword));
}
Now when I try to run following code to call the form it gives "Access violation" error.
TForm *frm = (TForm*)TFormClass(FindClass(formName));
UnicodeString str = frm->Name;
frm->Show();
Do this:
TForm *frm = 0;
Application->CreateForm( TFormClass(FindClass(formName)), &frm );
Then if frm is not null,
frm->Show();
TForm *frm = new TForm(this);
if( frm != NULL )
{
frm->ShowModal();
//or
frm->Show();
}

Breeze-Unreferenced Entity in Manager

I am developing a Single Page App using Hot Towel Template, using breeze...and I have come across a peculiar problem, and I am unable to figure out the internal working which causes it...
I have a Programmes table, and the Programmes table has a foreign key to Responses, so the structure of Programmes is:
Id, ResponseID, Name and Date
and the Page has Name and Date, the foreign comes from RouteData.
and for one ResponseId in Programmes table, I want to save only on Programme.
So, when user comes to this page, it check the Programmes table that if it has an Entry for that particular Response Id, if yes, it goes in Edit case and if not it goes to Add a new entry case.
To achieve this, what I am doing is below:
var objTempProgramme = ko.observable();
var objProgramme = ko.observable();
function activate(routeData) {
responseId = parseInt(routeData.responseId);
// Create a Programme Entity
objProgramme(datacontext.createProgramme());
// Fill in a Temporary Observable with Programmes data
datacontext.getEntitiesDetailsByRelativeId('responseID', responseId , 'Programmes', objTempProgramme, true).then(function(){
// Check if Programmes Exists
if (objTempProgramme() != null && objTempProgramme() != undefined) {
// What I am doing here is filling my Programmes Entity with data coming from database (if it is there)
objProgramme(objTempProgramme());
} else {
// The Else Part assigns the Foreign Key (ResponseId) to my Entity Created above
objProgramme().responseID(responseId);
}
});
}
In datacontext.js:
var createProgramme = function () {
return manager.createEntity(entityNames.programme);
}
var getEntitiesDetailsByRelativeId = function (relativeIdName, relativeId, lookupEntity, observable, forceRefresh) {
var query = entityQuery.from(lookupEntity).where(relativeIdName, "==", relativeId);
return executeGetQuery(query, observable, forceRefresh);
};
Now when I call manager.saveChanes on my page, I would Expect objProgramme to be saved, in any case, be it edit or be it save,
but what breeze is doing here is that though it is filling objTempProgramme in objProgramme, but it is also leaving the entity objProgramme unreferenced with its manager, so that when I call save, it tries to save that entity too (2 entities in total, objProramme and one unreferenced one), but that entity does not have foreign key set and it fails..but my question is why?
Assigning one entity to another does not mean all its properties get assigned to another? And why is that unreferenced entity present?

java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#

I've read several related posts and even posted and answer here but it seems like I was not able to solve the problem.
I have 3 Activities:
Act1 (main)
Act2
Act3
When going back and forth Act1->Act2 and Act2->Act1 I get no issues
When going Act2->Act3 I get no issues
When going Act3->Act2 I get occasional crashes with the following error: java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#.... This is a ListView cursor.
What I tried:
1. Adding stopManagingCursor(currentCursor);to the onPause() of Act2 so I stop managing the cursor when leaving Act2 to Act3
protected void onPause()
{
Log.i(getClass().getName() + ".onPause", "Hi!");
super.onPause();
saveState();
//Make sure you get rid of the cursor when leaving to another Activity
//Prevents: ...Unable to resume activity... trying to requery an already closed cursor
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
stopManagingCursor(currentCursor);
}
When returning back from Act3 to Act2 I do the following:
private void populateCompetitorsListView()
{
ListAdapter currentListAdapter = getListAdapter();
Cursor currentCursor = null;
Cursor tournamentStocksCursor = null;
if(currentListAdapter != null)
{
currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
if(currentCursor != null)
{
//might be redundant, not sure
stopManagingCursor(currentCursor);
// Get all of the stocks from the database and create the item list
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
((SimpleCursorAdapter)currentListAdapter).changeCursor(tournamentStocksCursor);
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
startManagingCursor(tournamentStocksCursor);
//Create an array to specify the fields we want to display in the list (only name)
String[] from = new String[] {StournamentConstants.TblStocks.COLUMN_NAME, StournamentConstants.TblTournamentsStocks.COLUMN_SCORE};
// and an array of the fields we want to bind those fields to (in this case just name)
int[] to = new int[]{R.id.competitor_name, R.id.competitor_score};
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter tournamentStocks = new SimpleCursorAdapter(this, R.layout.competitor_row, tournamentStocksCursor, from, to);
//tournamentStocks.convertToString(tournamentStocksCursor);
setListAdapter(tournamentStocks);
}
So I make sure I invalidate the cursor and use a different one. I found out that when I go Act3->Act2 the system will sometimes use the same cursor for the List View and sometimes it will have a different one.
This is hard to debug and I was never able to catch a crashing system while debugging. I suspect this has to do with the time it takes to debug (long) and the time it takes to run the app (much shorter, no pause due to breakpoints).
In Act2 I use the following Intent and expect no result:
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Intent intent = new Intent(this, ActivityCompetitorDetails.class);
intent.putExtra(StournamentConstants.App.competitorId, id);
intent.putExtra(StournamentConstants.App.tournamentId, mTournamentRowId);
startActivity(intent);
}
Moving Act1->Act2 Act2->Act1 never gives me trouble. There I use startActivityForResult(intent, ACTIVITY_EDIT); and I am not sure - could this be the source of my trouble?
I would be grateful if anyone could shed some light on this subject. I am interested in learning some more about this subject.
Thanks,D.
I call this a 2 dimensional problem: two things were responsible for this crash:
1. I used startManagingCursor(mItemCursor); where I shouldn't have.
2. I forgot to initCursorAdapter() (for autocomplete) on onResume()
//#SuppressWarnings("deprecation")
private void initCursorAdapter()
{
mItemCursor = mDbHelper.getCompetitorsCursor("");
startManagingCursor(mItemCursor); //<= this is bad!
mCursorAdapter = new CompetitorAdapter(getApplicationContext(), mItemCursor);
initItemFilter();
}
Now it seems to work fine. I hope so...
Put this it may work for you:
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
orderCursor.requery();
}
This also works
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
startManagingCursor(Cursor);
}

Resources