I wanted to create a new mdb file containing tables based on the structure of previously existing tables. I knew that I can use newTable.FieldDefs.Add()
to recreate the fields one by one in a loop. But since there is already an oldTable fully stocked with the correct FieldDefs
, that seemed terribly inelegant. I was looking for a single-statement solution!
I had found that newTable.FieldDefs.Assign(oldTable.FieldDefs)
would compile (and run) without error but it left newTable
with zero defined fields. This caused me to erroneously conclude that I didn't understand that statement's function. (Later I found that it failed only when oldTable.open
had not occurred, which could not happen when the database was not available, even though the FieldDefs
had been made Persistent and were clearly visible in the Object Inspector)
Here is my original code after some sleuthing:
procedure TForm2.Button1Click(Sender: TObject);
var
fname: string;
Table: TFDTable;
FDConn: TFDConnection;
begin
fname := 'C:\ProgramData\mymdb.mdb';
if FileExists(fname) then DeleteFile(fname);
{ Make new file for Table }
FDMSAccessService1.Database := fname;
FDMSAccessService1.DBVersion := avAccess2000;
FDMSAccessService1.CreateDB;
{ Connect to new file }
FDConn := TFDConnection.Create(nil);
FDConn.Params.Database := fname;
FDConn.Params.DriverID := 'MSAcc';
FDConn.Connected := true;
{ Set up new Table using old table's structure }
Table := TFDTable.Create(nil);
try
{ ADOTable1 has been linked to an existing table in a prior
database with Field Defs made Persistent using the Fields
Editor in the Object Inspector. That database will not be
available in my actual use scenario }
try
ADOTable1.open; // Throws exception when database file not found
except
end;
Table.Connection := FDConn;
{ specify table name }
Table.TableName := ADOTable1.TableName;
Table.FieldDefs.Assign(ADOTable1.FieldDefs); // No errors reported
ShowMessageFmt('New Table %s has %d fields',[Table.TableName,
Table.FieldDefs.Count]);
{ Reports correct TableName but "0 fields" with table not open
(i.e. file not found). Reports "23 fields" with table open }
{ Set Table definition into new mdb file }
Table.CreateTable(False); // Throws exception when 0 fields found
finally
Table.Free;
end;
end;
It turned out that the solution was to use a ClientDataSet originally linked to the same old database instead of the ADOTable. See the working solution below in my answer.
Edit: A final note. I had hoped to use this FireDAC approach, as indicated here, to get around the lack of a TADOTable.CreateTable
method. Alas, although the "solutions" above and below do work to create a new TADOTable
, that table's field definitions are not faithful replicas of the original table. There may be a combination of the myriad TFDTable
options that would get around this, but I was not able to discover it so I reverted to creating my ADO tables with SQL.