2018-01-29 17:30:53 +01:00
|
|
|
// ***************************************************************************
|
|
|
|
//
|
|
|
|
// Delphi MVC Framework
|
|
|
|
//
|
|
|
|
// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team
|
|
|
|
//
|
|
|
|
// https://github.com/danieleteti/delphimvcframework
|
|
|
|
//
|
|
|
|
// ***************************************************************************
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// *************************************************************************** }
|
|
|
|
|
2017-04-24 00:19:53 +02:00
|
|
|
unit MVCFramework.DataSet.Utils;
|
|
|
|
|
2018-01-29 17:30:53 +01:00
|
|
|
{$I dmvcframework.inc}
|
|
|
|
|
2017-04-24 00:19:53 +02:00
|
|
|
interface
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
uses
|
|
|
|
System.SysUtils,
|
|
|
|
Data.DB,
|
|
|
|
System.Generics.Collections,
|
|
|
|
System.JSON,
|
|
|
|
System.Rtti,
|
|
|
|
JsonDataObjects,
|
|
|
|
MVCFramework.Commons,
|
|
|
|
MVCFramework.Serializer.Commons;
|
2017-04-24 00:19:53 +02:00
|
|
|
|
|
|
|
type
|
|
|
|
TFieldNamePolicy = (fpLowerCase, fpUpperCase, fpAsIs);
|
|
|
|
|
|
|
|
TDataSetHelper = class helper for TDataSet
|
|
|
|
public
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure LoadFromTValue(const Value: TValue; const aNameCase: TMVCNameCase = TMVCNameCase.ncLowerCase);
|
2017-09-28 00:14:34 +02:00
|
|
|
function AsJSONArray: string;
|
2017-04-24 00:19:53 +02:00
|
|
|
function AsJSONArrayString: string; deprecated 'Use AsJSONArray';
|
2017-09-28 00:14:34 +02:00
|
|
|
function AsJSONObject(AFieldNamePolicy: TFieldNamePolicy = fpLowerCase): string;
|
2017-04-24 00:19:53 +02:00
|
|
|
function AsJSONObjectString: string; deprecated 'Use AsJSONObject';
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure LoadFromJSONObject(AJSONObject: TJSONObject; AFieldNamePolicy: TFieldNamePolicy = fpLowerCase); overload;
|
|
|
|
procedure LoadFromJSONObject(AJSONObject: TJSONObject; AIgnoredFields: TArray<string>;
|
2017-04-24 00:19:53 +02:00
|
|
|
AFieldNamePolicy: TFieldNamePolicy = fpLowerCase); overload;
|
2017-09-28 00:14:34 +02:00
|
|
|
procedure LoadFromJSONArray(AJSONArray: string;
|
2018-07-16 12:34:07 +02:00
|
|
|
AFieldNamePolicy: TFieldNamePolicy = TFieldNamePolicy.fpLowerCase); overload;
|
|
|
|
procedure LoadFromJSONArrayString(AJSONArrayString: string; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy = TFieldNamePolicy.fpLowerCase); overload;
|
2017-04-24 00:19:53 +02:00
|
|
|
procedure LoadFromJSONArrayString(AJSONArrayString: string;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy = TFieldNamePolicy.fpLowerCase); overload;
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure LoadFromJSONArray(AJSONArray: TJSONArray; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy = TFieldNamePolicy.fpLowerCase); overload;
|
2017-04-24 00:19:53 +02:00
|
|
|
procedure LoadFromJSONObjectString(AJSONObjectString: string); overload;
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure LoadFromJSONObjectString(AJSONObjectString: string; AIgnoredFields: TArray<string>); overload;
|
2017-04-24 00:19:53 +02:00
|
|
|
procedure AppendFromJSONArrayString(AJSONArrayString: string); overload;
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure AppendFromJSONArrayString(AJSONArrayString: string; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy = TFieldNamePolicy.fpLowerCase); overload;
|
|
|
|
function AsObjectList<T: class, constructor>(CloseAfterScroll: boolean = false): TObjectList<T>;
|
|
|
|
function AsObject<T: class, constructor>(CloseAfterScroll: boolean = false): T;
|
2017-04-24 00:19:53 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
TDataSetUtils = class sealed
|
|
|
|
private
|
|
|
|
class var CTX: TRttiContext;
|
|
|
|
public
|
|
|
|
class constructor Create;
|
|
|
|
class destructor Destroy;
|
|
|
|
class procedure DataSetToObject(ADataSet: TDataSet; AObject: TObject);
|
2018-07-16 12:34:07 +02:00
|
|
|
class procedure DataSetToObjectList<T: class, constructor>(ADataSet: TDataSet; AObjectList: TObjectList<T>;
|
2017-04-24 00:19:53 +02:00
|
|
|
ACloseDataSetAfterScroll: boolean = True);
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
[MVCNameCase(ncLowerCase)]
|
|
|
|
TDataSetHolder = class
|
|
|
|
private
|
|
|
|
FDataSet: TDataSet;
|
|
|
|
FMetadata: TMVCStringDictionary;
|
|
|
|
public
|
|
|
|
constructor Create(ADataSet: TDataSet); virtual;
|
|
|
|
destructor Destroy; override;
|
|
|
|
property Items: TDataSet read FDataSet;
|
|
|
|
[MVCNameAs('meta')]
|
|
|
|
property Metadata: TMVCStringDictionary read FMetadata;
|
|
|
|
end;
|
|
|
|
|
2017-04-24 00:19:53 +02:00
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
2018-07-16 12:34:07 +02:00
|
|
|
MVCFramework.Serializer.JsonDataObjects,
|
2017-04-24 00:19:53 +02:00
|
|
|
MVCFramework.Serializer.Intf;
|
|
|
|
|
|
|
|
{ TDataSetHelper }
|
|
|
|
|
2017-09-28 00:14:34 +02:00
|
|
|
procedure TDataSetHelper.LoadFromTValue(const Value: TValue; const aNameCase: TMVCNameCase);
|
|
|
|
var
|
|
|
|
lSer: TMVCJsonDataObjectsSerializer;
|
|
|
|
begin
|
2018-07-16 12:34:07 +02:00
|
|
|
if not({$IFDEF TOKYOORBETTER}Value.IsObjectInstance and {$ENDIF} (Value.AsObject is TJSONArray)) then
|
2017-09-28 00:14:34 +02:00
|
|
|
raise Exception.Create('LoadFromTValue requires a TValue containing a TJDOJsonArray');
|
|
|
|
|
|
|
|
lSer := TMVCJsonDataObjectsSerializer.Create;
|
|
|
|
try
|
2018-07-16 12:34:07 +02:00
|
|
|
lSer.JsonArrayToDataSet(TJSONArray(Value.AsObject), Self, [], TMVCNameCase.ncLowerCase);
|
2017-09-28 00:14:34 +02:00
|
|
|
finally
|
|
|
|
lSer.Free;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TDataSetHelper.AsJSONArray: string;
|
2017-04-24 00:19:53 +02:00
|
|
|
var
|
|
|
|
lSerializer: IMVCSerializer;
|
|
|
|
begin
|
|
|
|
Result := '[]';
|
|
|
|
if not Eof then
|
|
|
|
begin
|
|
|
|
lSerializer := TMVCJsonDataObjectsSerializer.Create;
|
|
|
|
Result := lSerializer.SerializeDataSet(Self, [], ncLowerCase);
|
|
|
|
// TDataSetUtils.DataSetToJSONArray(Self, JArr, false);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TDataSetHelper.AsJSONArrayString: string;
|
|
|
|
begin
|
|
|
|
Result := AsJSONArray;
|
|
|
|
end;
|
|
|
|
|
2017-09-28 00:14:34 +02:00
|
|
|
function TDataSetHelper.AsJSONObject(AFieldNamePolicy: TFieldNamePolicy): string;
|
2017-04-24 00:19:53 +02:00
|
|
|
var
|
|
|
|
lSerializer: IMVCSerializer;
|
|
|
|
begin
|
|
|
|
lSerializer := TMVCJsonDataObjectsSerializer.Create;
|
|
|
|
Result := lSerializer.SerializeDataSetRecord(Self, [], ncAsIs);
|
|
|
|
// Mapper.DataSetToJSONObject(Self, JObj, false);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TDataSetHelper.AsJSONObjectString: string;
|
|
|
|
begin
|
|
|
|
Result := AsJSONObject(fpLowerCase);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TDataSetHelper.AsObject<T>(CloseAfterScroll: boolean): T;
|
|
|
|
var
|
|
|
|
Obj: T;
|
|
|
|
begin
|
|
|
|
if not Self.Eof then
|
|
|
|
begin
|
|
|
|
Obj := T.Create;
|
|
|
|
try
|
|
|
|
TDataSetUtils.DataSetToObject(Self, Obj);
|
|
|
|
Result := Obj;
|
|
|
|
except
|
|
|
|
FreeAndNil(Obj);
|
|
|
|
raise;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TDataSetHelper.AsObjectList<T>(CloseAfterScroll: boolean): TObjectList<T>;
|
|
|
|
var
|
|
|
|
Objs: TObjectList<T>;
|
|
|
|
begin
|
|
|
|
Objs := TObjectList<T>.Create(True);
|
|
|
|
try
|
|
|
|
TDataSetUtils.DataSetToObjectList<T>(Self, Objs, CloseAfterScroll);
|
|
|
|
Result := Objs;
|
|
|
|
except
|
|
|
|
FreeAndNil(Objs);
|
|
|
|
raise;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.LoadFromJSONArray(AJSONArray: string; AFieldNamePolicy: TFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
var
|
|
|
|
lSerializer: IMVCSerializer;
|
|
|
|
begin
|
|
|
|
Self.DisableControls;
|
|
|
|
try
|
|
|
|
lSerializer := TMVCJsonDataObjectsSerializer.Create;
|
|
|
|
lSerializer.DeserializeDataSet(AJSONArray, Self, nil, ncAsIs);
|
|
|
|
// Mapper.JSONArrayToDataSet(AJSONArray, Self, TArray<string>.Create(), false,
|
|
|
|
// AFieldNamePolicy);
|
|
|
|
finally
|
|
|
|
Self.EnableControls;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.LoadFromJSONArray(AJSONArray: TJSONArray; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
begin
|
|
|
|
Self.DisableControls;
|
|
|
|
try
|
2018-03-14 12:30:41 +01:00
|
|
|
raise Exception.Create('Not Implemented');
|
2017-04-24 00:19:53 +02:00
|
|
|
finally
|
|
|
|
Self.EnableControls;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.LoadFromJSONArrayString(AJSONArrayString: string; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
begin
|
|
|
|
AppendFromJSONArrayString(AJSONArrayString, AIgnoredFields, AFieldNamePolicy);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDataSetHelper.LoadFromJSONArrayString(AJSONArrayString: string; AFieldNamePolicy: TFieldNamePolicy);
|
|
|
|
begin
|
2017-09-28 00:14:34 +02:00
|
|
|
AppendFromJSONArrayString(AJSONArrayString, TArray<string>.Create(), AFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.AppendFromJSONArrayString(AJSONArrayString: string; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
begin
|
|
|
|
LoadFromJSONArray(AJSONArrayString, AFieldNamePolicy);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDataSetHelper.AppendFromJSONArrayString(AJSONArrayString: string);
|
|
|
|
begin
|
|
|
|
AppendFromJSONArrayString(AJSONArrayString, TArray<string>.Create());
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.LoadFromJSONObject(AJSONObject: TJSONObject; AIgnoredFields: TArray<string>;
|
|
|
|
AFieldNamePolicy: TFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
begin
|
|
|
|
raise Exception.Create('Not Implemented');
|
|
|
|
// Mapper.JSONObjectToDataSet(AJSONObject, Self, AIgnoredFields, false,
|
|
|
|
// AFieldNamePolicy);
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.LoadFromJSONObjectString(AJSONObjectString: string; AIgnoredFields: TArray<string>);
|
2017-04-24 00:19:53 +02:00
|
|
|
var
|
|
|
|
lSerializer: IMVCSerializer;
|
|
|
|
begin
|
|
|
|
lSerializer := TMVCJsonDataObjectsSerializer.Create;
|
|
|
|
lSerializer.DeserializeDataSetRecord(AJSONObjectString, Self, nil, ncAsIs);
|
|
|
|
|
|
|
|
// JV := TJSONObject.ParseJSONValue(AJSONObjectString);
|
|
|
|
// try
|
|
|
|
// if JV is TJSONObject then
|
|
|
|
// LoadFromJSONObject(TJSONObject(JV), AIgnoredFields)
|
|
|
|
// else
|
|
|
|
// raise EMapperException.Create
|
|
|
|
// ('Extected JSONObject in LoadFromJSONObjectString');
|
|
|
|
// finally
|
|
|
|
// JV.Free;
|
|
|
|
// end;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
procedure TDataSetHelper.LoadFromJSONObject(AJSONObject: TJSONObject; AFieldNamePolicy: TFieldNamePolicy);
|
2017-04-24 00:19:53 +02:00
|
|
|
begin
|
|
|
|
LoadFromJSONObject(AJSONObject, TArray<string>.Create());
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDataSetHelper.LoadFromJSONObjectString(AJSONObjectString: string);
|
|
|
|
begin
|
|
|
|
LoadFromJSONObjectString(AJSONObjectString, TArray<string>.Create());
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TDataSetUtils }
|
|
|
|
|
|
|
|
class constructor TDataSetUtils.Create;
|
|
|
|
begin
|
|
|
|
TDataSetUtils.CTX := TRttiContext.Create;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
class procedure TDataSetUtils.DataSetToObject(ADataSet: TDataSet; AObject: TObject);
|
2017-04-24 00:19:53 +02:00
|
|
|
var
|
|
|
|
_type: TRttiType;
|
|
|
|
_fields: TArray<TRttiProperty>;
|
|
|
|
_field: TRttiProperty;
|
|
|
|
_attribute: TCustomAttribute;
|
|
|
|
_dict: TDictionary<string, string>;
|
|
|
|
_keys: TDictionary<string, boolean>;
|
|
|
|
mf: MVCColumnAttribute;
|
|
|
|
field_name: string;
|
|
|
|
Value: TValue;
|
|
|
|
FoundAttribute: boolean;
|
|
|
|
FoundTransientAttribute: boolean;
|
2018-03-14 12:30:41 +01:00
|
|
|
LField: TField;
|
2017-04-24 00:19:53 +02:00
|
|
|
begin
|
|
|
|
_dict := TDictionary<string, string>.Create();
|
|
|
|
_keys := TDictionary<string, boolean>.Create();
|
|
|
|
_type := CTX.GetType(AObject.ClassInfo);
|
|
|
|
_fields := _type.GetProperties;
|
|
|
|
for _field in _fields do
|
|
|
|
begin
|
|
|
|
FoundAttribute := false;
|
|
|
|
FoundTransientAttribute := false;
|
|
|
|
for _attribute in _field.GetAttributes do
|
|
|
|
begin
|
|
|
|
if _attribute is MVCColumnAttribute then
|
|
|
|
begin
|
|
|
|
FoundAttribute := True;
|
|
|
|
mf := MVCColumnAttribute(_attribute);
|
|
|
|
_dict.Add(_field.Name, mf.FieldName);
|
|
|
|
_keys.Add(_field.Name, mf.IsPK);
|
|
|
|
end
|
|
|
|
else if _attribute is MVCDoNotSerializeAttribute then
|
|
|
|
FoundTransientAttribute := True;
|
|
|
|
end;
|
|
|
|
if ((not FoundAttribute) and (not FoundTransientAttribute)) then
|
|
|
|
begin
|
|
|
|
_dict.Add(_field.Name, _field.Name);
|
|
|
|
_keys.Add(_field.Name, false);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
for _field in _fields do
|
|
|
|
begin
|
2018-03-14 12:30:41 +01:00
|
|
|
|
2017-04-24 00:19:53 +02:00
|
|
|
if not _dict.TryGetValue(_field.Name, field_name) then
|
|
|
|
Continue;
|
2018-03-14 12:30:41 +01:00
|
|
|
|
|
|
|
LField := ADataSet.FindField(field_name);
|
|
|
|
|
|
|
|
if not Assigned(LField) then
|
|
|
|
Continue;
|
|
|
|
|
2017-04-24 00:19:53 +02:00
|
|
|
case _field.PropertyType.TypeKind of
|
|
|
|
tkEnumeration: // tristan
|
|
|
|
begin
|
|
|
|
if _field.PropertyType.Handle = TypeInfo(boolean) then
|
|
|
|
begin
|
2018-03-14 12:30:41 +01:00
|
|
|
case LField.DataType of
|
2017-04-24 00:19:53 +02:00
|
|
|
ftInteger, ftSmallint, ftLargeint:
|
|
|
|
begin
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := (LField.AsInteger = 1);
|
2017-04-24 00:19:53 +02:00
|
|
|
end;
|
|
|
|
ftBoolean:
|
|
|
|
begin
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := LField.AsBoolean;
|
2017-04-24 00:19:53 +02:00
|
|
|
end;
|
|
|
|
else
|
|
|
|
Continue;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
tkInteger:
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := LField.AsInteger;
|
2017-04-24 00:19:53 +02:00
|
|
|
tkInt64:
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := LField.AsLargeInt;
|
2017-04-24 00:19:53 +02:00
|
|
|
tkFloat:
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := LField.AsFloat;
|
2017-04-24 00:19:53 +02:00
|
|
|
tkString:
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := LField.AsString;
|
2017-04-24 00:19:53 +02:00
|
|
|
tkUString, tkWChar, tkLString, tkWString:
|
2018-03-14 12:30:41 +01:00
|
|
|
Value := LField.AsWideString;
|
2017-04-24 00:19:53 +02:00
|
|
|
else
|
|
|
|
Continue;
|
|
|
|
end;
|
|
|
|
_field.SetValue(AObject, Value);
|
|
|
|
end;
|
|
|
|
_dict.Free;
|
|
|
|
_keys.Free;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
class procedure TDataSetUtils.DataSetToObjectList<T>(ADataSet: TDataSet; AObjectList: TObjectList<T>;
|
|
|
|
ACloseDataSetAfterScroll: boolean);
|
2017-04-24 00:19:53 +02:00
|
|
|
var
|
|
|
|
Obj: T;
|
|
|
|
SavedPosition: TArray<Byte>;
|
|
|
|
begin
|
|
|
|
ADataSet.DisableControls;
|
|
|
|
try
|
|
|
|
SavedPosition := ADataSet.Bookmark;
|
|
|
|
while not ADataSet.Eof do
|
|
|
|
begin
|
|
|
|
Obj := T.Create;
|
|
|
|
DataSetToObject(ADataSet, Obj);
|
|
|
|
AObjectList.Add(Obj);
|
|
|
|
ADataSet.Next;
|
|
|
|
end;
|
|
|
|
if ADataSet.BookmarkValid(SavedPosition) then
|
|
|
|
ADataSet.Bookmark := SavedPosition;
|
|
|
|
finally
|
|
|
|
ADataSet.EnableControls;
|
|
|
|
end;
|
|
|
|
if ACloseDataSetAfterScroll then
|
|
|
|
ADataSet.Close;
|
|
|
|
end;
|
|
|
|
|
|
|
|
class destructor TDataSetUtils.Destroy;
|
|
|
|
begin
|
|
|
|
CTX.Free;
|
|
|
|
end;
|
|
|
|
|
2018-07-16 12:34:07 +02:00
|
|
|
{ TDataSetHolder }
|
|
|
|
|
|
|
|
constructor TDataSetHolder.Create(ADataSet: TDataSet);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FDataSet := ADataSet;
|
|
|
|
FMetadata := TMVCStringDictionary.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TDataSetHolder.Destroy;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
FMetadata.Free;
|
|
|
|
end;
|
|
|
|
|
2017-04-24 00:19:53 +02:00
|
|
|
end.
|