Τετάρτη, Μαρτίου 09, 2005

How Applying updates works

I have to clarify some things relevant to TKTDatasetprovider and how it applies updates. Truth is I should have added more comments in that part of code and should have made it more clear. If someone checks the code of KTProvider.pas probably he will get confused. What is TIntegerPair, what is TDependOnInfo?

(I have to say that from now on I'll use the term "master cds" to denote the "least dependent dataset" and the term "detail cds" to denote the "most dependent cds"- Don't confuse with master-detail relationships)

I'll try to make it clear here:

The main issue I had building a way to wrap all updates in one call to server was this: If I have a newly inserted record in a "master" cds ( and I have either an insertion or an update in a "detail" cds that uses this new master primary key value, how is it possible to update the foreign key value with the new value during applying updates, since we are talking about two different deltas?
It is a usual technique to give a negative value to the primary key of a cds when inserting a new record in a client dataset (in a multitier application) and give it new positive values (from a generator function) in BeforeUpdateRecords, during applying updates. This value will then be propagated back to the client (if poPropogateChanges is set which is the default behavior in TKTDataSetProvider).
If a foreign key (belonging to a different cds, not in a nested dataset) references this negative value, then this cds should probably be refreshed after applying updates.
I tried to find a way to bypass this extra round-trip and that made my code a little bit complicated. I implemented the following steps:

  1. I call provider.ApplyUpdates once for all deltas, but I don't make any real updating in this step (I set BeforeUpdateRecord Applied parameter to True). What I do is call the generator function and fill the IntPairList property of TKTDataSetProvider.
    • This property is a TList of TIntegerPair objects. These objects (as their name actually says ) have two Integer fields: OldValue and NewValue. The other fields (ParentIdValue and QualifiedFieldName) are just used for "precise navigation" to the relevant OldValue. OldValue takes the negative value set on client side and NewValue is the new value we just got using the generator function.
  2. I repeat the previous step, only this time I dohe actual applying of updates and in particular only insertions in a order from the "least dependent" to the "most dependent" Delta. At this step I use the values from IntPairList property and assign the new primary key value (always in a BeforeUpdateRecord handler). At this step I update and any negative foreign key value and I use DependOnInfoList property to achieve this.
    • DependOnInfoList is set in client side and is sent along with the OleVariant which contains deltas. It has information that allows us to "navigate" to the "correct" IntPairList that has the new value that we want our foreign key field to be assigned to.
  3. I repeat the previous step, only this time I do it in the reverse order and apply updates only relevant to modifications or deletions.
May be the above custom solution is very complicated, but the goal is to avoid having to refresh the data of a "detail" cds, each time I apply updates and to be generic enough to cover complicated cases. This can be combined with a technique to check if some other user has applied any changes and only then refreshing data (I have an example in my help file-in Cds.NeedsUpdate property). Using the above code all new values set on app server automatically propagate back to client and get merged (preserving data integrity) (Of course, this happens only if transaction commits)
May be I could achieve the same thing with simpler implementation, but I am not a professional component developer and I can't think of a simpler work around. The truth is that it is working as it is. (And I've tested it in a complex application with 23 tables and one nested dataset structure 4 levels deep). Anyway, any suggestion is always welcomed!