Code doesn't go into TJsonProxyLoader

Hi,

TJsonProxyLoader is not calling when I go the the property Address.

Code:

procedure TForm1.Button2Click(Sender: TObject);
var
  oRepo: TRepositoryRest<TCustomer>;
  oCustomer: TCustomer;
begin
  oRepo:= TRepositoryRest<TCustomer>.Create(Factories.DataSnapRestConnection.TDataSnapRestConnection.CreateConnecion('localhost', 8080));
  try
    oCustomer:= oRepo.GetItem(self.edtCustomerID.text);

    If assigned(oCustomer)
    then begin
      self.Memo1.Lines.Add(oCustomer.Name);
      if (assigned(oCustomer.CustomerToAddress)) and
         (oCustomer.CustomerToAddress.Count>0)
      then begin
        If (assigned(oCustomer.CustomerToAddress.First.Address))
        then begin
          self.Memo1.Lines.Add(oCustomer.CustomerToAddress.First.Address.StreetName);
        end;
      end;
    end;
  finally
    oRepo.Free;
  end;
end;

On line ...   if (assigned(oCustomer.CustomerToAddress)) and ... the ProxyLoader is calling and set as result

'[{"$type":"Entities.CustomerToAddress.TCustomerToAddress","$id":1,"FCustomerID":1,"FAddressID":501,"FAddressTypeID":1,"FAddress":{"$proxy":"single","key":501,"class":"TCustomerToAddress","member":"FAddress"}}]'


Then when I go to the code ...   If (assigned(oCustomer.CustomerToAddress.First.Address))...the code doesn't go into the Proxyloader.

Any idea why it doesn't go into the Proxyloader?

Regards,
Christophe
 




Hard to tell. What is the mapping of the involved properties? What is the JSON you get in both requests?

Mapping

  [Entity]
  [Table('CUSTOMERS')]
  TCustomer = class (TBaseWithID)
  private
    [Column('FIRSTNAME')]
    FFirstName: String;
    [Column('NAME')]
    FName: String;

    [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAll)]
    [ForeignJoinColumn('CUSTOMERID', [TColumnProp.Required])]
    FCustomerToAddress: Proxy<TList<TCustomerToAddress>>;

    [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAll)]
    [ForeignJoinColumn('CUSTOMERID', [TColumnProp.Required])]
    FCustomerToAddress1: Proxy<TList<TCustomerToAddress1>>;

    [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAll)]
    [ForeignJoinColumn('CUSTOMERID', [TColumnProp.Required])]
    FCustomerToAddress3: Proxy<TList<TCustomerToAddress3>>;

  [Entity]
  [Table('CUSTOMERS2ADDRESSES')]
  [Id('FCustomerID', TIdGenerator.None)]
  [Id('FAddressID', TIdGenerator.None)]
  [Id('FAddressTypeID', TIdGenerator.None)]
  [UniqueKey('CUSTOMERID, ADDRESSID, ADDRESSTYPEID')]

  TCustomerToAddress = class (TObject)
  private
    [Column('CUSTOMERID', [TColumnProp.Required])]
    FCustomerID: Integer;
    [Column('ADDRESSID', [TColumnProp.Required])]
    FAddressID: Integer;
    [Column('ADDRESSTYPEID', [TColumnProp.Required])]
    FAddressTypeID: Integer;

    [Association([TAssociationProp.Lazy], CascadeTypeAll)]
    [JoinColumn('ADDRESSID', [TColumnProp.Required])]
    FAddress: Proxy<TAddress> ;

    procedure SetCustomerID(const Value: Integer);
    procedure SetAddressID(const Value: Integer);
    procedure SetAddressTypeID(const Value: Integer);
    procedure SetAddress(const Value: TAddress );
    function GetAddress: TAddress;

  public

    property CustomerID: Integer read FCustomerID write SetCustomerID;
    property AddressID: Integer read FAddressID write SetAddressID;
    property AddressTypeID: Integer read FAddressTypeID write SetAddressTypeID;


    property Address: TAddress read GetAddress write SetAddress;
  end;


  [Entity]
  [Table('CUSTOMERS2ADDRESSES')]
  [Inheritance(TInheritanceStrategy.SingleTable)] [DiscriminatorColumn('ADDRESSTYPEID', TDiscriminatorType.dtInteger)]
  TCustomerToAddressWithDiscriminator = class(TCustomerToAddress)

  end;

  [Entity]
  [DiscriminatorValue(1)]
  TCustomerToAddress1 = class(TCustomerToAddressWithDiscriminator)

  end;

  [Entity]
  [DiscriminatorValue(3)]
  TCustomerToAddress3 = class(TCustomerToAddressWithDiscriminator)

  end;


  [Entity]
  [Table('ADDRESSES')]
  TAddress = class (TBaseWithID)
  private
    [Column('ADDRESSNAME')]
    FAddessName: String;
    [Column('STREETNAME')]
    FStreetName: String;
    [Column('STREETNUMBER')]
    FStreetNumber: String;
    procedure SetAddessName(const Value: String);
    procedure SetStreetName(const Value: String);
    procedure SetStreetNumber(const Value: String);

  public
    property AddessName: String read FAddessName write SetAddessName;
    property StreetName: String read FStreetName write SetStreetName;
    property StreetNumber: String read FStreetNumber write SetStreetNumber;


  end;


When I call oCustomer:= oRepo.GetItem(self.edtCustomerID.text); I get as jsonvalue

'{"$type":"Entities.Customer.TCustomer","$id":1,"FID":1,"FFirstName":"","FName":"ACKE JOHAN","FCustomerToAddress":{"$proxy":"list","key":1,"class":"TCustomer","member":"FCustomerToAddress"},"FCustomerToAddress1":{"$proxy":"list","key":1,"class":"TCustomer","member":"FCustomerToAddress1"},"FCustomerToAddress3":{"$proxy":"list","key":1,"class":"TCustomer","member":"FCustomerToAddress3"}}'

When I call assigned(oCustomer.CustomerToAddress) I go into the ProxyLoader to get the  CustomerToAddress

'[{"$type":"Entities.CustomerToAddress.TCustomerToAddress","$id":1,"FCustomerID":1,"FAddressID":501,"FAddressTypeID":1,"FAddress":{"$proxy":"single","key":501,"class":"TCustomerToAddress","member":"FAddress"}}]'

When I call assigned(oCustomer.CustomerToAddress.First.Address) , the Address is not loaded by the ProxyLoader. I can get into the TJsonProxyLoader.Create

I hope this information can help.







Your TCustomerToAddress class is not an entity, thus it cannot have mapping attributes in it. I didn't read much further yet into the JSON content you provided, but just having mapping attributes in an non-entity class might bring unexpected results. You should refactor your class to keep mappings only in the actual entity classes. 

Why is TCustomerToAddress class is not an entity?

when I connect with the database(No rest datasnap)  I can get the Address.
Connection is a firedac connection. I test it and I can get to Address.


procedure TForm1.Button6Click(Sender: TObject);
var
  oItem: TCustomer;
  oMng: TObjectManager;
begin
  oMng:= TObjectManager.Create(Connection);
  oItem:= oMng.Find<TCustomer>(edtCountryID.Text);
  try
    If assigned(oItem)
    then begin
      If oItem.CustomerToAddress.Count>0
      then begin
        self.Memo1.Lines.Add(format('%s (%d) : %d - %d - ALL: %s', [oItem.Name,
                                                                    oItem.CustomerToAddress.Count,
                                                                    oItem.ID,
                                                                    oItem.CustomerToAddress.First.CustomerID,
                                                                    oItem.CustomerToAddress.First.Address.StreetName]));
      end;



      If oItem.CustomerToAddress1.Count>0
      then begin
        self.Memo1.Lines.Add(format('%s (%d) : %d - %d - 1: %s', [oItem.Name,
                                                                  oItem.CustomerToAddress1.Count,
                                                                  oItem.ID,
                                                                  oItem.CustomerToAddress1.First.CustomerID,
                                                                  oItem.CustomerToAddress1.First.Address.StreetName]));
      end;

      If oItem.CustomerToAddress3.Count>0
      then begin
        self.Memo1.Lines.Add(format('%s (%d) : %d - %d - 3: %s', [oItem.Name,
                                                                  oItem.CustomerToAddress3.Count,
                                                                  oItem.ID,
                                                                  oItem.CustomerToAddress3.First.CustomerID,
                                                                  oItem.CustomerToAddress3.First.Address.StreetName]));
      end;
    end;
  finally
    oMng.Free;
  end;
end;

I also change the class, but I can't get into the proxyloader in the Rest application.


Refactoring the TCustomerToAddress class:

  [Entity]
  [Table('CUSTOMERS2ADDRESSES')]
  [Id('FCustomerID', TIdGenerator.None)]
  [Id('FAddressID', TIdGenerator.None)]
  [Id('FAddressTypeID', TIdGenerator.None)]
  [UniqueKey('CUSTOMERID, ADDRESSID, ADDRESSTYPEID')]
  [Inheritance(TInheritanceStrategy.SingleTable)] [DiscriminatorColumn('ADDRESSTYPEID', TDiscriminatorType.dtInteger)]
  TCustomerToAddress = class (TBaseWithOutID)
  private
    [Column('CUSTOMERID', [TColumnProp.Required])]
    FCustomerID: Integer;
    [Column('ADDRESSID', [TColumnProp.Required])]
    FAddressID: Integer;
    [Column('ADDRESSTYPEID', [TColumnProp.Required])]
    FAddressTypeID: Integer;

    [Association([TAssociationProp.Lazy], CascadeTypeAll)]
    [JoinColumn('ADDRESSID', [TColumnProp.Required])]
    FAddress: Proxy<TAddress> ;

    procedure SetCustomerID(const Value: Integer);
    procedure SetAddressID(const Value: Integer);
    procedure SetAddressTypeID(const Value: Integer);
    procedure SetAddress(const Value: TAddress );
    function GetAddress: TAddress;

  public

    property CustomerID: Integer read FCustomerID write SetCustomerID;
    property AddressID: Integer read FAddressID write SetAddressID;
    property AddressTypeID: Integer read FAddressTypeID write SetAddressTypeID;


    property Address: TAddress read GetAddress write SetAddress;
  end;


//  [Entity]
//  [Table('CUSTOMERS2ADDRESSES')]
//
//  TCustomerToAddressWithDiscriminator = class(TCustomerToAddress)
//
//  end;

  [Entity]
  [DiscriminatorValue(1)]
  TCustomerToAddress1 = class(TCustomerToAddress)

  end;

  [Entity]
  [DiscriminatorValue(3)]
  TCustomerToAddress3 = class(TCustomerToAddress)

  end;



Sorry, it is an entity, but indeed the [Inheritance] attribute should be in the base class (TCustomerToAddress). It's correct now.


How are you creating the proxy loader and retrieving the object from the JSON?

I'm using the TDSRestConnection


constructor TRepositoryRest<T>.Create(aConnection: TDSRestConnection);
begin
  FClient := TCarfacPlusMethodsClient.Create(aConnection);
  FSerializer := TDataSnapJsonSerializer.Create;
  FDeserializer := TDataSnapJsonDeserializer.Create;

  FDeserializer.ProxyLoader := TJsonProxyLoader.Create(
      function(ProxyInfo: IProxyInfo): TObject
      var
        Serializer: TDataSnapJsonSerializer;
        Deserializer: TDataSnapJsonDeserializer;
        JsonObject: TJsonValue;
      begin
        Serializer:= TDataSnapJsonSerializer.Create;
        Deserializer := TDataSnapJsonDeserializer.Create;

        try
          JsonObject := FClient.RemoteProxyLoad(Serializer.ToJson(ProxyInfo));
          if JsonObject is TJSONArray then
          begin
            Result := Deserializer.FromJson(JsonObject, TList<TObject>);
          end
          else
          begin
            Result := Deserializer.FromJson(JsonObject, TObject);
          end;
      finally
        Deserializer.Free;
        Serializer.Free;
      end;
    end
    );
  FDeserializer.OwnsEntities := true;
end;

Client:
function TCarfacPlusMethodsClient.RemoteProxyLoad(aJsonProxyInfo: TJSONValue; const ARequestFilter: string): TJSONValue;
begin
  if FRemoteProxyLoadCommand = nil then
  begin
    FRemoteProxyLoadCommand := FConnection.CreateCommand;
    FRemoteProxyLoadCommand.RequestType := 'POST';
    FRemoteProxyLoadCommand.Text := 'TCarfacPlusMethods."RemoteProxyLoad"';
    FRemoteProxyLoadCommand.Prepare(TCarfacPlusMethods_RemoteProxyLoad);
  end;
  FRemoteProxyLoadCommand.Parameters[0].Value.SetJSONValue(aJsonProxyInfo, FInstanceOwner);
  FRemoteProxyLoadCommand.Execute(ARequestFilter);
  Result := TJSONValue(FRemoteProxyLoadCommand.Parameters[1].Value.GetJSONValue(FInstanceOwner));
end;


Server:
function TCarfacPlusMethods.RemoteProxyLoad(aJsonProxyInfo: TJsonValue): TJsonValue;
var
  oProxyInfo: IProxyInfo;
  oConnection: IDBConnection;
  oObjectManager: TObjectManager;
begin
  oProxyInfo := FDeserializer.ProxyInfoFromJson(aJsonProxyInfo);

  oConnection:= Factories.FireBirdConnection.TFireBirdConnectionFactory.CreateConnecion(cConnectionString);
  oObjectManager:= TObjectManager.Create(oConnection);
  try
    Result := FSerializer.ToJson(oObjectManager.ProxyLoad(oProxyInfo));
  finally
    oObjectManager.Free;
  end;
end;



Well, you are setting the proxy loader in the first deserializer. Then in the proxy loading implementation, you create another deserializer, which doesn't have a proxy loader. That's why it's not loading it.

Make sure that both deserializers have their proxy loaders set so they can load the proxies.

Nice, It's working.

I've change the code into:

constructor TRepositoryRest.Create(aConnection: TDSRestConnection);
begin
  FClient := TCarfacPlusMethodsClient.Create(aConnection);
  FSerializer := TDataSnapJsonSerializer.Create;
  FDeserializer := TDataSnapJsonDeserializer.Create;
  FJsonProxyLoader:=TJsonProxyLoader.Create(
      function(ProxyInfo: IProxyInfo): TObject
      var
        Serializer: TDataSnapJsonSerializer;
        Deserializer: TDataSnapJsonDeserializer;
        JsonObject: TJsonValue;
      begin
        Serializer:= TDataSnapJsonSerializer.Create;
        Deserializer := TDataSnapJsonDeserializer.Create;
        Deserializer.ProxyLoader:= FJsonProxyLoader;
        try
          JsonObject := FClient.RemoteProxyLoad(Serializer.ToJson(ProxyInfo));
          if JsonObject is TJSONArray then
          begin
            Result := Deserializer.FromJson(JsonObject, TList<TObject>);
          end
          else
          begin
            Result := Deserializer.FromJson(JsonObject, TObject);
          end;
      finally
        Deserializer.Free;
        Serializer.Free;
      end;
    end
    );

  FDeserializer.ProxyLoader := FJsonProxyLoader;
  FDeserializer.OwnsEntities := true;
end;