RTC Forums
April 27, 2024, 12:14:23 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: client can not retrieve the dataset normally  (Read 7260 times)
zhaohuateng
RTC Expired
*
Posts: 5


« on: July 17, 2017, 04:50:20 AM »

I try to create a dbserver from the following link
http://www.realthinclient.com/category/rtc/rtcsdk/rtcsdk-db/
Using FireDAC to connect to the PostgreSQL database, there is an array type field in PostgreSQL, and the client can not retrieve the dataset normally.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #1 on: July 17, 2017, 08:33:53 AM »

Whatever you put into a TRtcDataSet on one side (Server?) will be available in the exact same form in a TRtcDataSet object on the other side (Client?), but ... if you are working with a TDataSet component on either side, then you are going to copy data from a TDataSet into a TRtcDataSet before sending and/or copy data from a TRtcDataSet into a TDataSet after receiving. Even though standard functions from the "rtcDB" unit for copying data from TDataSet to TRtcDataSet and back should work with the most basic DB Field types, these functions can NOT automatically recognize and translate all DB Field types available with all Database Access components.

So ... unless you are ONLY using the most basic DB Field types, you will have to copy the "DelphiDataSetToRtc" function from the "rtcDB" unit into one of your own units, give it a different name, then modify your version of the function to copy DB Fields from a TDataSet component (provided by Database Access components of your choice) to a TRtcDataSet object before sending your DataSet to the Client. Depending on your implementation of that function (usually used on the Server), you will also need another function to extract that data (on the Client?) if you want to use it with a TDataSet descendant like TRtcClientDataSet (which is based on  TClientDataSet), or any other 3rd-party TDataSet component.

See these two FAQ Topics for more info:
How to copy data from a TDataSet component into a TRtcDataSet object?
How to copy data from a TRtcDataSet object into a TDataSet component?

Best Regards,
Danijel Tkalcec
Logged
zhaohuateng
RTC Expired
*
Posts: 5


« Reply #2 on: July 17, 2017, 01:06:47 PM »

Hello Danijel Tkalcec
I read the two frequently asked questions you provided and tried to use DelphiDataSetToRtc on the server side and RtcDataSetToDelphi on the client and replace the RtcMemDataset with the RtcClientDataSet, but I tried unsuccessfully that the dataset and RtcDataets were not converted correctly. Can you use DEMO to answer this question, thank you
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #3 on: July 17, 2017, 02:09:25 PM »

You can take a closer look at FishFactServer and FishFactClient Demos from the "Demos/DB_Access" folder, which demonstrate working with DataSets containing numerical, textual and BLOB fields, but ... there is no DEMO which would do exactly what you want and I can NOT write one for you, because each Database works differently and each set of Database Access components handles DB-specific field types differently. That's why functions for copying data from a TDataSet into a TRtcDataSet have to be MANUALLY MODIFIED to work correctly with all field types provided by your Database and Database Access components.

In other words, as I've already tried to explain, the default implementations of DelphiDataSetToRtc and RtcDataSetToDelphi functions (provided in the "rtcDB.pas" unit) work ONLY with the most basic field types. If you want to handle other field types, you need to MAKE A COPY of the DelphiDataSetToRtc function (original is in the "rtcDB.pas" unit) and MODIFY YOUR FUNCTION to read DB fields from a TDataSet and store their contents in a TRtcDataSet.

If you need help using Database Access components to read the contents of Database fields, please consult documentation available for the Database and Database Access components of your choice.

Once you have all DB fields from a TDataSet correctly copied into a TRtcDataSet object, you can send it over to the Client. On the Client side, if you want to use your data with visual DB-Aware components, you will also need to copy data from a TRtcDataSet into a TDataSet descendant like TRtcClientDataSet (which is a direct TClientDataSet component descendant, with a TRtcDataSetMonitor built-in). Here, you will start by copying the RtcDataSetToDelphi function from the "rtcDB.pas" unit and MODIFYING IT until you get all the fields correctly populated.

Best Regards,
Danijel Tkalcec
Logged
zhaohuateng
RTC Expired
*
Posts: 5


« Reply #4 on: July 17, 2017, 03:37:19 PM »

After trying to find the fields in the DataSet that I have retrieved in the rtc_DataSet type, I have added a judgment on this type in the case RTC_FIELD2VALUE_TYPES [rtcDS.FieldType [fldname]] statement.
I now encountered the problem is unable to convert rtcDataSet into DataSet,
I try to use RtcDataSetToDelphi this function will be wrong in the compiler suggested that the statement of the const RTC_FIELD2DB_TYPE constant error, can you give me the revised version of the RtcDataSetToDelphi function?
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #5 on: July 17, 2017, 03:48:38 PM »

RtcDataSetToDelphi function was split into two functions: RtcDataSetFieldsToDelphi and RtcDataSetRowsToDelphi. The first only copies field definition, while the second copies the actual data. Both functions, along with a number of other DB-related functions, are provided with full source code in the "rtcDB.pas" unit, which is included in the RTC SDK and available in the "Lib" folder. Here is the complete list of available functions ...

// Convert RTC Field Type to Delphi Field Type
function RTC_FIELD2DB_TYPE(val:TRtcFieldTypes):TFieldType;

// Convert Delphi Field Type to RTC Field Type
Function RTC_DB2FIELD_TYPE(val:TFieldType):TRtcFieldTypes;

// Copy data from a Delphi TDataSet into a TRtcDataSet (used for transport)
procedure DelphiDataSetToRtc(DelphiDS:TDataSet; rtcDS:TRtcDataSet; ClearFieldDefs:boolean=True; OnlyDataFields:boolean=True);

// Copy data from a Delphi TDataSet into a TRtcArray of TRtcRecords (used for transport)
procedure DelphiDataSetToRtcArray(DelphiDS:TDataSet; rtcArr:TRtcArray; OnlyDataFields:boolean=False);

// Copy field definition from a TRtcDataSet (used for transport ) to a Delphi TDataSet
procedure RtcDataSetFieldsToDelphi(rtcDS:TRtcDataSet; DelphiDS:TDataSet);

// Copy data rows from a TRtcDataSet (used for transport) to a Delphi TDataSet
procedure RtcDataSetRowsToDelphi(rtcDS:TRtcDataSet; DelphiDS:TDataSet);

{ Extend the SQL "WHERE" clause with a new record filter:
  Filter = record with filter names and values;
  ParamPrefix = prefix to be used for field names in Params;
  CompareOperator = compare operator to be used in the filter (=, <, >, <=, >=);
  TableName = Table Name;
  SqlWhere = SQL "WHERE" clause to be modified/extended }
procedure RtcPrepareSqlWhere(Filter:TRtcRecord;
                             ParamPrefix:String;
                             CompareOperator:String;
                             const TableName:String;
                             var SqlWhere:String);

{ Set SQL Where Params with record filter values (call "rtcPrepareSqlWhere" to prepare the SQL statement first!):
  qry = SQL Query Params to be extended;
  Filter = record with filter names and values;
  ParamPrefix = prefix to be used for field names in Params }
procedure RtcSetSqlWhereParams(Filter:TRtcRecord;
                               ParamPrefix:String;
                               qry:TParams);

{ Prepare Action SQL statement (INSERT/UPDATE/DELETE):
  chg = TRtcDataSetChanges object positioned at the action to be applied;
  TableName = Table Name;
  SqlWhere = Optional SQL "WHERE" clause;
  Result = SQL Statement for executing the Action }
function RtcPrepareSqlAction(chg:TRtcDataSetChanges;
                             const TableName:String;
                             SqlWhere:String=''):String;

{ Set SQL Action Params:
  qry = SQL Query Params to be extended;
  chg = TRtcDataSetChanges object positioned at the action to be applied }
procedure RtcSetSqlActionParams(chg:TRtcDataSetChanges;
                                qry:TParams);

{ Internal function for skipping TGraphicField headers inside a BlobStream.
  Can be used in custom functions for copying a Delphi DataSet to a RTC DataSet. }
procedure RtcSkipGraphicFieldHeader(BlobStream:TStream);

Best Regards,
Danijel Tkalcec
Logged
zhaohuateng
RTC Expired
*
Posts: 5


« Reply #6 on: July 17, 2017, 04:17:31 PM »

Sorry to bother again
The following code is I modify the DelphiDataSetToRtc function function field type rtc_DataSet to determine the location of the identity I need to pass the data set value, this value should be how?
              case RTC_FIELD2VALUE_TYPES[rtcDS.FieldType[fldname]] of
                rtc_Currency: rtcDS.asCurrency[fldname]:=field.AsCurrency;
                rtc_DateTime: rtcDS.asDateTime[fldname]:=field.AsDateTime;
                rtc_String: rtcDS.asString[fldname]:= RtcString(field.AsString);

                rtc_DataSet: rtcDS.asDataSet[fldname]:= ??;  //How can we get the value of the dataset type?

              {$IFDEF UNICODE}rtc_Text: rtcDS.asText[fldname]:=field.AsWideString;{$ENDIF}
                //rtc_Array: rtcDS.asString[fldname]:=RtcString(field.AsString);
                else rtcDS.Value[fldname]:=field.Value;
                end;
            end;
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #7 on: July 17, 2017, 06:55:14 PM »

The purpose of the DelphiDataSetToRtc function is to create a TRtcDataSet object by using field definition and row data stored in a TDataSet object. First, it uses the SetFields method to create fields (one field at a time) and then it loops from First to Last row of a TDataSet and copies data field-by-field. Here is the complete function code, as implemented in the "rtcDB.pas" unit ...

Code:
procedure DelphiDataSetToRtc(DelphiDS:TDataSet; rtcDS:TRtcDataSet; ClearFieldDefs:boolean=True; OnlyDataFields:boolean=True);
  var
    flds:integer;
    fldname:RtcWideString;
    field:TField;
    fstream:TStream;
  begin
  if ClearFieldDefs then
    begin
    rtcDS.Clear;
    for flds:=0 to DelphiDS.Fields.Count-1 do
      begin
      field:=DelphiDS.Fields[flds];
      if assigned(field) then
        begin
        fldname:=RtcWideString(field.FieldName);
        if (OnlyDataFields=False) or (field.FieldKind=fkData) then
          rtcDS.SetField(fldname,
                         RTC_DB2FIELD_TYPE(field.DataType),
                         field.Size,
                         field.Required);
        end;
      end;
    end;

  DelphiDS.First;
  while not DelphiDS.Eof do
    begin
    rtcDS.Append;
    for flds:=0 to rtcDS.FieldCount-1 do
      begin
      fldname:=rtcDS.FieldName[flds];
      field:=DelphiDS.FindField(String(fldname));
      if assigned(field) then
        if not field.IsNull then
          if (OnlyDataFields=False) or (field.FieldKind=fkData) then
            if field.isBlob then
              begin
              fstream:=DelphiDS.CreateBlobStream(field,bmRead);
              try
                if {$IFNDEF FPC} TBlobField(field).GraphicHeader and {$ENDIF}
                  ( (field.DataType = ftGraphic) or
                    (field.DataType = ftTypedBinary) ) then
                  RtcSkipGraphicFieldHeader(fstream);
                rtcDS.NewByteStream(fldname).CopyFrom(fstream,fstream.Size-fstream.Position);
              finally
                fstream.Free;
                end;
              end
            else
              case RTC_FIELD2VALUE_TYPES[rtcDS.FieldType[fldname]] of
                rtc_Currency: rtcDS.asCurrency[fldname]:=field.AsCurrency;
                rtc_DateTime: rtcDS.asDateTime[fldname]:=field.AsDateTime;
                rtc_String: rtcDS.asString[fldname]:=RtcString(field.AsString);
              {$IFDEF UNICODE}rtc_Text: rtcDS.asText[fldname]:=field.AsWideString;{$ENDIF}
                else rtcDS.Value[fldname]:=field.Value;
                end;
      end;
    DelphiDS.Next;
    end;
  end;

The function above uses RTC_FIELD2VALUE_TYPES to handle standard field types, but since you want to handle a custom field type, you should directly check the field type on the field:TField object (provided by the TDataSet class in Delphi), then extract raw data from that field. The actual code you will need to write to extract the data will depend on the field type, Database and Database Access components you are using.

I'm NOT going to try and explain how to extract data from a Database, because that has nothing to do with the RTC SDK, but with the Database and Database Access components you are using. I would suggest, however, that you find a way to get your field data into a TStream, so you can store it asByteStream (similar to the code used above for storing BLOB fields), or ... into an array of bytes, so you can directly assign it to the asByteArray property, or ... as plain String, in which case you can assign it directly to the asText property (to avoid Unicode problems, in case Unicode characters are used). When writing your function for the Server, keep in mind that you will also need to extract that data on the Client.

NOTE: TRtcClientDataSet and TRtcDataSetMonitor components only support flat table structures and do NOT support extracting changes from nested tables (tables inside tables). In other words, if a field in your Database contains an entire Table, you will NOT be able to use the TRtcClientDataSet or the TRtcDataSetMonitor component to extract changes to rows or fields on the inner table.

Best Regards,
Danijel Tkalcec
Logged
zhaohuateng
RTC Expired
*
Posts: 5


« Reply #8 on: July 17, 2017, 11:18:04 PM »

My problem has been resolved, have to say that I encountered a strange question, PostgreSQL database array type field in the FireDAC engine after processing, will be in the form of table in the form, in order to avoid dealing with too complex judgments, I In the SELECT statement inside the use of type conversion, the result set directly processed as a general data type, complex directly converted to JSON format, once again thank you for your patience!
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #9 on: July 18, 2017, 07:26:52 AM »

Thank you for your feedback. I'm glad that the problem is now resolved.

PS. I think that using JSON to retrieve and store complex data is a good solution.

Best Regards,
Danijel Tkalcec

Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.03 seconds with 16 queries.