Ver Mensaje Individual
  #6  
Antiguo 28-04-2019
manelb manelb is offline
Miembro
 
Registrado: mar 2017
Posts: 280
Reputación: 8
manelb Va por buen camino
OnValidate para Integer devuelve 0

El segundo problema es que en el mismo evento onvalidate, los campos de tipo integer i smallint siempre devuelven 0 cuando se accede a su valor para proceder a su validación.
El siguiente evento
Código Delphi [-]
procedure TForm1.Validate(Sender: TField);
begin
  ListBoxSalida.Items.Add('Validate: '+sender.FieldName+': '+sender.AsString);
end;
conectado a campos de diferentes tipos e introduciendo siempre el valor ‘100’, devolvería el siguiente resultado:

Validate: SMALLINT: 0
Validate: INTEGER: 0
Validate: BIGINTEGER: 100
Validate: FLOAT: 100
Validate: NUMERIC: 100
Validate: CHAR_5: 100
Validate: VARCHAR_10: 100

Sobra decir que si utilizamos Firedac el resultado es el correcto, por lo que para investigar hemos tratado de comparar el comportamiento de los dos sistemas.

Parece ser que el problema se origina en el siguiente procedimiento de la unidad Data.DB debido a que la variable FValidating siempre es False cuando procede de FibPlus
Código Delphi [-]
function TIntegerField.GetValue(var Value: Longint): Boolean;
Begin
  if (FIOBuffer <> nil) and not FValidating then
    TDBBitConverter.UnsafeFrom(0, FIOBuffer);
  Result := GetData(FIOBuffer);
  if Result then
    case DataType of
      ftShortint:
        Value := TDBBitConverter.UnsafeInto(FIOBuffer);
      ftByte:
        Value := TDBBitConverter.UnsafeInto(FIOBuffer);
      ftSmallint:
        Value := TDBBitConverter.UnsafeInto(FIOBuffer);
      ftWord:
        Value := TDBBitConverter.UnsafeInto(FIOBuffer);
      ftLongWord:
        Value := TDBBitConverter.UnsafeInto(FIOBuffer);
      Else
        Value := TDBBitConverter.UnsafeInto(FIOBuffer);
      end;
end;


Investigando a quien corresponde la responsabilidad de mantener el valor de la variable FValidating nos encontramos con que en Firedac, se ejecuta el siguiente código(uno u otro en función del parámetro Buffer)
Código Delphi [-]
procedure TField.Validate(Buffer: TValueBuffer);
var
  prevBuffer: TValueBuffer;
begin
  if Assigned(OnValidate) then
  begin
    { Use the already assigned FValueBuffer if set }
    if FValueBuffer = nil then
      FValueBuffer := Buffer;
    FValueBuffer := Copy(FValueBuffer);
    prevBuffer := Copy(Buffer);
    FValidating := True;
    try
      OnValidate(Self);
    finally
      FValidating := False;
      Move(prevBuffer[0], Buffer[0], Length(Buffer));
    end;
  end;
end;

{$IFNDEF NEXTGEN}
procedure TField.Validate(Buffer: Pointer);
begin
  if Assigned(OnValidate) then
  begin
    { Use the already assigned FValueBufferPtr if set }
    if FValueBufferPtr = nil then
      FValueBufferPtr := Buffer;
    FValidating := True;
    try
      OnValidate(Self);
    finally
      FValidating := False;
    end;
  end;
end;
{$ENDIF !NEXTGEN}

Mientras que en los Fibs el trabajo equivalente lo realiza el siguiente procedimiento de la unidad FIBDataset, que no tiene en cuenta la variable FValidating, ya que utiliza un sistema diferente para controlar el estado de validación del campo
Código Delphi [-]
procedure TFIBCustomDataSet.DoFieldValidate(Field:TField;Buffer:Pointer);
begin
  if Assigned(Field.OnValidate) then
  begin
   Include(FRunState,drsInFieldValidate);
   Try
    FValidatingFieldBuffer:=Buffer;
    FValidatedField:=Field;
    FValidatedRec:= ActiveRecord;
    Field.OnValidate(Field);
   Finally
     Exclude(FRunState,drsInFieldValidate);
     FValidatingFieldBuffer:=nil;
   end;
  end;
end;


Llegados a este punto desearía conocer algunas opiniones y si os parece acertado o no el razonamiento que hemos hecho.

Soluciones???

Nosotros proponemos una pequeña modificación en TFIBCustomDataSet.DoFieldValidate de los fibs, introduciendo 3 linieas de código en el inicio del evento para que quede tal que así:
Código Delphi [-]
procedure TFIBCustomDataSet.DoFieldValidate(Field:TField;Buffer:Pointer);
begin
  if field is TIntegerField then  //lineas añadidas
    Field.Validate(Buffer)          // para solucionar problema onvalidate
  else                                             //en campos de tipo integer y smallint

  if Assigned(Field.OnValidate) then
  begin
   Include(FRunState,drsInFieldValidate);
   Try
    FValidatingFieldBuffer:=Buffer;
    FValidatedField:=Field;
    FValidatedRec:= ActiveRecord;
    Field.OnValidate(Field);
   Finally
     Exclude(FRunState,drsInFieldValidate);
     FValidatingFieldBuffer:=nil;
   end;
  end;
end;

De esta forma, el tratamiento de los campos integer lo delegamos al mismo procedimiento que usan los Firedac, y el resto de tipos de campos que no presentan error se siguen tratando igual que hasta ahora.
Que os parece??

Gracias por aguantar el rollo ….
Saludos a todos
Responder Con Cita