I need to create a tool for performing complex scripts against a database.
For several resasons I cannot rely on DB transactional behaviour, but I need to implement my own transactional system.
The approach I am trying is with the help of the command pattern (my case is more complex, here I put a simplified version for discussion):
type
IMyCommand = interface(IInterface)
procedure Execute();
procedure Undo();
end;
type
TSQLCommand = class (TInterfacedObject, IMyCommand)
private
FDBConnection: TDBConnection;
FDBQuery: TDBQuery;
FExecuteSQL: string;
FUndoSQL: string;
FExecuted: boolean; // set to True as the command has been executed
public
procedure Execute;
procedure Undo;
procedure Prepare(aExecuteSQL, aUndoSQL: string);
constructor Create(aDBConnection: TDBConnection);
destructor Destroy; override;
end;
I create a set of actions, for every action I will pass a "Execute" and "Undo" sql statement, examples:
A call to Prepare could be:
Prepare('INSERT INTO TESTTABLE (ID, DATA) VALUES (15, 'Hello')',// aExecuteSQL
'DELETE FROM TESTTABLE WHERE ID = 15'); //aUndoSQL
so somehow I am making very small changes (like inserting a single simple row, updating a single row, ...), for every change the "undo" is very obvious.
I will prepare a stack of command objects (using probably the TObjectStack collection), and call the Execute method one command at a time and as one is executed I will set FExecuted to True, and save the component to disk.
So what I whant to do is to run all the scripts, but I want to manage the cases in which something goes wrong.
If something goes wrong I would like to execute all the commands from last to first calling the Undo method. Of course before doing this I need to be able to restore from disk the components (in case the failure is an hardware failure, in case the failure is another reason I already have the stack in memory and I can easily call undo one command at a time).
Note: The main reason why I cannot rely on the DB transactional behaviour is that I need to insert also big blobs, and every blob is downloaded from internet and then inserted, so I cannot leave a transaction open for ever because I want to commit every small change to the db. What I do with blobs is download one, insert it, download next, insert it, ...
So my question is: could you suggest a way to persist to disk my objects? I have Delphi 2009, so one option is to make a TInterdacedPersistent and save the component to stream and then to file, anyway in this way I would have many files, with extra complicatinos, while I would prefer a single file. Could you suggest?
Edit: I realized TObjectStack is buggy in Delphi 2009 (Pop doesn't return a correct type), so the same can be done with TObjectStack.
I can't see a better approach than using a transaction, as Andrei K. mentioned your implementation is NOT safe, therefore using StartTransaction, Commit and Rollback is a MUST!
Related
Using Delphi Berlin.
I have a nested Clientdataset in a datamodule ("dmCore").
There are about 5000 records in the detail table for any given master item (testing with 2 master records).
I have a "Post" button connected to an action in ActionManager.
Its OnUpdate is simple:
actPost.Enabled:=dmCore.HasChanges;// checks master for changes
"HasChanges" is simple:
function TdmCore.HasChanges: boolean;
begin
result := False;
if cdsPSet.Active then
result:=(cdsPSet.ChangeCount>0);
end;
Unfortunately, having CDS.ChangeCount run in the action's onUpdate is taking up huge CPU time (>50%).
I haven't noticed this happen on non-nested CDS...
Is there a simpler (faster) mechanism I can use to see if the CDS has changed? I don't need the count, just the fact that there's a change somewhere.
TIA
EdB
Try the UpdatesPending property. I can't say it will be faster, but it's the way to detect if there were any changes in the dataset. So in your case you could actually write just:
function TdmCore.HasChanges: boolean;
begin
Result := cdsPSet.UpdatesPending;
end;
The action's OnUpdate event is not the best place to toggle its enabled/disabled status. It is fired all the time.
You might want to move it to the TDataSource.OnDataChanged event. There you can insert the same code as in TAction.OnUpdate and it is triggered only when your data really changed. You can also use the TField parameter to separate between different fields.
Example (sorry I only got C++ Builder, but in Delphi it looks the same):
void __fastcall TForm1::DataSource1DataChange(TObject *Sender, TField *Field)
{
Action1->Enabled = ClientDataSet1->ChangeCount > 0;
}
Implement your own invalidation mechanism. This is as simple as a single Boolean variable which, when it's True, it means something has changed, and when it's False, it means nothing has changed. You would then be responsible for updating this for every change and action you make on this dataset.
If it were me, I'd create an inherited class, override everything, and implement an invalidation mechanism. It would be much lighter than the existing methods. Of course it wouldn't be as easy to use in design-time, but it would get the job done in run-time.
so I was just looking through the Code of our inventory management system
and I saw some Snippet's of one of my Coworkers, all he does in some Functions is
simply open them and insert a command in there for example
procedure TWerkStF.TBBtnStatiClick(Sender: TObject);
begin
inherited;
//
end;
so i am wondering when you should do something like that and when is it usefull, are there some benefits?
This is not useful, and has no benefits. Such a function can, and should, be removed.
The function in the question, by using inherited, simply searches in the super classes for a function of the same name, and if one is found calls it. If one is not found then no action is performed. As a rule, such a function, one that only calls inherited, does not modify the behaviour of the program. You can remove it without changing behaviour.
We use such functions/procedures most times to tell other Co-Workers where they should not change anything, the reason for that is because some events are just not fitting the needs of some Modules, therefore we "warn" each other with empty Functions.
Sounds wiered, i know.
I have a set of tables that were included in an Advantage Database data dictionary. The dictionary is no longer available, and the tables will not open.
I would like to free those tables using code (not the Advantage Data Architect).
The only reference I can find to this is a function listed in the help called ADSDDFreeTable.
The documentation for the function is at this link:
http://devzone.advantagedatabase.com/dz/WebHelp/Advantage11.1/index.html?ace_adsddfreetable.htm
but it does not offer a code sample, and I cannot understand how to use it.
Would anyone be kind enough to show a code sample of how this function is used (with variables, not literals, for file names, etc)
Thanks very much!
Ace.pas defines AdsDDFreeTable as
function AdsDDFreeTable( pucTableName: PAceChar;
pucPassword: PAceChar ):UNSIGNED32; {$IFDEF WIN32}stdcall;{$ENDIF}{$IFDEF LINUX}cdecl;{$ENDIF}
The same Ace.pas defines PAceChar:
type
PAceChar = PAnsiChar;
Therefore, the call to the function should be fairly straightforward:
var
TableName: AnsiString;
begin
TableName := 'C:\Data\MyTable.adt`;
if AdsDDFreeTable(PAnsiChar(TableName), nil) <> ADS_FREETABLEFAILED then
ShowMessage('Table removed from datadictionary')
else
// Call ADSGetLastError to retrieve reason for failure;
end;
In addition to #Ken's solution (+1), there is also a standalone command line utility named freeadt.exe that will free ADT tables from their associated data dictionary. I believe it is installed with Advantage Data Architect.
If you run it from the command line with no parameters, it displays usage information. In general, though, you can give it a folder name (to process all the tables) or a specific file as a parameter.
This is similar to this question. I asked "Why?" to the most popular response but I don't know that anyone would ever look at it again. At least not in any timely manner.
Anyway, my question is about best practices for delegating responsibility for creation of objects to functions or procedures, without causing memory leaks. It seems that this:
procedure FillObject(MyObject: TMyObject; SomeParam: Integer);
begin
//Database operations to fill object
end;
procedure CallUsingProcedure();
var
MyObject: TMyObject;
begin
MyObject = TMyObject.Create();
try
FillObject(MyObject, 1);
//use object
finally
MyObject.Free();
end;
end;
is preferred over this:
function CreateMyObject(DBID: Integer): TMyObject;
begin
Result := TMyObject.Create();
try
//Database operations to fill object
except on E: Exception do
begin
Result.Free();
raise;
end;
end;
end;
procedure CallUsingFunction();
var
MyObject: TMyObject;
begin
MyObject = CreateMyObject(1);
try
//use object
finally
MyObject.Free();
end;
end;
Why?
I'm relatively new to Delphi, having previously worked most with Java and PHP, as well as C++, though to a lesser extent. Intuitively, I lean toward the function method because:
It encapsulates the object creation code in the function, rather than create the object separately whenever I want to use the procedure.
I dislike methods that alter their parameters. It's often left undocumented and can make tracing bugs more difficult.
Vague, but admittedly it just "smells" bad to me.
I'm not saying I'm right. I just want to understand why the community chooses this method and if there is good reason for me to change.
Edit:
References to #E-Rock in comments are to me(Eric G). I changed my display name.
One problem is what Ken White wrote: you hand the user of the function an object he or she must free.
Another advantage of procedures is that you can pass several objects of a hierarchy, while a function that creates such an object always generates the same. E.g.
procedure PopulateStrings(Strings: TStrings);
To that procedure, you can pass any kind of TStrings, be it the Lines of a TMemo, the Items of a TListBox or TComboBox or a simple standalone TStringList. If you have a function:
function CreateStrings: TStrings;
You always get the same kind of object back (which object exactly is not known, as TStrings is abstract, so you probably get a TStringList), and must Assign() the contents to the TStrings you want to modify. The procedure is to be preferred, IMO.
Additionally, if you are the author of the function, you can't control whether the object you create is freed, or when. If you write a procedure, that problem is taken off your hands, since the user provides the object, and its lifetime is none of your concern. And you don't have to know the exact type of the object, it must just be of the class or a descendant of the parameter. IOW, it is also much better for the author of the function.
It is IMO seldom a good idea to return an object from a function, for all the reasons given. A procedure that only modifies the object has no dependency on the object and creates no dependency for the user.
FWIW, Another problem is if you do that from a DLL. The object returned uses the memory manager of the DLL, and also the VMT to which it points is in the DLL. That means that code that uses as or is in the user code does not work properly (since is and as use the VMT pointer to check for class identity). If the user must pass an object of his, to a procedure, that problem does not arise.
Update
As others commented, passing an object to a DLL is not a good idea either. Non-virtual functions will call the functions inside the DLL and use its memory manager, which can cause troubles too. And is and as will not work properly inside the DLL either. So simply don't pass objects into or out of a DLL. That goes with the maxime that DLLs should only use POD type parameters (or compound types -- arrays, records -- that only contain POD types) or COM interfaces. The COM interfaces should also only use the same kind of parameters.
Creating the object instance and passing it into another procedure makes it clear which code is responsible for freeing the instance.
In the first case (using a procedure to fill it):
MyObj := TMyObject.Create;
try
// Do whatever with MyObj
finally
MyObj.Free;
end;
This is clear that this block of code is responsible for freeing MyObj when it's finished being used.
MyObj := CreateMyObject(DBID);
What code is supposed to free it? When can you safely free it? Who is responsible for exception handling? How do you know (as a user of someone else's code)?
As a general rule, you should create, use, and free object instances where they're needed. This makes your code easier to maintain, and definitely makes it easier for someone who comes along later and has to try and figure it out. :)
I use a combination of both idioms. Pass the object as an optional parameter and if not passed, create the object. And in either case return the object as the function result.
This technique has (1) the flexibility of the creation of the object inside of the called function, and (2) the caller control of the caller passing the object as a parameter. Control in two meanings: control in the real type of the object being used, and control about the moment when to free the object.
This simple piece of code exemplifies this idiom.
function MakeList(aList:TStrings = nil):TStrings;
var s:TStrings;
begin
s:=aList;
if s=nil then
s:=TSTringList.Create;
s.Add('Adam');
s.Add('Eva');
result:=s;
end;
And here are three different ways to use it
simplest usage, for quick and dirty code
var sl1,sl2,sl3:TStrings;
sl1:=MakeList;
when programmer wants to make more explicit ownership and/or use a custom type
sl2:=MakeList(TMyStringsList.create);
when the object is previously created
sl3:=TMyStringList.Create;
....
MakeList(sl3);
I'm asking this because I'm out of good ideas...hoping for someone else's fresh perspective.
I have a user running our 32-bit Delphi application (compiled with BDS 2006) on a Windows 7 64-bit system. Our software was "working fine" until a couple weeks ago. Now suddenly it isn't: it throws an Access Violation while initializing (instancing objects).
We've had him reinstall all our software--starting all over from scratch. Same AV error. We disabled his anti-virus software; same error.
Our stack tracing code (madExcept) for some reason wasn't able to provide a stack trace to the line of the error, so we've sent a couple error logging versions for the user to install and run, to isolate the line which generates the error...
Turns out, it's a line which instances a simple TStringList descendant (there's no overridden Create constructor, etc.--basically the Create is just instancing a TStringList which has a few custom methods associated with the descendant class.)
I'm tempted to send the user yet another test .EXE; one which just instances a plain-vanilla TStringList, to see what happens. But at this point I feel like I'm flailing at windmills, and risk wearing out the user's patience if I send too many more "things to try".
Any fresh ideas on a better approach to debugging this user's problem? (I don't like bailing out on a user's problems...those tend to be the ones which, if ignored, suddenly become an epidemic that 5 other users suddenly "find".)
EDIT, as Lasse requested:
procedure T_fmMain.AfterConstruction;
begin
inherited;
//Logging shows that we return from the Inherited call above,
//then AV in the following line...
FActionList := TAActionList.Create;
...other code here...
end;
And here's the definition of the object being created...
type
TAActionList = class(TStringList)
private
FShadowList: TStringList; //UPPERCASE shadow list
FIsDataLoaded : boolean;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
procedure DataLoaded;
function Add(const S: string): Integer; override;
procedure Delete(Index : integer); override;
function IndexOf(const S : string) : Integer; override;
end;
implementation
procedure TAActionList.AfterConstruction;
begin
Sorted := False; //until we're done loading
FShadowList := TStringList.Create;
end;
I hate these kind of problems, but I reckon you should focus on what's happening recently BEFORE the object tries to get constructed.
The symptoms you describe sound like typical heap corruption, so maybe you have something like...
An array being written to outside bounds? (turn bounds checking on, if you have it off)
Code trying to access an object which has been deleted?
Since my answer above, you've posted code snippets. This does raise a couple of possible issues that I can see.
a: AfterConstruction vs. modified constructor:
As others have mentioned, using AfterConstruction in this way is at best not idiomatic. I don't think it's truly "wrong", but it's a possible smell. There's a good intro to these methods on Dr. Bob's site here.
b: overridden methods Add, Delete, IndexOf
I'm guessing these methods use the FshadowList item in some way. Is it remotely possible that these methods are being invoked (and thus using FShadowList) before the FShadowList is created? This seems possible because you're using the AfterConstruction methods above, by which time virtual methods should 'work'. Hopefully this is easy to check with a debugger by setting some breakpoints and seeing the order they get hit in.
You should never override AfterConstruction and BeforeDestruction methods in your programs. They are not meant for what you're doing with them, but for low-level VCL hacking (like reference adding, custom memory handling or such).
You should override the Create constructor and Destroy destructor instead and put your initialization code here, like such:
constructor TAActionList.Create;
begin
inherited;
// Sorted := False; // not necessary IMHO
FShadowList := TStringList.Create;
end;
Take a look at the VCL code, and all serious published Delphi code, and you'll see that AfterConstruction and BeforeDestruction methods are never used. I guess this is the root cause of your problem, and your code must be modified in consequence. It could be even worse in future version of Delphi.
Clearly there is nothing suspicious about what TAActionList is doing at time of construction. Even considering ancestor constructors and possible side-effects of setting Sorted := False indicate there shouldn't be a problem. I'm more interested in what's happening inside T_fmMain.
Basically something is happening that causes FActionList := TAActionList.Create; to fail, even though there is nothing wrong in the implementation of TAActionList.Create (a possibility is that the form may have been unexpectedly destroyed).
I suggest you try changing T_fmMain.AfterConstruction as follows:
procedure T_fmMain.AfterConstruction;
begin
//This is safe because the object created has no form dependencies
//that might otherwise need to be initialised first.
FActionList := TAActionList.Create;
//Now, if the ancestor's AfterConstruction is causing the problem,
//the above line will work fine, and...
inherited AfterConstruction;
//... your error will have shifted to one of these lines here.
//other code here
end;
If an environment issue with a component used by your form is causing it destroy the form during AfterConstruction, then it's the assignment of the new TAActionList.Create instance to FActionList that's actually causing the AV. Another way to test would be to first create the object to a local variable, then assign it to the class field: FActionList := LActionList.
Environment problems can be subtle. E.g. We use a reporting component which we discovered requires that a printer driver is installed, otherwise it prevents our application from starting up.
You can confirm the destruction theory by setting a global variable in the form's destructor. Also you may be able to output a stack trace from the destructor to confirm the exact sequence leading to the destruction of the form.
Our software was "working fine" until a couple weeks ago... suddenly become an epidemic that 5 other users suddenly "find".) :
Sounds like you need to do some forensic analysis, not debugging: You need to discover what changed in that user's environment to trigger the error. All the more so if you have other users with the same deployment that don't have the problem (sounds like that's your situation). Sending a user 'things to try' is one of the best ways to erode user confidence very quickly! (If there is IT support at the user site, get them involved, not the user).
For starters, explore these options:
*) If possible, I'd check the Windows Event Log for events that may have occurred on that machine around the time the problem arose.
*) Is there some kind of IT support person on the user's side that you can talk to about possible changes/problems in that user's environment?
*) Was there some kind of support issue/incident with that user around the time the error surfaced that may be connected to it, and/or caused some kind of data or file corruption particular to them?
(As for the code itself, I agree with #Warran P about decoupling etc)
Things to do when MadExcept is NOT Enough (which is rare, I must say):
Try Jedi JCL's JCLDEBUG instead. You might get a stack traceback with it, if you change out MadExcept for JCLDEBUG, and write directly the stack trace to the disk without ANY UI interaction.
Run a debug-viewer like MS/SysInternals debugview, and trace output things like the Self pointers of the objects where the problems are happening. I suspect that somehow an INVALID instance pointer is ending up in there.
Decouple things and refactor things, and write unit tests, until you find the really ugly thing that's trashing you. (Someone suggested heap corruption. I often find heap corruption goes hand in hand with unsafe ugly untested code, and deeply bound UI+model cascading failures.)