Link to home
Start Free TrialLog in
Avatar of GrahamDLovell
GrahamDLovell

asked on

clientdataset.saveToStream problems

I use clientdataset.LoadFromStream to lock out other users from the file / table.

Is there a way to do the simpler, LoadFromFile, and lock out other users, by setting a parameter, or similar?
Avatar of Sinisa Vuk
Sinisa Vuk
Flag of Croatia image

You want to lock table/row(s) in database where you connected?
Avatar of GrahamDLovell
GrahamDLovell

ASKER

Yes. I cannot just lock rows, since I am using the client data set as my "database", so I have to load the whole table, and then save it completely.

To lock the file, I use TStream, which gives you the option to lock the file in various modes.

I would be happy with this method if SaveToStream worked, but it didn't seem to do so.

I have tried various methods, and my final workaround was to open as a Stream, and then save as file (after freeing the stream), but this seems to be a bit outside of the documentation.

If I could avoid TSteam altogether (if there is an appropriate option in the LoadFromFile method), I would be happy. I would also be happy if I could get TStream to work as expected.
Instead of using the VCL TClientDataset, there is another alternative.  I use TKBMemTable in my product line and have never had any issues with it.

http://www.components4programmers.com/products/kbmmemtable/
Thanks for your suggestion, but I would rather resolve my issue with the native code than move to a 3rd party product.
what database type is this ?

locking a whole table is not required in most cases

If you use clientdataset on a file ...
i'm not sure that is multi-user safe.

probably not a good idea to work like that in a multi-user environment
The database is a Tclientdataset, saved as a file.

Yes, I agree it is not multi-user safe. However, it is safe in this situation, as I only want one user to use the file. I just want to make sure he (or she) cannot open it again while that user has it open elsewhere.
I have discovered my problem relates to a long-standing (not fixed) bug in TClientDataSet.SaveToStream.

The method calls WriteDataPacket(TStream,false,Format). It should call WriteDataPacket(TStream,true,Format), since "false" stops it writing to the stream.

Nils van der Mee has a workaround at http://www.delphigroups.info/2/67/461553.html, which involves creating a descendant procedure that sends the correct WriteDataPacket command. However, I am not sure how to implement this so that I can simply call it for all my existing cds tables.

Can I write a procedure that extends TClientDataSet, so that it just operates as an additional procedure for that class?
I have found that it is not hard to add a class helper to do this, but I don't think that Nils' post represents a solution to my problem. I have tried it, and it corrupts my data.

Back to the drawing board.
a file based database has one very big problem in a multi-user (multiple use) environment
> severed network connections

you need a system which keeps track of who is connected
and allows use of resources when they are free.

this comes as part of any rdbms system

clientdataset simply doesn't have this
Geert

Thank you for your interest in my problem.

My application is definitely single-user, and I won't be going to a rmbs. I don't need the complication of requiring this to be set and managed, when it is NOT needed for my application. Why should I? It was bad enough with the BDE, when you had to cope with existing installations, etc., and the support complications associated with a tiny software fee. The TClientDataSet was a break through for me in installation and running simplicity.

I have tried every combination of things that I can think of to get SaveToStream to work, but it won't work.

I have a workaround - I will use that, since either no-one has had the same problem, or they have given up, as I am about to do.
in your original question statement you noted this:
to lock out other users

this means you are in a multi-user environment.

you could go the old (dinosaur) way (like paradox used to do)
add a secondary file: file.lck
if the file exists, then someone else is accessing the data

keep the file open with open exclusive deny everything to any else
since you would manage this file yourself you would have total control over it.

if it's impossible to tackle a specific problem, a workaround can be a solution.
My original question was imprecise. I want to lock out a single user from opening another application and damaging the data. The design should not allow "another user" access (but it could be done by a dummy with Administrator rights). I was more concerned about the program being instantiated twice (even with a different program name) by the same user.

Will you believe me now? Single User - not multiple-user.

The file.lck was an administrative problem under BDE in the event of a crash, since it was left open. TStream is easier to administer.

It looks like a work-around is required.
I use this code for single instance - put in .dpr source file
..
var
  Mutex : THandle;
  bAppRunning: Boolean;

begin
  Mutex := CreateMutex(nil, True, 'UNIQUE_MUTEX_STRING_4_MY_APP');
  bAppRunning:= (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS);
  if not bAppRunning then
  begin
    Application.Initialize;
    Application.Title:=....
    Application.CreateForm(....);
    Application.Run;
  end;

Open in new window


This one will not allow to run second instance of application.
... similar code using Mutexes can be used for other lock things.
Thanks for the info on Mutex, however, TStream does the task I want done quite adequately.

Further investigation has shown that I can create a new application with the following code:
  StreamData := TFileStream.create('chapters.cds',
      fmOpenReadWrite or fmShareExclusive);
  cds.LoadFromStream(StreamData);
  cds.SaveToStream(StreamData);

Open in new window

  StreamData := TFileStream.create('chapters.cds',
      fmOpenReadWrite or fmShareExclusive);
  cds.LoadFromStream(StreamData);
  cds.SaveToStream(StreamData);

Open in new window

StreamData := TFileStream.create('chapters.cds',
      fmOpenReadWrite or fmShareExclusive);
  cds.LoadFromStream(StreamData);
  cds.SaveToStream(StreamData);

It works fine, but when I use what I think is the same code in my larger (multi-unit) form, it doesn't work. That is, it does not write the file to disk.

Interestingly, in the small program, I can drill down in the code (while running, using F7) to the DataSnap.DBClient procedure TCustomClientDataSet.SaveToStream(Stream: TStream; Format: TDataPacketFormat = dfBinary); and see it in operation.

However, in the larger program, I cannot drill down (using F7) to expose that procedure and to watch it in operation.
ASKER CERTIFIED SOLUTION
Avatar of GrahamDLovell
GrahamDLovell

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
My original question was imprecise, which created some difficulties, for which I apologise. Other solutions were work-arounds. Only this fix actually solved the "clientdataset.saveToStream problem".