26

According to this page, it's possible to use TClientDataset as an in-memory dataset, completely independent of any actual databases or files. It describes how to setup the dataset's table structure and how to load data into it at runtime. But when I tried to follow its instructions in D2009, step 4 (table.Open) raised an exception. It said that it didn't have a provider specified.

The entire point of the example on that page is to build a dataset that doesn't need a provider. Is the page wrong, is it outdated, or am I missing a step somewhere? And if the page is wrong, what do I need to use instead to create a completely independent in-memory dataset? I've been using TJvMemoryData, but if possible I'd like to reduce the amount of extra dependencies that my dataset adds into my project.

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477

11 Answers11

25

At runtime you can use table.CreateDataset or if this is on a design surface you can right click on the CDS and click create dataset. You need to have specified columns/types for the CDS before you can do this though.

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
MikeJ
  • 14,430
  • 21
  • 71
  • 87
23

If it helps further, here is a piece of code where I created a ClientDataset that is used as an in-memory table:

procedure TfrmPRMain.ConfigureDataset;
begin
  With cdsMain do begin
    FieldDefs.Add('bDelete', ftBoolean);
    FieldDefs.Add('sSource', ftString, 10);
    FieldDefs.Add('iSection', ftInteger);
    FieldDefs.Add('iOrder', ftInteger);
    FieldDefs.Add('sBranch', ftString, 10);
    FieldDefs.Add('sPulseCode', ftString, 10);
    FieldDefs.Add('sCode', ftString, 10);
    FieldDefs.Add('dtWorkDate', ftDate);
    FieldDefs.Add('iWorkWeek', ftInteger);
    FieldDefs.Add('sName', ftString, 50);
    CreateDataSet;
    LogChanges := False;
    Open;
  end;
end;

You can just substitute your own data information and go. Jack

jrodenhi
  • 2,237
  • 1
  • 21
  • 31
9

Don't forget to include MIDAS.DLL in your installation or simply include MidasLib in uses clause. Otherwise using TClientDataSet will raise an error on client's machine. Maybe it's obvious, but I actually forgot this once.

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
vrad
  • 836
  • 1
  • 7
  • 16
7

You can use table.CreateDataSet

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
Tom
  • 1,381
  • 3
  • 15
  • 26
6

The code from this page doesn't work in ANY Delphi version. A call to CreateDataSet already puts the dataset into active state ("opened"). You should use .CreateDataSet OR .Open. Not both.

Use .Open when you want to fetch data from a Provider (via ProviderName property) and .CreateDataSet when you want to populate the Dataset by yourself.

BTW: For an in depth reference about ClientDataSets and its features take a look on excellent Cary Jensen articles on CodeGear Developer Network (read the oldest ones, first)

F.D.Castel
  • 2,284
  • 1
  • 16
  • 15
3

If you'd like a dependency-free, high quality, and feature rich (not to mention free!) in-memory dataset, I highly recommend kbmMemTable. Does everything TClientDataset does and then some.

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
Tim Sullivan
  • 16,808
  • 11
  • 74
  • 120
1

for some reason this is not working for me. I perform CreateDataset in design time, but it still crashes application. That is still unknown for me. One warning. DO NOT DO THIS:

XXXClientDataSet.Close;
XXXClientDataSet.Open;

because it will report error. Instead of Open, use

xxxClientDataSet.CreateDataset;

In my application I needed to reset data and load it again, and that again caused error message.

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
1

Graveyard stones below for some libre components

In times of Delphi 5/ Delphi 7 there were initiatives to make any object with published properties (more accurately - array or some collection of those) into a database. On Torry.net those are CollectionDataSet and Object DataSet Years before LINQ and such. But since DB-VCL code is little documented and is spaghetti since 16-bit Delphi 1.0 - those have no development.

There is also callback-based (events-based) Snap Object Dataset, not so outdated. Though it leaves too much IMHO on developers shoulders.

TDBF.sf.net table had in-memory mode, but was early removed. TDBF is dead also.

rxLib/JediVCL has MemoryDataset. Though rxLib target was source-level compatibility since 16-bit Delphi 1 up to Delphi 5. That crippled the code much. In JVCL it had some attention and removing of aging code, but still is half-baked when needed some deeper than trivial usage.

There are also free-for-personal DCU components like SQLMemoryTable, not for recent releases though. I wonder if Firebird Embedded / SQLite could be used to create in-memory table without using system-wide hacks like RAMdrive :-)

Arioch 'The
  • 15,799
  • 35
  • 62
  • Did not research the version of SQLite required, but SQLite makes an excellent in-memory database. Just open the database using the filename ':memory:' [see reference page](https://www.sqlite.org/inmemorydb.html) for more details and uses. Heavy overhead for in-memory dataset, but very feature-heavy and trivial to change to a disk-based version which is great for debugging with the data actually stored. – Gary Walker May 09 '23 at 18:24
  • @GaryWalker AnyDAC (today - FireDAC) exacty has the `sqlite` backend for their memory table. Not sure if it was a good idea though. They tend to step over their own bootstraps and trip over. When you do things like `.Edit`/`.Post`/`.Delete` they copy the data from the `TDataset` into SQLite and back, and occasionally loose track what data still exists and what is no more... Also, there is a commercial Delphi-native `NexusDB` component. – Arioch 'The May 11 '23 at 11:42
0

This is a corrected working code mentioned by OP in the first post. You get a memory table from a TClientDataset shown in DBGrid.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, DBClient, Grids, DBGrids, StdCtrls, MidasLib;

type
  TForm1 = class(TForm)
    MemTable: TClientDataSet;
    Button1: TButton;
    Button2: TButton;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  i: word;
begin
  MemTable.DisableControls;
  for i := 1 to 20000 do
  begin
    MemTable.Append;
    MemTable.FieldByName('ID').AsInteger       := i;
    MemTable.FieldByName('Status').AsString    := 'Code'+IntToStr(i);
    MemTable.FieldByName('Created').AsDateTime := Date();
    MemTable.FieldByName('Volume').AsFloat     := Random(10000);
    MemTable.Post;
  end;
  MemTable.EnableControls;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MemTable.IndexFieldNames := 'Volume';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MemTable.FieldDefs.Add('ID',      ftInteger, 0, False);
  MemTable.FieldDefs.Add('Status',  ftString, 10, False);
  MemTable.FieldDefs.Add('Created', ftDate,    0, False);
  MemTable.FieldDefs.Add('Volume',  ftFloat,   0, False);
  MemTable.CreateDataSet;
end;

end.
Fabrizio
  • 7,603
  • 6
  • 44
  • 104
avra
  • 3,690
  • 19
  • 19
0

For me this was caused by a midas.dll mismatch. I fixed it by adding MidasLib to the main program´s uses clause (thus linking the library statically). More info here: http://codeverge.com/embarcadero.datasnap/tclientdataset-createdataset-failing-wit/1097715 and here: http://edn.embarcadero.com/article/29297

Ludecan
  • 331
  • 4
  • 13
0

My preference is to actually manage the dataset as XML. You can use the designer tools to create the basic structure and then save it to disk. This allows it to be managed outside of the executable, compiled in as a resource, or separately managed in version control.

When doing it in this manner you can use LoadFromFile/Stream and the Save variants. Remember to make proper use of LogChanges and MergeChangeLog depending on your usage.