Ver Mensaje Individual
  #3  
Antiguo 03-05-2020
Avatar de gatosoft
[gatosoft] gatosoft is offline
Miembro Premium
 
Registrado: may 2003
Ubicación: Bogotá, Colombia
Posts: 833
Reputación: 21
gatosoft Va camino a la fama
Cita:
Empezado por javicho_villa Ver Mensaje
Buenos dias con todos, siempre es un gusto contactarlos, en esta oportunidad estoy tratando de hacer una función generica Get para que me devuelva un objeto de cualquier tipo a traves de una consulta con un Query.

Es una iniciativa interesante, en mi opinión es un inicio hacia un ORM, lo cual no es algo tan sencillo de implementar,y menos si no se trabaja con una concepción mas orientada a objetos, es decir el get es solo una parte de todo el proceso.

Me llama la atención lo que haces con el prefijo i para identificar las propiedades del objeto, o el mismo hecho de que la. En lugar de eso, te invito a que revises la documentación asociada a TCustomAttribute

es la forma como puedes etiquetar tus clases, métodos, propiedades y darles un tratamiento.

Por ejemplo


Código Delphi [-]
[TMyORMField('GEN_CLIENTES')]
TCliente = Class(TObject)
Private
Public
end;


Con esto, puedes indicarle a tu sistema que la clase definida tiene un atributo de tipo TMyORMField (creado por ti) que indica algo, en este caso tu decides que te va a representar el nombre de la tabla en la base de datos. con esto, tu clase y tu tabla pueden llamarse diferente.


De igual forma puedes agregar atributos a tus "Fields" y "properties", asi:

Código Delphi [-]
    [TMyORMField('GEN_CLIENTES')]
    TCliente = Class(TObject)
    Private

    Public

    [TMyORMField('CLI_RUT')]
  RUT: String; //este es un field, a diferencia de las properties, no tienen getters and setters
    
    [TMyORMField('CLI_ID',True,True)]  
  property Id_Cliente:Integer  read FId_Cliente           write FId_Cliente;
    
  [TMyORMField('CLI_RAZONSOCIAL')]   
  property RazonSocial:String           read FRazonSocial          write FRazonSocial;
  
  Property FechaConstitucion: TDateTime read FFechaConstitucion write FFechaConstitucion
    
  [TMyORMField('CLI_RAZONSOCIALCORTA')]   
  property RazonSocialCorta:String      read FRazonSocialCorta     write FRazonSocialCorta;
  
  end;

Mira que aqui utilizo el mismo atributo TMyORMField (podrian ser diferentes), con la diferencia que uno opera sobre la clase, indicando el nombre de la tabla y el otro indica el nombre del campo y otros datos.

Fijate tambien que los atributos pueden ir sobre properties o sobre fields.

En el caso del ID_Cliente, recibe dos parametros mas de tipo boleano (o cualquier tipo), tu decides que interpretación le puedes dar, por ejemplo, el primero podria indicar si es un campo requerido dentro de tu tabla y el segundo si es parte de la llave primaria.

Otra opcion es utilizar multi-atributos para indicar esto

Código Delphi [-]
[TMyORMField('CLI_RAZONSOCIAL')] 
[TMyORMFieldFlags('ESLLAVE')] 
Property Id_Cliente


Para llegar a estom a grosso modo debes definir una calse para el atributo:

Código Delphi [-]
TMyORMField = class(TCustomAttribute)
  private
    FIsKeyField: Boolean;
    FFieldName: String;
    FIsNotNull: Boolean;
    procedure SetFieldName(const Value: String);
    procedure SetIsKeyField(const Value: Boolean);
    procedure SetIsNotNulll(const Value: Boolean);
  public
   Property FieldName: String read FFieldName write SetFieldName;
   Property IsNotNull: Boolean read FIsNotNull write SetIsNotNulll;
   Property IsKeyField: Boolean read FIsKeyField write SetIsKeyField;
   Constructor Create(pFieldName: String;
                      pIsNotNulll: Boolean = False;
                      pIsKey: Boolean = False
                      ); reintroduce;
   Procedure Inicializar; //digamos para asignar valores por defecto
  end;//TMyORMField


Posteriomente en tu "getObjeto(pObjeto: TObject)"

debes revisar estas properties:


Código Delphi [-]
function TMyORMEngine.getAtributosObjeto: Boolean; 
//TMyORMEngine, deberia ser la clase que hace todo el trabajo sucio con los objetos
var ctx : TRttiContext;
    rt : TRttiType;
    prop : TRttiProperty;
    xfield: TRttiField;
    Attr: TCustomAttribute;
  
  
    vCampos: TCampo; //puedes guardar lo que encuentres en una lista de campos... 
                   //TList, o en un StringList (depende de lo que quieras) 
begin
 Result:= False;

 if not Assigned(Objeto) then //objeto es una variable que le asignas a tu TMyORMEngine
    exit;

 FTabla:='';
 LimpiarCampos; //la lista

 Try
   ctx := TRttiContext.Create();
   rt := ctx.GetType(Objeto.ClassType);
   
   //buscas los atributos de tus properties
   for prop in rt.GetProperties() do
     for Attr in prop.GetAttributes() do
        if Attr is TMyORMField then
           Begin
             Result:= True;
             vCampos:= TCampo.Create;       
       //le indico que esto es una property. este valor lo defiens tu con
       // TipoCampo = (ptProperty, ptField)
             vCampos.PropType:= ptProperty; 
             vCampos.Name:= prop.Name;
             vCampos.FieldName:= (Attr as TMyORMField).FieldName;
             vCampos.IsNotNull:= (Attr as TMyORMField).IsNotNull;
             vCampos.IsKeyField:= (Attr as TMyORMField).IsKeyField;
             FlstCampos.Add(vCampos);
           End; //if

    //buscas los atributos de tus Fields
    for xfield in rt.GetFields do
      for Attr in xfield.GetAttributes do
        if Attr is TMyORMField then
           Begin
             Result:= True;
             vCampos:= TgtsCampo.Create;
             vCampos.PropType:= ptField;
             vCampos.Name:= xfield.Name;
             vCampos.FieldName:= (Attr as TMyORMField).FieldName;
             vCampos.IsNotNull:= (Attr as TMyORMField).IsNotNull;
             vCampos.IsKeyField:= (Attr as TMyORMField).IsKeyField;
             FlstCampos.Add(vCampos);
           End; //if

    //buscas los atributos de la clase
    for Attr in rt.GetAttributes do
      if Attr is TMyORMField then
         begin
          Result:= True;
          FTabla:= (Attr as TMyORMField).FFieldName;
         end;
 Finally
   ctx.Free;
 End;//Try..finally
end;

la asignacion de valores , que es lo que creo que quieres la harias con


Código Delphi [-]
procedure TMyORMEngine.AsignarPropiedades(pDataSet: TDataSet);
Var i:Integer;
    vCampo: TgtsCampo;
begin

  if (not Assigned(pDataSet)) or
     (not pDataSet.Active) or
     (pDataSet.IsEmpty) then
     exit;

  if not Assigned(Objeto) then
     exit;

  pDataSet.First; //solo el primer registro... para el demo
  For i:=0 to pDataSet.FieldCount-1 do
  begin
    vCampo:= getCampoByFieldName(pDataSet.Fields[i].FieldName); //tares el campo listado
    if Assigned(vCampo) then
       begin
        if vCampo.IsKeyField then
           Continue;

        if vCampo.PropType = ptProperty then
           SetPropValue(vCampo.Name, pDataSet.Fields[i].Value);

        if vCampo.PropType = ptField then
           SetFieldValue(vCampo.Name, pDataSet.Fields[i].Value);
       end;
  end;//for i

end;

SetField y setProp:

Código Delphi [-]
procedure TMyORMEngine.SetFieldValue(pName: String; pValor: Variant);
var ctx : TRttiContext;
    rt : TRttiType;
    xfield: TRttiField;
begin
 if not Assigned(Objeto) then
    exit;
 ctx := TRttiContext.Create();
 Try
   rt := ctx.GetType(Objeto.ClassType);
   xfield := rt.GetField(pName);
   if Assigned(xfield) then
      xfield.SetValue(Objeto, TValue.From(pValor));
 Finally
   ctx.Free;
 End;

end;

procedure TMyORMEngine.SetPropValue(pName: String; pValor: Variant);
var ctx : TRttiContext;
    rt : TRttiType;
    prop : TRttiProperty;
begin
 if not Assigned(Objeto) then
    exit;
 ctx := TRttiContext.Create();
 Try
   rt := ctx.GetType(Objeto.ClassType);
   prop := rt.GetProperty(pName);
   if Assigned(prop) then
      prop.SetValue(Objeto, TValue.From(pValor));
 Finally
   ctx.Free;
 End;
end;


Lo anterior es un extracto de algunas pruebas que hice pensando tal vez en lo mismo que tu quieres, pero solo a manera de ejercicio... funcionó lo que hice, pero nunca lo implementé. Como te dije lo anterior es un "a grosso modo"

Con la clase TMyORMEngine, iban muchos, muchos metodos y propiedades, como la persistencia en la DB a partir de los datos obtenidos... o la asignacion de sentencias SQL custom (insert, update, delete),

Como te digo, GetObject es solo una tarea de todo lo que deberias organizar sobre este tema.

Última edición por gatosoft fecha: 03-05-2020 a las 10:40:20. Razón: etiquetas delphi
Responder Con Cita