delphispring4dremobjectsdataabstract

Spring4d: How to "force" the container to believe a class implements an interface


I am using RemObjects DataAbstract along with Spring4d. RemObjects generates a SchemaServer_Intf.pas file that contains interfaces for every kind of table that exists in it's schema. It allows for "Strongly typed" datasets, allowing one to access a field using

(aDataSet as IMyDataSet).MyField := aValue

Here is a snapshot of one of the interfaces generated by DataAbstract

IEntiteType = interface(IDAStronglyTypedDataTable)
  ['{96B82FF7-D087-403C-821A-0323034B4B99}']
    { Property getters and setters }
    function GetEntiteIdValue: String;
    procedure SetEntiteIdValue(const aValue: String);
    function GetEntiteIdIsNull: Boolean;
    procedure SetEntiteIdIsNull(const aValue: Boolean);
    function GetNameValue: WideString;
    procedure SetNameValue(const aValue: WideString);
    function GetNameIsNull: Boolean;
    procedure SetNameIsNull(const aValue: Boolean);
    function GetIsSystemValue: SmallInt;
    procedure SetIsSystemValue(const aValue: SmallInt);
    function GetIsSystemIsNull: Boolean;
    procedure SetIsSystemIsNull(const aValue: Boolean);


    { Properties }
    property EntiteId: String read GetEntiteIdValue write SetEntiteIdValue;
    property EntiteIdIsNull: Boolean read GetEntiteIdIsNull write SetEntiteIdIsNull;
    property Name: WideString read GetNameValue write SetNameValue;
    property NameIsNull: Boolean read GetNameIsNull write SetNameIsNull;
    property IsSystem: SmallInt read GetIsSystemValue write SetIsSystemValue;
    property IsSystemIsNull: Boolean read GetIsSystemIsNull write SetIsSystemIsNull;
  end;

Though, there is one problem. If you cast a dataTable like so:

aDataTable := IEntiteType(TDAMemDataTable.Create(nil));

You'll have an "Interface not supported error"

But, as soon as you do:

aDataTable.LogicalName := 'EntiteType';
aDataTable.BusinessRulesId := MyBusinessRuleID;

You can safely write

aDataTable := IEntiteType(TDAMemDataTable.Create(nil));

And you don't get any error.

So, with Spring4d, I thought of writing this in my registration unit:

aContainer.RegisterType<TDAMemDataTable>.Implements<IEntiteType>.DelegateTo(
   function : TDAMemDataTable
   var aDataTable : TDAMemDataTable; 
   begin
      Result:= TDAMemDataTable.Create(nil);
      Result.LogicalName := 'EntiteType';
      Result.BusinessRulesId := MyBusinessRuleId;         
   end
) 

But then, Spring4d throws (with reason) error :

Exception 'first chance' à $762D5B68. Classe d'exception ERegistrationException avec un message 'Component type "uDAMemDataTable.TDAMemDataTable" incompatible with service type "SchemaClient_Intf.IEntiteType".'. Processus EntiteREM2.exe (3088) 

Is there a way to override this check?


Solution

  • Ok I've found a way to do that. Super simple actually :

      aContainer.RegisterType<IAddress>.DelegateTo(
        function : IAddress
        var aTable : TDAMemDataTable;
        begin
          aTable := TDAMemDataTable.Create(nil);
          aTable.LogicalName := nme_Address;
          aTable.BusinessRulesID := RID_Address;
          Result := aTable as IAddress;
        end
      );
    

    Also, for people interested in registering many tables in an elegant fashion :

    aContainer.RegisterType<IAddress>.DelegateTo(TableConfigurator.GetTableDelegate<IAddress>(nme_Address, RID_Address));
    // Registering other tables here...
    

    Just create some "Helper" class with this method :

    class function TableConfigurator.GetTableDelegate<T>(aLogicalName, aBusinessRulesId: string): TActivatorDelegate<T>;
    begin
      Result := (function: T
        var
          aTable: TDAMemDataTable;
        begin
          aTable := TDAMemDataTable.Create(nil);
          aTable.LogicalName := aLogicalName;
          aTable.BusinessRulesID := aBusinessRulesId;
          Result := T(TValue.From(aTable).AsInterface);
        end);
    end;