Browse Source

BUG FIX: ensure datarow variants managed correctly

Herman Schoenfeld 7 years ago
parent
commit
97adfd7b8e

+ 1 - 1
src/core/UDataSources.pas

@@ -15,7 +15,7 @@ type
   TAccountsDataSourceBase = class(TCustomDataSource<TAccount>)
   TAccountsDataSourceBase = class(TCustomDataSource<TAccount>)
     protected
     protected
       function GetItemDisposePolicy : TDisposePolicy; override;
       function GetItemDisposePolicy : TDisposePolicy; override;
-      function GetColumns : TDataColumns;  override;
+      function GetColumns : TDataColumns; override;
     public
     public
       function GetEntityKey(constref AItem: TAccount) : Variant; override;
       function GetEntityKey(constref AItem: TAccount) : Variant; override;
       function GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant; override;
       function GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant; override;

+ 1 - 1
src/gui/wizards/UWIZSendPASC_Confirmation.pas

@@ -47,7 +47,7 @@ type
     private
     private
       FModel : TWIZSendPASCModel;
       FModel : TWIZSendPASCModel;
     protected
     protected
-      function GetColumns : TDataColumns;
+      function GetColumns : TDataColumns; override;
     public
     public
       property Model : TWIZSendPASCModel read FModel write FModel;
       property Model : TWIZSendPASCModel read FModel write FModel;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;

+ 22 - 13
src/libraries/sphere10/UCommon.Data.pas

@@ -63,18 +63,18 @@ const
     class destructor Destroy;
     class destructor Destroy;
   protected type
   protected type
     TColumnMapToIndex = TDictionary<AnsiString, Integer>;
     TColumnMapToIndex = TDictionary<AnsiString, Integer>;
-    TColumnsDictionary = TObjectDictionary<AnsiString, TColumnMapToIndex>;
+    TColumnsDictionary = TObjectDictionary<PtrInt, TColumnMapToIndex>;
   protected class var
   protected class var
     FColumns: TColumnsDictionary;
     FColumns: TColumnsDictionary;
   protected
   protected
-    class function MapColumns(const ADataSourceClassName : AnsiString; const AColumns: TDataColumns): TColumnMapToIndex;
+    class function MapColumns(AClassID : PtrInt; const AColumns: TDataColumns): TColumnMapToIndex;
   public
   public
     function GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean; override;
     function GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean; override;
     function SetProperty(var V: TVarData; const Name: string; const Value: TVarData): Boolean; override;
     function SetProperty(var V: TVarData; const Name: string; const Value: TVarData): Boolean; override;
     procedure Clear(var V: TVarData); override;
     procedure Clear(var V: TVarData); override;
     procedure Copy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean); override;
     procedure Copy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean); override;
     function DoFunction(var Dest: TVarData; const V: TVarData; const Name: string; const Arguments: TVarDataArray): Boolean; override;
     function DoFunction(var Dest: TVarData; const V: TVarData; const Name: string; const Arguments: TVarDataArray): Boolean; override;
-    class function New(const ADataSourceClassName : AnsiString; const AColumns: TDataColumns): Variant;
+    class function New(AClassID : PtrInt; const AColumns: TDataColumns): Variant;
   end;
   end;
 
 
   EDataRow = class(Exception);
   EDataRow = class(Exception);
@@ -178,7 +178,7 @@ const
   TCustomDataSource<T> = class(TComponent, IDataSource)
   TCustomDataSource<T> = class(TComponent, IDataSource)
     private
     private
       FLock : TCriticalSection;
       FLock : TCriticalSection;
-      FClassName : AnsiString;
+      FClassID : PtrInt;
     protected
     protected
       function GetNullPolicy(const AFilter : TColumnFilter) : TSortNullPolicy; virtual;
       function GetNullPolicy(const AFilter : TColumnFilter) : TSortNullPolicy; virtual;
       function GetItemDisposePolicy : TDisposePolicy; virtual; abstract;
       function GetItemDisposePolicy : TDisposePolicy; virtual; abstract;
@@ -282,7 +282,7 @@ begin
   FColumns.Free;
   FColumns.Free;
 end;
 end;
 
 
-class function TDataRow.MapColumns(const ADataSourceClassName : AnsiString; const AColumns: TDataColumns): TColumnMapToIndex;
+class function TDataRow.MapColumns(AClassID : PtrInt; const AColumns: TDataColumns): TColumnMapToIndex;
 var
 var
   i: Integer;
   i: Integer;
 begin
 begin
@@ -290,7 +290,7 @@ begin
   for i := Low(AColumns) to High(AColumns) do
   for i := Low(AColumns) to High(AColumns) do
     Result.Add(AColumns[i].Name, i);
     Result.Add(AColumns[i].Name, i);
   Result.Add('__KEY', i + 1);
   Result.Add('__KEY', i + 1);
-  FColumns.Add(ADataSourceClassName, Result);
+  FColumns.Add(AClassID, Result);
 end;
 end;
 
 
 function TDataRow.GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean;
 function TDataRow.GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean;
@@ -343,10 +343,12 @@ begin
     Variant(Dest) := LRow.vvalues[Variant(Arguments[0])];
     Variant(Dest) := LRow.vvalues[Variant(Arguments[0])];
 end;
 end;
 
 
-class function TDataRow.New(const ADataSourceClassName:AnsiString; const AColumns: TDataColumns): Variant;
+class function TDataRow.New(AClassID:PtrInt; const AColumns: TDataColumns): Variant;
 
 
 var
 var
   LColumnMap: TColumnMapToIndex;
   LColumnMap: TColumnMapToIndex;
+  AKey : AnsiString;
+  i : Integer;
 begin
 begin
   if not Assigned(AColumns) then
   if not Assigned(AColumns) then
     raise EDataRow.Create(sAColumnsCantBeNil);
     raise EDataRow.Create(sAColumnsCantBeNil);
@@ -354,10 +356,17 @@ begin
   VarClear(Result);
   VarClear(Result);
   FillChar(Result, SizeOf(Result), #0);
   FillChar(Result, SizeOf(Result), #0);
   TDataRowData(Result).vtype:=DataRowType.VarType;
   TDataRowData(Result).vtype:=DataRowType.VarType;
-
-  if not FColumns.TryGetValue(ADataSourceClassName, LColumnMap) then
-    LColumnMap := MapColumns(ADataSourceClassName, AColumns);
-
+  if not FColumns.TryGetValue(AClassID, LColumnMap) then
+    LColumnMap := MapColumns(AClassID, AColumns)
+  else begin
+    // Ensure all instances have consistent columns for AClassID
+    if Length(AColumns) <> (LColumnMap.Count - 1) then  // -1 to account for __KEY
+      raise EDataRow.Create('Internal Error: Inconsistent column counts for given AClassID. Ensure AClassID values are unique for each class of constructed TDataRow');
+    for i := Low(AColumns) to High(AColumns) do begin
+      if NOT LColumnMap.ContainsKey(AColumns[i].Name) then
+        raise EDataRow.Create(Format('Internal Error: Inconsistent columns for given AClassID. Unknown column "%s".', [AColumns[i].Name]));
+    end;
+  end;
   TDataRowData(Result).vcolumnmap:=LColumnMap;
   TDataRowData(Result).vcolumnmap:=LColumnMap;
   SetLength(TDataRowData(Result).vvalues, LColumnMap.Count);
   SetLength(TDataRowData(Result).vvalues, LColumnMap.Count);
 end;
 end;
@@ -391,7 +400,7 @@ constructor TCustomDataSource<T>.Create(AOwner: TComponent);
 begin
 begin
   inherited Create(AOwner);
   inherited Create(AOwner);
   FLock := TCriticalSection.Create;
   FLock := TCriticalSection.Create;
-  FClassName := Self.ClassName;
+  FClassID := PtrInt(ClassInfo); // Use pointer to ClassInfo as unique identifier for DataSource
 end;
 end;
 
 
 destructor TCustomDataSource<T>.Destroy;
 destructor TCustomDataSource<T>.Destroy;
@@ -542,7 +551,7 @@ begin
        j := 0;
        j := 0;
        SetLength(ADataTable.Rows, pageEnd - pageStart + 1);
        SetLength(ADataTable.Rows, pageEnd - pageStart + 1);
        for i := pageStart to pageEnd do begin
        for i := pageStart to pageEnd do begin
-         ADataTable.Rows[j] := TDataRow.New(FClassName, ADataTable.Columns);
+         ADataTable.Rows[j] := TDataRow.New(FClassID, ADataTable.Columns);
          DehydrateItem( data[i], ADataTable.Rows[j]);
          DehydrateItem( data[i], ADataTable.Rows[j]);
          ADataTable.Rows[j].__KEY := GetEntityKey(data[i]);
          ADataTable.Rows[j].__KEY := GetEntityKey(data[i]);
          inc(j)
          inc(j)