Sunteți pe pagina 1din 18

Resolving

kbmMW v. 2.50+
Pro, ProPlus and Enterprise Editions

Introduce
But may be the result set of a query through a query service, tables with original data in the data store (database) of
backend changes made to a result set, it is important to apply a (usually true).
In this concept kbmMW called "resolving."
It sounds easy to say that resolving, in fact, are quite complex and many variables. KbmMW hides the complexity of
probe enables developers to easily resolved data in a very complicated situation.
This article describes the easiest several resolving a difficult situation in order geotbuteo. To best understand, you
need to read this entire document.

Resolving Tier 1
To start with an easy case, let's create a standard T F orm-based applications that use only the database adapter
components. The purpose of this application is on the table of the custome r BDE DBDEMOS Select all of the
records and using kbmMW BDE adapter component is to allow the user to modify the contents.
This example may seem trivial, it shows a number of basic aspects of resolving in kbmMW. Afterwards we will take
advantage of such knowledge to resolving the n-tier.
TkbmMWBDEConnectionPool, TkbmMWPooledSession, TkbmMWBDEQuery, TkbmMWBDEResolver One
component is required. To learn more about how to use these components, see the "kbmMW query service
development," the document.
In addition, the resolver also requires information about the types of data to be able to resolve the database. The
meta data component is required for this purpose. For this example, we will select the
TkbmMWGenericSQLMetaData. If the auto-increment fields has been resolved if the desired values are exactly back
to the user, to choose a more accurate metadata component particularly important.
Is the responsibility of the resolver component is to manage the way to reflect changes in the data set to the
database / data store. So we have several attributes, depending on the type resolver.
kbmMW supports both SQL and non-SQL databases / data storage both, and therefore so are several types of
resolvers. SQL resolver has the attributes of the related SQL resolving.
Other types are the resolver has other attributes for controlling the respective operating mode. We have a lot of
people will primarily look at the SQL based resolving most commonly used.
The resolver will work closely with the query component to interoperate. Query components are responsible for
tracking and managing all Dili agent, update, insert operations that occur in the result set.
The query component of this work through the record versioning. If a user updated a record of Dili, the original record
is marked as deleted but still exists in practice. When a user modifies a record kept of the original record intact but
adds a new modified version.
Therefore, to be able to change data resolves the query component of the query component EnableVersioning: must
be set to = true. (Default)
VersioningMode, but you can have two values, mtvm1SinceCheckPoint that consumes less memory. Other modes
(mtvmAllSinceCheckPoint) applications are at various stages of undo (undo) the support to try to be as helpful. (For
example using StartTransaction, Commit and Rollback, or using the Undo method)
To set the query component for resolving, the Resolver property must be set to the same type of resolver.

1
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
Applications
Now let's create a simple example application, as shown in the following figure.

Set the property as follows:

kbmMWConnectionPool 1.Database: = Database1


kbmMWConnectionPool1.MetaData: = kbmMWGenericSQLMetaData1
Database1.AliasNames: = 'DBDEMOS'
kbmMWPooledSession1.ConnectionPool: = kbmMWConnectionPool1
kbmMWPooledSession1.SessionName: = 'DEMO'
kbmMWBDEQuery1.SessionName: = 'DEMO'
kbmMWBDEQuery1.SQL.Text: = 'select * from customer '
kbmMWBDEQuery1.Resolver: = kbmMWBDEResolver1
DataSource1.DataSet: = kbmMWBDEQuery1
DBGrid1.DataSource: = DataSource1

Query event handler for the button must coded as follows:


kbmMWBDEQuery1.Open;
Event handler in the Resolve button must coded as follows.
kbmMWBDEQuery1.Resolve;
In this example, the query uses a component TkbmMWBDEQuery because of BDE-based query, so chose
TkbmMWBDEResolver components resolved to probe the data.
In order to generate the correct resolver SQL statements for the database backend you should be set to select the
correct metadata components. Although some metadata components, some of which will designed for a specific
database, so that we are now the same as the rest of the selected TkbmMWGenericSQLMetaData is more common,
you can modify the database and the various settings to suit.

2
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
DateLayout - to specify a date format for the database. In general, the resolving of parameters based not used. (Yes,
most of the resolver)
DateTimeLayout - Specify the date / time format for the database. In general, the resolving of parameters based not
used. (Yes, most of the resolver)
TimeLayout - Specifies the time format for the database. In general, the resolving of parameters based not used. (Yes,
most of the resolver)
FalseValue - specifies the format of the false values called for the database. In general, the resolving of parameters
based not used. (Yes, most of the resolver)
TrueValue - specifies the format of the true value, called for the database. In general, the resolving of parameters based
not used. (Yes, most of the resolver)
FieldNameBrackets - Specifies whether the field name is to be a cheap and parentheses. If this value is true
FieldNameQuote Specify the type of start in parentheses property ({[<(Parentheses one) is automatically selected in
the end of KbmMW.
FieldNameCase -.. Unaltered, it means that it uses the field name as it is a means to transform the Lower field name in
lowercase Upper is a means to convert a field name in uppercase letters. Have a direct impact on the generated
SQL.
It specifies the character to surround the field name - FieldNameQuote. If FieldNameBrackets is true, FieldNameQuote
are (, {, [, < And this must be one of the means the first letter of the brackets. KbmMW end bracket character is
selected automatically.
PrependTableName - Whether the table name should be prefixed field names.
QuoteAllFieldNames - true if all of the field names FieldNameQuote And FieldNameBrackets Create a format according
to the setting. False if the field name is enclosed in brackets only with inappropriate character.
QuoteTableName - true if the table name TableNameQuote Wrap a letter.

3
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
QuoteStringQuote - This property StringQuote If the letter is included in the actual string specifies the character to be
added before it. For example, "if, text such as The lady's purse 'QuoteStringQuote = \ and the like will be
StringQuote = The lady \' s purse '.
TableFieldSeperator - The table names and field names, specify the characters to be used when combined. In general, a
period (.) Is used.
TableNameBrackets - Except that the same applies to the table and FieldNameBrackets.
TableNameCase - is also similar to the FieldNameCase.
It specifies the character to wrap the table name - TableNameQuote. If the Table NameBrackets true, Table NameQuote
are (, {, [, < And this must be one of the means the first letter of the brackets. KbmMW end bracket character is
selected automatically. When TableNameQuote is set to # 0 (chr (0)) will wrap the table name at all.
StringQuote - specifies the character used to wrap the string. See the QuoteStringQuote about how to do this process
when you see the characters in the string.
If you want to know more about the metadata components, please refer to the article 'kbmMW and generalized
metadata management'.

Resolver There are several settings to control the resolving process.

AllOrNothing - All records are set to true to be resolved in a single operation. If one of the jobs record that can not be
resolved by any reason, the back-end database or data store, all changes will be rolled back. If set to false,
regardless of the probe resolving the remaining records the error occurred. Often, developers set this property to
true.
IgnoreFailedDelete - true if the report will not be there when the record is deleted.
IgnoreFailedInsert - true if the report will not be there when the record is inserted.
IgnoreFailedUpdate - true if the report will not be there when the record is modified.
InsertKeyFields - true even if you insert the values of key fields.
InsertOnFailedU pdate - true if the record is modified when you can not attempt to insert a new record.
SkipFieldsWithoutOrigin - true if the field with an empty value is not included in the Origin resolved tasks.
UpdateInsertAutoIncFields - If true, ftAutoInc types of field are included in the update and insert statements.
UpdateInsertReadonlyFields - ReadOnly is true, the field will be included in the update and insert statements.

4
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
UpdateKeyFields - true if Unique key fields contained in the record is also included in the update statement.
UpdateWhereAll - true if Include all the fields in the where clause of SQL. Only if the key field is defined to include False.

When you run the application and click the Query button, you will see a number of records in the DBGrid. Internally, it
is the cursor / DBDEMOS connection to the database by the connection pool is open, so open the connection is
scheduled to query components, and the query is executed, all records are fetched as kbmMWBDEQuery
components. The connections here are returned to the connection pool to be reused again after being closed
instead.
DBGrid does that even modify data in the data in the database are immediately corrected, because it does not have
a query component no longer connected to the database. This concept is called a broken (disconnected) dataset.
We can record in any desired modifications, such as delete, insert, but is not known to have any changes DBDEMOS
database. Horses until we resolve to change those.
It is resolved that the changes, which means that the resolver components are changed, inserted, updated in Dili
that the confirmation of the recordstorage query component. The resolver 'INSERT ... VALUES ....' These changes,
'DELETE .... WHER E ... ', 'UPDATE .... SET ....' to create and execute a SQL statement with parameters as
attempts to reflect the database .
To generate an accurate SQL statement, the resolver will need to know the name of the table to receive the data. We
know that it is a 'customer' table. The resolver find out this information from the TableName property of the query
component. Therefore kbmMWBDEQuery.TableName: Set to = 'customer'.
One other thing you should know is the name resolver component of the fields that make up the unique key. We
know that unique key called 'custno'. Therefore kbmMWBDEQuery.KeyFieldNames: Set to = 'custno'.
If the unique key consists of a number of fields, field names, the semicolon must be divided into (). For example,
'field1; field2' says so.
Now we are ready to resolves the changes to the database.
The appropriate point of query or component when the button is clicked Resolve Resolve Simply call the method is.

Error Handling
So, what happens when for some reason it can not be resolved either part or all of the work is complete?
While resolving process, the resolver builds tables error (can be accessed through the kbmMWBDEQuery1.ErrorTable
property). Error table has references to all records that can not be resolved, any error of Resolver without setting
somehow related Ignorexxxx such properties are registered.
Error table has four fields, a standard is defined as a constant as shown below.

KBMMW_ERROR_RESOLVER_RECORDID_FIELDNAME = 'KBMMW_RECORDID';
KBMMW_ERROR_RESOLVER_TYPE_FIELDNAME = 'KBMMW_ERRORTYPE';
KBMMW_ERROR_RESOLVER_MESSAGE_FI ELDNAME = 'KBMMW_ERRORMESSAGE';
KBMMW_ERROR_RESOLVER_DATA_FIELDNAME = 'KBMMW_DATA';

It can be accessed directly on ErrorTable resolved shortly after, and you can check the records to see if the error
occurs in the process of resolving. 'KBMMW_ERRORMESSAGE' you will see a message that describes the cause in
the field.
If the content of the 'KBMMW_ERRORTYPE' field has a value of 0 (kbmM WErrorResolverDB) it would not resolve a
success for the record. In this field there are other values, one value (kbmMWErrorResolverModified) and is actually
not an error indicating that the change in the value of the field during resolved, it will inform the user that fact. This
does not mean that resolves probe fails.

5
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
Rove could not be resolved in order to find the records from the records of the query using the method
SearchRecordID will go find RecordID. This method returns a recordnumber of the requested records. For example:

var
i: integer;
begin
q.CurIndex.SearchRecordID (q.ErrorTable.FieldByName ( 'KBMMW_RECORDID') .AsInteger, i);
if (i <0) then Raise Exception.Create ( 'Record not found' );
q.RecNo : = i + 1;

// Now the failing record has been found .


ShowMessage ( 'Record with value' + q.Fields [0] .AsString + 'didn' 't resolve'.);
end;

KBMMW_RECORDID the field to separate the records. If requested by the client to resolve the query, the record ID
and record ID of the client record, and the record ID of the server-side query records if it is not.
There may have been an error record registration records without ID. This is not an error if the error is related to the
specific record.
How little less cumbersome, easy-coding is to create an event handler for the OnResolveError the query or stored
procedure components. This event is called whenever the record is not resolved for each failed to receive the
information on the types of error factor (always kbmMWErrorResolverDB) and error messages.
If possible,
If possible, it automatically makes sure that the
current record in the query / storedprocedure dataset, is the
failing record and thus information about the record can be obtained through normal field operations.
Check the Current argument to see if a current record could be determined.
Finally the OnResolveError event have a R etry argumentwhich is default true. It can be set to false if this
particular record should not be re-resolved on next resolve operation. All failing records are normally
automatically marked to be resolved again on next opportunity.
Original field values for the current record can be obtained like this:
var
oldvalue: variant;
n: integer;
us: TUpdateStatus;
begin
// Get the original value for a field as a variant.
oldvalue : = q.GetVersionFieldData (q.FieldByName ( 'somefieldname'), 9999);

// Oldvalue contain the value of the field 9999 versions ago ...
// Which in reality means the original value .
// The number of versions of the record can be obtained by
n : = q.GetVersionCount;
// And the updatestatus of a particular version can be obtained by:
us : = q.GetVersionStatus (x);
// Where x is the version number. 0 = current version, 1 = older version,
// 2 = even older version etc.
end;

6
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
Raising errors manually
On occasions, it's practical to validate the resolve operation while it's taking place, and potentially stop it or
signal a problem back to the user.
Let's say you want to check the contents of a new record before its being inserted into the database.
Write some code for the OnInsert event handler of the resolver component. Eg

procedure TForm1.kbmMWBDEResolver1Insert (Sender: TkbmMWCustomResolver;


var Skip, Abort: Boolean);
begin
if Sender.ValueByFieldName [ 'afieldname'] = 22 then
Skip : = true;
end;

What this does is that it checks the value of the field named 'afieldname' for its value. If it's 22, we ask the
resolver to skip the insert silently.
We could also have forced an abort of the resolving operation by setting Abort: = true.
The client would immediately get to know that the resolve operation was aborted. However the user would not get
any information that may have been provided in the errortable by previous records being resolved in the
same operation.
If we would like to signal a problem with
a specific record in a way that the user would be able to receive via
the error table, and without aborting the resolve operation as such, we can use the LogError and
LogRecordError methods. Eg
procedure TForm1.kbmMWBDEResolver1Insert (Sender: TkbmMWCustomResolver;
var Skip, Abort: Boolean);
begin
if Sender.ValueByFieldName [ 'afieldname'] = 22 then
begin
Sender.LogRecordError ( 'We don' '' t like the value 22 in afieldname ');
Skip : = true;
e nd;
end;

with a record reference, in such way that the record in


LogRecordError logs an error in the errortable along
question can be selected automatically for the user to scrutinize.
If there are errors that can not really be related to a specific record, instead use LogError.

Change the value in resolving and working groups


Developers can also change the values of the resolver resolving process is working before or working on resolving to
proceed.
Resolver has an event called OnResolveInitialization. This event occurs every time you start to become a resolver
dataset.Resolve tasks, such as calling the method.
During this event operation, it is possible to change the value of the entire data set to become resolved. Resolver
Dataset The property can access the corresponding data set.
Remember, all the things that you fix at this point is that directly reflects on resolving tasks. For example, if you
delete a record in a data set, the resolver will attempt to delete the record from the database backend.

7
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
Therefore OnResolveInitialization Events allows you to work on the entire dataset. However, you should not attempt
to add or remove fields. Because the dataset is andoegi itself be changed.
Another way to change the data imported from the resolver is working on resolving the dataset, the resolver
OnGetValue It is through the event.
This event allows you to assign new values to a particular field of work for resolving the current record. For example,

procedure TForm1.kbmMWBDEResolver1GetValue (
ADeltaHandler: TkbmCustomDeltaHandler; AField: TField;
v ar AValue: Variant);
begin
if AField.FieldName = 'field1' then
AValue: = 22;
end;

In the above case, the resolver will always get a value of 22 for the field named field1. This code, or when you need
to be aware of what the value will be used as a relatively less. The assignment of a particular fixed value, so usually
insert operations, the update operations may try to keep the original values of the dataset itself intact.

procedure TForm1.kbmMWBDEResolver1GetValue (
ADeltaHandler: TkbmCustomDeltaHandler; AField: TField;
var AValue: Variant);
begin
if (TkbmMWCustomResolver (ADeltaHandler) .State = mwrsInsert) and
(AField.FieldName = 'field1') then
AVa lue: = 22;
end;

The revised code above to examine whether the operation is a record insert. If you are working on resolving that only
22 values are assigned to the fields field1.
If you need any state of the resolver mwrsInsert, mwrsDelete, mwrsModify, You can check which of mwrsIdle
(resolver when you are not resolving the work).
good. Now we are able to specify a new value in working on resolving. But I Should not confuse our user? There you
will see any value in their disconnected (disconnected) datasets, the database will have a different value.
Therefore, for the value changes, you need to tell what happened to you. This LogFieldValueChange You can easily
implement the method. Consider the following example.

procedure TForm1.kbmMWBDEResolver1GetValue (
ADeltaHandl er: TkbmCustomDeltaHandler; AField: TField;
var AValue: Variant);
begin
with TkbmMWCustomResolver (ADeltaHandler) do
begin
if (State = mwrsInsert) and ( AField.FieldName = 'field1') then
begin
Avalue : = 22;
LogFieldValueChange ([AField], [AValue], 'We chose 22 instead for field1' );
end;
end;
end;

8
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
LogFieldValueChange Gatneunde method is a factor of three, variant refers to changes in the dataset that matches
the new value in the array, each array of resolving the field, and the last message you want to display.
Basically, the changes are recorded via this method are automatically updated in the original dataset. Therefore, as
soon as the end user is able to resolve tasks that will see the new value.
If you want to notify the user to change the content, you can use the OnResolveFieldValueChange events in the
dataset. For example,
procedure TForm1.kbmMWBDEQuery1ResolveFieldValueChange (Sender: TObject;
Fields: array of TField; Message: String; RecordID: Integer;
Current: Boolean; var Skip: Boolean );
var
s, a: string;
i: integer;
begin
s : = '';
a : = '';
for i: = low (Fields) to high (Fields) do
begin
s : = s + a + Fields [i] .FieldName + 'To' + Fields [i] .AsString;
a : = ",";
end;
ShowMessage ( 'The following fields have changed their value:' + s);
end;

Note that this is actually not on the field Fields array of data points to three fields in the error table. Therefore, you
can To get the original field values as follows:

Originalvalue : = yourquery.FieldByName (Fields [i] .FieldName) .AsString;

Message argument has provided the message when the field value changes in working resolved. Skip argument is
false by default, can be set to true for the purpose of the current override any changes to the record. This means
that the modified contents of the user's disconnected data sets do not reflect.

And other database operations in resolving working group


If in special circumstances, you may need to update another table working on resolving.
To do this, you can use the Connection property of the resolver pointing to a connection that is being used for the
current job database. So take advantage of this connection, all updates to the database that you are a part of a
complete transaction.

procedure TForm1.kbmMWBDEResolver1Delete (Sender: TkbmMWCustomResolver;


var Skip, Abort: Boolean);
var
c: TkbmMWBDEConnection;
q: TQuery;
begin
c : = TkbmMWBDEConnection (Sender.Connection);
q : = TQuery.Create;
try
q.Database: = c.Da tabase;
q.SQL: = 'INSERT INTO TRACE ( MESSAGE) VALUES (' 'DELETING'

9
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
+ ValueByName [ 'somefield'] + '' ')';
q.ExecSQL;
finally
q.Free;
end;
end;

Resolving the auto-increment field


Resolver UpdateInsertAutoIncFields Auto, the default setting of the property Increase (Autoincrement) field is not
resolved.
But will auto-increment field is assigned a new unique value automatically by the database when a record is inserted.
KbmMW is
However while the record is inserted, the autoincrement field will automatically be assigned a new unique
value by the database. kbmMW supports getting that value for a good part of the su pported databases
(although not guaranteed for all).
When a record is inserted, to bring a new autoinc value corresponding to the record, you can use the following
properties resolver (only supports single autoinc fields per table).

property LastAutoIncValueByIndex [AIndex: integer]: integer


property LastAutoIncValueBy TableName [ATableName: string]: integer

LastAutoIncValueByIndex require you to provide an index from 0 to the number of tables being resolved less
one (see resolving joins).
LastAutoIncValueByTableName allow you to provide the table name for which you want to get the latest
autoinc value. The table obviously must be part of the tables being resolved.
The resolver automatically logs field changes when an autoinc field change value during resolve.
When we later talk about resolving joins, we will see how to us e this to effectively resolve master / detail
relations which involves autoinc fields.

Field optional resolving


So, what do you do if you have some of the fields that should not be resolved? For example,

SELECT * from customer order by custno

Contact can also be called a field if you prefer not to resolve if they wish, or this field have a sum of the values of
the other two fields.
There are several ways to ensure that the specified field is not resolved.
One) Origin will be resolved in the customer table of all fields except field should not only Set the properties.
Resolver SkipFieldsWithoutOrigin The property is set to true.
2) It includes the pfInUpdate ProviderFlags property values in the fields. When you do this This way you can
also control if a field should be considered part of the unique key or not during resolve by setting
pfInKey and pfInWhere.
3) Use the database adapter's resolver's event ExcludeFromUpdateInsert and ExcludeFromWhere.
Simply return a set of matching field provider flags in the Flags argument of the event excluding the

10
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
operation you do not want the field to participate in. The returned set can contain mwpfInUpdate,
mwpfInInsert and mwpfInKey.
Further the resolve have several properties which can be
set to include or exclude resolving fields ma rked as
readonly in the dataset, key fields and autonumeric fields namely UpdateInsertReadonlyFields,
UpdateInsertAutoIncFields, InsertKeyFields, UpdateKeyFields.

Resolving the joined results


Now, let's push forward the project to the more advanced SELECT statements.

SELECT * from customer, orders where orders.cu stno = customer.custno order by


customer.custno

Above SQL statement that joins two tables, this case is more complicated to resolve tasks.
If the SQL statement above is executed, all fields from the customer table and the orders table are returned in a
single record. Some field names are in conflict with one another in the two tables. For example, custno taxrate and
both exist in the customer table and the orders table. The result set because there can not be two fields with the
same name, the two fields are not the only name they are given a unique name automatically. Therefore, the result
will be three customer.custno, customer.taxrate, orders.custno_1, orders.taxrate_1 field returns.
custno_1 and taxrate_1 does not exist in the original table if you attempt to resolve this case does not function
properly. Thus, the field is to be assigned so the origin of the field to indicate whether derived from any field of any
table.
Some database adapters, such as adapter ADOX are automatically set, but the origin information, such as another
adapter BDE are not. This is because the database API does not support such information.
This means that developers must specify the origin directly.
It must resolve before the Origin values are assigned to all fields that will be resolved. Of course, this is not
necessary when there is only one table relationships. This is because the returned field names to match the actual
field names in the database table.
In our example, insert the SQL statement in the SQL property of the above TkbmMWBDEQuery components.
And then double-click to bring up the components TkbmMWBDEQuery field editor.
Right-click on the 'Add all fields' Select.
This ensures that all the fields in the query component will be generated statically. If you use the same query
component to a query of different type and origin it is necessary to set a field to run the code.
If you generate a static field, you can set the Origin field at design time. Shift or Ctrl Clicking on the field, press the
key, you can select multiple fields at the same time designers in the field.
Customer Select all fields from the table. Then press the F11 key to bring up the Object Inspector.
Origin on the property the Customer . I called sseoneot.
Note the period (.) To catch the end!
This is a field that ' the Customer ' means that from the table. If there is no period at the end kbmMW is the fields
'customer' will recognize from the database field called nawatdago.
Origin This value ends with a period, the name of the field in the database table TField means equal to the field
name of the object.
Next, Orders , select all the fields from the table and its Origin value orders. Sets said.
Again, do not forget to put a period at the end.
Now we would need to separate processing on the field, the query is automatically given a unique name attached to.

11
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
In the field list CustNo_1 select a field named. Origin the value of the property orders.custno Sets.
In the field list TaxRate_1 select a field named. Origin the value of the property orders.taxrate Sets.
Now we have an object field to specify it gave unseat from any database field.
The next step is 'Simple resolving' as we did in the TableName and KeyFieldNames will specify that information on.
In this example, we are customer and the field of orders try to probe all the fields resolved.
Therefore, make the following entries:

TableName : = ' the Customer; the Orders '

This resolver will be able to see that the two tables should be resolved. T ableName only fields belonging to the
property listed in the table are resolved. So join itself is perfectly valid, but will be one of the tables involved in the
join or be partially resolved probe.
Finally, make the following entries:

KeyFieldNames : = ' customer.custno; orders.orderno '

This is to specify a field known to be the only key fields in the table.
If you have a unique key consists of two fields in a table, you can specify two field names, including the name of the
table.
Resolver SkipFieldsWithoutOrigin property because the field without origin can be confusing where to go normally is
set to true. If the value of all the Origin field is set, SkipFieldsWithoutOrigin properties are not affected.
Resolve tasks of the join is done under the control of the complete transaction. This means that if an error occurs at
one of the tables that resolve all up Eight rollback on all the tables.
And that's all the things you need to know in order to resolve the join result.
Master / key detail relationship
The keys for the master / detail relation are manually determined by the user or application when adding new
records.
But what if the auto-increment key field type? In that case, the Customer on the table Disconnect, ed value provided
in the user data are set to differ from the values stored in the database. This new customer because it automatically
assigns a unique number to the new database automatically when you insert a record increase (key) field.
In such cases, the Customer table, pointing at the record of the (master table), the Orders will be no field does not
match the table (the detail table).
There are also solutions.

The master table Sure that the make is Only listed FIRST in The TableName property, followed by the Detail The
table. Eg q.TableName : = ' the Customer; the Orders '.
Doing that ensures that fields belonging to the customer (master table) is resolved first, followed by fields from the
orders (detail) table.
Because of that we can use the LastAutoIncValue ... methods to obtain the correct newly created autoinc value for
the customer table and use that value as the reference value in the orders table. Eg

: procedure TForm1.kbmMWBDEResolver1GetValue (
ADeltaHandler: TkbmCustomDeltaHandler; AField: TField;

12
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
var aValue: Variant);
begin
with TkbmMWCustomResolver (ADeltaHandler) do
begin
the IF (State = mwrsInsert) and (AField.FieldName = 'CustNo') the then
begin
AValue : = LastAutoIncValueByTableName [ ' the Customer ' ];
LogFieldValueChange ([AField], [AValue],
'Referenced the Customer was changed to the Record ' + aValue);
end;
end;
end;

The Time The same we Remember to log AT the Change the SO The The Disconnected Your the DataSet with the
CAN be the Automatically Updated The new Rate Valid with the VALUES OUT a HAVING to Reopen The Query.

Datasets to resolve multiple servers in a single transaction


And you have multiple datasets,
The IF you have The Multiple Datasets, possibly originating from different Database the Servers of even different
types, and you The resolve operation The Changes, in Each Call the DataSet, One, by One,, yo u the CAN have The
problem that One, the DataSet is the Resolved ok, and Another is not The which may the Leave you with a set of
partially updated databases .
The Remedy that the To, kbmMW employs ares Server side (side Adapter db) the called the Component
TkbmMWTransactionResolver .
This component handle distributed transaction control. It means that it automatically will start, commit or rollback
transactions on multiple databases at the same time, virtually operating like the updates on all the databases are all
running in one big transaction.
It's very easy to use. An instance of Add TkbmMWTransactionResolver to Your Application Design Time The AT,

One, or the CREATE AT Runtime. Eg.


var
t: TkbmMWTransactionResolver;
begin
t : = TkbmMWTransactionResolver.Create (nil);
try
t.Resolve ([dataset1, dataset2, ..., datasetn]);
finally
t.Free;
end;
end;

Each dataset will automatically be resolved according to the normal resolving settings used when resolving the
dataset standalone. The difference with the transaction resolver is that if one of the datasets does not resolve
properly, all the resolves for all datasets will be rolled back. It however requires that the backend database fully
supports transactions.
What if you want not only to resolve changes in datasets, but also do other custom SQL operations within the same
transaction?
That's simple.
We add a query component containing the SQL you want to introduce to your database while resolving. Eg

13
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
qSpecialStuff.SQL.Text : = ' INSERT INTO SomeTable (X, Y, Z) VALUES (10,22,33) ' ;

Now, TransactionOperation the property mwtoExecute set to (the default is mwtoResolve a). And the following
qSpecialStuff include a data set to be resolved in a transaction resolver.

t.Resolve ([dataset1, dataset2, qSpecialStuff, ..., datasetn]);

Now the transaction resolver dataset1 and dataset2 this procedure resolves, and then as a q SpecialStuff execute
the SQL statement specified in the data and continue with the next set of resolving them.
You can use an SQL statement as you want anywhere in the resolve of the transaction work.

N- tier client-side resolving


Keeping the knowledge from the previous chapters present, we will now resolve data in an n-tier setup from the
client. Intere The Sting is Part Now that we have an in-between Application Server through The which The Client
communicates.

Application server
An Application Server Query ares containing the CREATE FIRST Service Guidelines from The Following ' the Using the
AS kbmMW ares Query Server ' Whitepaper, or the Use One, of the Predefined The ones like The BDE Application
Server.
The query service could look like this (as copied from the BDE server):

The Query Components Trade shows All have The Resolver property kbmMWBDEResolver1 The Hooked up to the
Component.
This query service contains 3 query components where two of them contain a predefined SQL statement while one
(ClientSideQuery) require the client to provide the SQL statements.
In case of SELECTED_EVENT and ALL_EVENTS, the TableName and KeyFieldNames properties should be given at
server side. This makes sure that the client do not have to worry about that when the client want to resolve.
Field ALSO Origins and / or ProviderFlags shouldnt be on The Specified Server side Those Two The Queries for the
IF AT Trade shows All they are needed (eg. In the JOIN Situations etc.). The client will automatically get to know the
values defined on the server.

14
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
The ClientSideQuery is another story. The SQL Client the CAN Give the Any a Statement Since Cares for IT,
predefining TableName , KeyFieldNames , Origins and ProviderFlags wont The Server on the make Sense. Its the
responsibility of the client to setup that.
To let the client provide TableName and KeyFieldNames settings, a couple of properties must be set on the query
service authorizing the client to do so.

AllowClientKeyFields and AllowClientTableName Must be true. In addition, AllowClientStatement should be true to


let the client specify a query statement (SQL) of its own.
AllOrNothing on the Component The resolver shouldnt Usually ALSO SET to be true, Disallow to Successful partial
resolves.

The client application

The client contains a TkbmMWClientTCPIPIndyTransport, a TkbmMWClientConnectionPool, a


TkbmMWPooledSession, a TkbmMWClientQuery and a dataset streamformatter in addition to the dataaware
controls.
All components are hooked together according to the 'Using kbmMW as a query server' whitepaper.
If the client should access the predefineded server query ALL_EVENTS, the query statement in the client should be
'@ALL_EVENTS' utilizing a named query statement.

15
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
When opening the query, all informations needed to resolve are automatically transferred from the server to the
client so that the client can advice the server about them later on when / if the client choose to resolve changes in
the resultset.
In other words, all what the developer needs to do is to open the query, let the user alter data in the grid, and press
a key which calls the Resolve method of the client query component.
The Client will Itself Provide Query the IF / SQL statements, ITS Now The Responsibility of The Client to the Setup
TableName , KeyFieldNames , OPTIONALLY field Origins and ProviderFlags .
In some cases, the server is able to deliver correct Origin values, but this is very much dependent on the database
and its API as mentioned earlier on.
Error handling after a resolve event on the client is exactly the same as on the server, described in a previous
chapter.

To resolve a number of client datasets as a single transaction


Earlier, to resolve the multiple tasks on the server side data sets into a single work under complete control
transaction TkbmMWTransactionResolver looked at the components. A component which functions as the client

TkbmMWClientTransactionResolver are components.


To use this component must be set following attributes are.
ConnectionPool TkbmMWClientConnectionPool must point to the component.
QueryService in the App server, set to query service name to be responsible for resolving.
QueryServiceVersion set to the version string for the query service.
ormat set to meet transport and transport format data set of server-side components. ( For example TkbmMW
BinaryStreamFormat )
It is now necessary to probe resolving the client dataset, Resolve to do is call the method.

Myclienttransactionresolver.Resolve ([clientdataset1, ..., clientdatasetN]);

If you will return the probe resolving the false call failed Resolve jobs, to find out why ErrorTable you should look for. (
Or OnResolveError can also take advantage of the event )
As with server-side resolving transactional components, in order to run a SQL statement contains a data set that
contains the SQL statement to the data set list and TransactionOperation the mwtoResolve instead mwtoExecute
you set.

Developing custom resolver


Some very special cases, which may be necessary to control for the overall operation of the resolver. For example, if
a yes to the resolving operations to other data structures such as a database file system.

In this case, you may find it easier to create your own resolver components. The following is an example of a simple
resolver.

= TYourResolver class (TkbmMWCustomResolver)


protected
: function DoInsertRecord (AObject: TkbmMWCustomResolverObject): boolean; The
override ;

16
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
: function DoModifyRecord (AO bject: TkbmMWCustomResolverObject): boolean; The
override ;
: function DoDeleteRecord (AObject: TkbmMWCustomResolverObject): boolean; The
override ;
end;

: function TYourResolver.DoInsertRecord (AObject: TkbmMWCustomResolverObject)


: boolean;
var
i: Integer;
v: variant;
S: String ;
begin
// Getting new values for the record to be inserted.
for i: = 0 to FieldCount-1 do
begin
v : = Values [i]; // The value for a field.
S : = FieldNames [i]; // S contain the name for the field .

// Do whatever you need here.


...

// If you raise an exception it will automatically add the exception message to


// The ErrorTable and continue with next record, unless AllOrNothing is true.
end;

// If you do not return true, an error record will be added to the ErrorTable.
Result : = True;
end;

: function TYourResolver.DoModifyRecord (AObject: TkbmMWCustomResolverObject)


: boolean;
begin
Result : = True;
end;

: function TYourResolver.DoDeleteRecord (AObject: TkbmMWCustomResolverObject)


: boolean;
begin
Result : = True;
end;

The resolver of Simply ASSIGN an instance of the Then to The Resolver property of The Server Query / the Stored:
procedure the Component. You can register the resolver as a component to be able to use it at designtime just like
any other custom component.

17
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .
This concludes our resolve document.

Kim Madsen
Components4Developers
One
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
Translation : bakjihun . Imp

18
Copyright 2001-2005 Components4Developers
http://www.components4developers.com
: .

S-ar putea să vă placă și