Копирование данных из датасета в поля класса/записи

makhaon
Дата: 12.11.2019 10:58:22
Всем добрый день. Написал код копирования данных из любого датасета в поля любых записей или классов. Возможно кому-то будет интересно. В плане возможных доработок, можно было бы обобщить для работы со свойствами и попробовать избавится от var при передаче параметров.

+
unit MakhDBCommon;
//(c) 2019 Makhaon Software

interface

uses
Data.DB,
System.Classes,
System.SysUtils,
System.Rtti;

type
DBFieldAttribute = class(TCustomAttribute)
strict private
FFormatParam: string;
FFieldName: string;
public
constructor Create; overload;
constructor Create(const AFieldName: string); overload;
constructor Create(const AFieldName, AFormatParam: string); overload;
property FieldName: string Read FFieldName;
property FormatParam: string Read FFormatParam;
end;

type
TDSHelper<T> = class
class procedure DataSetToVar(DataSet: TDataSet; var Variable: T; ClearRec: boolean = True);
class function DataSetToRecords(DataSet: TDataSet; OnFoundRec: TNotifyEvent = nil): TArray<T>;
end;

implementation

class procedure TDSHelper<T>.DataSetToVar(DataSet: TDataSet; var Variable: T; ClearRec: boolean = True);
var
Context: TRttiContext;
Fields: TRttiType;
Field: TRttiField;
Attr: TCustomAttribute;
AttrAs: DBFieldAttribute;
DBField: TField;
begin
if ClearRec then
Variable := Default(T);
Context := TRttiContext.Create;
try
Fields := Context.GetType(TypeInfo(T));
for Field in Fields.GetFields do
for Attr in Field.GetAttributes do
if Attr is DBFieldAttribute then
begin
AttrAs := DBFieldAttribute(Attr);
DBField := DataSet.FindField(AttrAs.FieldName);
if Assigned(DBField) then
if (Field.FieldType is TRttiStringType) then
if AttrAs.FormatParam.IsEmpty then
Field.SetValue(@Variable, TValue.From(DBField.AsString.TrimRight))
else
Field.SetValue(@Variable, TValue.From(Format(AttrAs.FormatParam, [DBField.AsString.TrimRight])))
else if Field.FieldType.Handle = TypeInfo(boolean) then
Field.SetValue(@Variable, TValue.From(DBField.AsString.StartsWith('+')))
else
Field.SetValue(@Variable, TValue.From(DBField.AsVariant));
end;
finally
Context.Free;
end;
end;

class function TDSHelper<T>.DataSetToRecords(DataSet: TDataSet; OnFoundRec: TNotifyEvent = nil): TArray<T>;
var
i: integer;
begin
with Dataset do
begin
First;
SetLength(Result, RecordCount);
i := 0;
while not Eof do
begin
TDSHelper<T>.DataSetToVar(Dataset, Result[i]);
if Assigned(OnFoundRec) then
OnFoundRec(DataSet);
Inc(i);
Next;
end;
end;
end;

{ DBFieldAttribute }

constructor DBFieldAttribute.Create;
begin
inherited Create;
FFieldName := '';
end;

constructor DBFieldAttribute.Create(const AFieldName: string);
begin
inherited Create;
FFieldName := AFieldName;
FFormatParam := '';
end;

constructor DBFieldAttribute.Create(const AFieldName, AFormatParam: string);
begin
inherited Create;
FFieldName := AFieldName;
FFormatParam := AFormatParam;
end;

end. 
makhaon
Дата: 12.11.2019 11:05:26
Использование:

type
 TNodeParams = record
  UID:         string;
  [DBField('AE_TITLE')]
  AETitle:     string;
  [DBField('HOSTNAME')]
  HostName:    string;
  Port:        string;
  Description: string;
  IsLite:      string;
  [DBField('IS_MOST_PRIOR')]
  IsMostPrior: string;
  [DBField('ADD_HOSTNAMES')]
  AddIPs:      string;
 end;

FDataset1.FetchAll;
TDSHelper<TNodeParams>.DataSetToRecord(FDataset1, NodeParams);
AlexeyM123
Дата: 12.11.2019 11:12:21
в догонку
DataSet Enumerator от Uwe Raabe
https://www.uweraabe.de/Blog/?s=Dataset Enumerator
makhaon
Дата: 12.11.2019 11:56:31
У меня на XE6 не взлетел, увы. Синтаксиса не хватает. Пришлось свой делать.
DimaBr
Дата: 12.11.2019 12:09:47
makhaon
У меня на XE6 не взлетел, увы. Синтаксиса не хватает. Пришлось свой делать.

У меня D7 и вся ваша примочка не взлетела
makhaon
Дата: 12.11.2019 12:43:45
DimaBr,

"В Delphi 2010 появились пользовательские атрибуты, которые можно добавить к объявлениям типов и методам"

D2010 минимум. Меньше, увы, никак.
AlexeyM123
Дата: 12.11.2019 12:53:44
я к сожалению не могу найти ссылку, но Uwe Raabe писал,
что вся его конструкция работает на >= XE8 только
_Vasilisk_
Дата: 12.11.2019 16:07:36
Как минимум, я бы один раз сделал сопоставление поля датасета <=> поля записи, а потом бы уже в цикле вызывал
makhaon
TDSHelper<T>.DataSetToVar(Dataset, Result[i]);
ну и вот это
makhaon
SetLength(Result, RecordCount);
работать не будет. Нужно записи складывать в TList<T>, а потом делать
Result := List.ToArray;


Далее, код будет работать только для Record. Для объектов классов будет AV

Ну и остальное по мелочи
makhaon
Дата: 12.11.2019 20:27:50
_Vasilisk_,

автор
SetLength(Result, RecordCount);


Это работает точно. Ну а вообще правки принимаются.
_Vasilisk_
Дата: 12.11.2019 22:12:25
makhaon
Это работает точно.
Значит (не) повезло с датасетом. Попробуй на IBX