Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Access en red (https://www.clubdelphi.com/foros/showthread.php?t=91531)

GOG 27-02-2017 07:13:13

Access en red
 
Hola a todos, necesito una ayuda con el siguiente tema:
Tengo que adaptar un sistema desarrollado en Delphi6 con una base de datos en Access para que funcione en red. Pueden llegar a ser hasta 6 usuarios que se conecten a la vez y no más de tres que realicen el mismo proceso a la vez. Utilizo ADOConnection, ADOTable, etc... He adaptado el código iniciando transacciones al momento de grabar los datos en las tablas pero algo no está bien... lo he probado con dos usuarios, el proceso de uno lo termina bien y en el otro da un error. Agrego el código desarrollado para una tarea de ingresos de artículos a una tabla INGRESOS, actualizar una tabla STOCK y agregar los datos en otra tabla MOVIM... Desde ya, muchas gracias !!!

Código Delphi [-]
procedure TFIng.BGrabarClick(Sender: TObject);            // formuario FIng 
var largoCod: Integer;
begin
  FAguarde.Show;
  FAguarde.Refresh;
  if DM1.Movim.Active = false then                              // actualizo en 3 tablas: Movim, Ingresos y Stock de un data module DM1
     DM1.Movim.Active:= true;
  if DM1.Ingresos.Active = false then
     DM1.Ingresos.Active:= true;
  if DM1.Stock.Active = false then
     DM1.Stock.Active:= true;

  LargoCod:= Length(Table1TM1CHR1.AsString) - 9;
  Table1.First;                  // la tabla temporal donde cargo los datos del DBGrid
  if DM1.ADOConnection1.InTransaction = false then
  begin
    If MessageDlg('Confirma el Ingreso ',mtConfirmation,[mbYes,mbNo],0) = mrYes then
    begin
      try
        DM1.Ingresos.IndexFieldNames:= 'INGNRO';  // Actualizo en número del comprobante
        DM1.Ingresos.Last;
        EdNro.Text:= InttoStr(DM1.IngresosINGNRO.AsInteger + 1);
      except
      end;

      try
        DM1.ADOConnection1.BeginTrans;
        while not Table1.Eof do
        begin
          FAguarde.L1.Caption:= 'Grabando datos en Ingresos...';
          FAguarde.L1.Refresh;
          DM1.Ingresos.Append;
          DM1.IngresosINGNRO.AsInteger:=  StrToInt(EdNro.Text);
          DM1.IngresosINGNIV.AsInteger:=       Nivel;
          DM1.IngresosINGOPE.AsInteger:=       Opera;
          DM1.IngresosINGFEC.AsDateTime:=    StrToDate(EdFec.Text);
          DM1.IngresosINGHOR.AsDateTime:=   Time;
          DM1.IngresosINGLOT.AsString:=         LeftStr(Table1TM1CHR1.AsString, 5);
          DM1.IngresosINGCOD.AsString:=        MidStr(Table1TM1CHR1.AsString, 6, LargoCod);
          DM1.IngresosINGCMCT.AsString:=       Table1TM1CHR1.AsString;
          DM1.IngresosINGCOS1.AsFloat:=        Table1TM1PRE1.AsFloat;
          if Depo = 1 then
             DM1.IngresosINGCAN1.AsFloat:=   Table1TM1NUM1.AsFloat;
          if Depo = 2 then
             DM1.IngresosINGCAN2.AsFloat:=   Table1TM1NUM1.AsFloat;

          if DM1.Stock.Locate('ARTCMCT',Table1TM1CHR1.AsString,[]) = true then
          begin
             FAguarde.L1.Caption:= 'Actualizando Stock...                 ';
             FAguarde.L1.Refresh;
             DM1.Stock.Edit;
             DM1.StockARTFEC1.AsDateTime:=  StrToDate(EdFec.Text);
             if Depo = 1 then
               DM1.StockARTEX1.AsFloat:=   DM1.StockARTEX1.AsFloat + Table1TM1NUM1.AsFloat;
             if Depo = 2 then
               DM1.StockARTEX2.AsFloat:=   DM1.StockARTEX2.AsFloat + Table1TM1NUM1.AsFloat;
             DM1.StockARTFEC1.AsDateTime:=    StrToDate(EdFec.text);
          END;

          FAguarde.L1.Caption:= 'Actualizando en Movimientos...              ';
          FAguarde.L1.Refresh;
          DM1.Movim.Append;
          DM1.MovimMOVFEC.AsDateTime:=   Date;
          DM1.MovimMOVHOR.AsDateTime:=   Time();
          DM1.MovimMOVLOT.AsString:=     LeftStr(Table1TM1CHR1.AsString, 5);
          DM1.MovimMOVCOD.AsString:=     MidStr(Table1TM1CHR1.AsString, 6, LargoCod);
          DM1.MovimMOVCMCT.AsString:=    Table1TM1CHR1.AsString;
          if Depo = 1 then
            DM1.MovimMOVCAN1.AsFloat:=   Table1TM1NUM1.AsFloat;
          if Depo = 2 then
            DM1.MovimMOVCAN2.AsFloat:=   Table1TM1NUM1.AsFloat;
          DM1.MovimMOVNRO.AsInteger:=    StrToInt(EdNro.Text);
          DM1.MovimMOVTIP.AsString:=       'I';
          DM1.MovimMOVOBS.AsString:=     'Por Ingreso Nro. ' + EdNro.Text;

          Table1.Next;
        end;

        DM1.Stock.Post;
        DM1.Ingresos.Post;
        DM1.Movim.Post;
        DM1.ADOConnection1.CommitTrans;
        FAguarde.Close;
        ShowMessage('Proceso Terminado.');
        RB1.Checked:= false;
        RB2.Checked:= false;
        BCerrarClick(nil);
      except
        DM1.ADOConnection1.RollbackTrans;
        FAguarde.Close;
        ShowMessage('No se pudo completar el proceso.');
      end;
    end;            // del si confirma...
  end else begin         // del InTransaction
    ShowMessage('Archivo ocupado... Aguarde un insante e intente nuevamente.');
  end;

  try
    FAguarde.Close;
  except
  end;

end;

AgustinOrtu 27-02-2017 08:00:35

Me pierdo entre tanto código sin las etiquetas

Más allá del problema, hay varias cosas para mejorar en tú código. Hay muchas cosas que lo hacen muy difícil de leer, por ejemplo el como haces las comparaciones booleanas.

Código Delphi [-]
 If CondicionBooleana = True then

 // Sintaxis equivalente, más compacta y mucho más clara
 if CondicionBooleana then
 
 // Lo mismo sucede para comparar por falso

  if CondicionBooleana = False 
 
  if not CondicionBooleana then

Luego otro punto importantísimo es el manejo de excepciones. Veo bloques try-except en los que ocultas las excepciones; esto ocurre porque dejas el bloque except vacío. Créeme, es una muy mala idea. Podría estar ocurriendo algo malo y como lo ocultas, ni siquiera vos te puedes enterar del error. Te recomiendo que elimines estos bloques try-except en el cual no controlas las excepciones. Además como bonus le quitas nivel de sangría y complejidad al código, habiéndolo más fácil de entender

También veo que utilizas un condicional para verificar si los DataSet (tablas, query) están activos, y si no lo están, abrirlos. No es necesario preguntar. Con poner Active a True, el componente solo ya se encarga de verificarlo. También podes usar el método Open, es equivalente a poner Active a True

Todos estos consejos sean para mejorar el código, que está muy extenso y hace muchas cosas, y como ya dije, es complejo

Falta más información sobre el problema. Cuando hay un error, debes colocar en tú post en que línea (si es una excepción) y el mensaje. Si el error es de programación (datos inconsistentes, no hace lo que quiero, etc) deberías decirnos que sucede.

A simple vista pareciera que estas iterando, grabando los datos, pero el Post sobre las tablas lo haces una vez. Debes hacer una combinación de insert (o append) y post por cada registro que querés guardar. Para edit es igual

GOG 27-02-2017 08:24:37

Gracias Agustín por tu respuesta y tus observaciones, las voy a tener en cuenta !!!
El tema del error es así, cuando graba el primero (de los dos usuarios con los que probé) sale todo bien, termina el proceso y cierra el formulario como se esperaba, pero cuando graba el otro, hace el ADOConection1.CommitTrans, dice que terminó el proceso, pero luego aparece el error: "Ha intentado confirmar o deshacer una transacción sin iniciar una antes"...

GOG 27-02-2017 08:36:29

Cita:

Empezado por GOG (Mensaje 513748)
Gracias Agustín por tu respuesta y tus observaciones, las voy a tener en cuenta !!!
El tema del error es así, cuando graba el primero (de los dos usuarios con los que probé) sale todo bien, termina el proceso y cierra el formulario como se esperaba, pero cuando graba el otro, hace el ADOConection1.CommitTrans, dice que terminó el proceso, pero luego aparece el error: "Ha intentado confirmar o deshacer una transacción sin iniciar una antes"...

Agrego el código (espero que más fácil de leer...)


Código Delphi [-]
procedure TFIng.BGrabarClick(Sender: TObject);
var largoCod: Integer;
begin
  FAguarde.Show;
  FAguarde.Refresh;
  DM1.Movim.Active:= true;
  DM1.Ingresos.Active:= true;
  DM1.Stock.Active:= true;

  LargoCod:= Length(Table1TM1CHR1.AsString) - 9;
  Table1.First;
  if DM1.ADOConnection1.InTransaction = false then
  begin
    If MessageDlg('Confirma el Ingreso? ',mtConfirmation,[mbYes,mbNo],0) = mrYes then
    begin
      try
        DM1.Ingresos.IndexFieldNames:= 'INGNRO';
        DM1.Ingresos.Last;
        EdNro.Text:= InttoStr(DM1.IngresosINGNRO.AsInteger + 1);
      except
        EdNro.Text:= '1';
      end;
      try
        DM1.ADOConnection1.BeginTrans;
        while not Table1.Eof do
        begin
          FAguarde.L1.Caption:= 'Grabando datos en Ingresos...';
          FAguarde.L1.Refresh;
          DM1.Ingresos.Append;
          DM1.IngresosINGNRO.AsInteger:=  StrToInt(EdNro.Text);
          DM1.IngresosINGNIV.AsInteger:=  Nivel;
          DM1.IngresosINGOPE.AsInteger:=  Opera;
          DM1.IngresosINGFEC.AsDateTime:= StrToDate(EdFec.Text);
          DM1.IngresosINGHOR.AsDateTime:= Time;
          DM1.IngresosINGLOT.AsString:=   LeftStr(Table1TM1CHR1.AsString, 5);
          DM1.IngresosINGCOD.AsString:=   MidStr(Table1TM1CHR1.AsString, 6, LargoCod);
          DM1.IngresosINGCMCT.AsString:=  Table1TM1CHR1.AsString;
          DM1.IngresosINGCOS1.AsFloat:=   Table1TM1PRE1.AsFloat;
          if Depo = 1 then
             DM1.IngresosINGCAN1.AsFloat:= Table1TM1NUM1.AsFloat;
          if Depo = 2 then
             DM1.IngresosINGCAN2.AsFloat:= Table1TM1NUM1.AsFloat;
          DM1.Ingresos.Post;

          if DM1.Stock.Locate('ARTCMCT',Table1TM1CHR1.AsString,[]) = true then
          begin
             FAguarde.L1.Caption:= 'Actualizando Stock...                 ';
             FAguarde.L1.Refresh;
             DM1.Stock.Edit;
             DM1.StockARTFEC1.AsDateTime:=  StrToDate(EdFec.Text);
             if Depo = 1 then
               DM1.StockARTEX1.AsFloat:= DM1.StockARTEX1.AsFloat + Table1TM1NUM1.AsFloat;
             if Depo = 2 then
               DM1.StockARTEX2.AsFloat:= DM1.StockARTEX2.AsFloat + Table1TM1NUM1.AsFloat;
             DM1.StockARTFEC1.AsDateTime:=  StrToDate(EdFec.text);
             DM1.Stock.Post;
          END;


          FAguarde.L1.Caption:= 'Actualizando en Movimientos...              ';
          FAguarde.L1.Refresh;
          DM1.Movim.Append;
          DM1.MovimMOVFEC.AsDateTime:= Date;
          DM1.MovimMOVHOR.AsDateTime:= Time();
          DM1.MovimMOVLOT.AsString:=   LeftStr(Table1TM1CHR1.AsString, 5);
          DM1.MovimMOVCOD.AsString:=   MidStr(Table1TM1CHR1.AsString, 6, LargoCod);
          DM1.MovimMOVCMCT.AsString:=  Table1TM1CHR1.AsString;
          if Depo = 1 then
            DM1.MovimMOVCAN1.AsFloat:= Table1TM1NUM1.AsFloat;
          if Depo = 2 then
            DM1.MovimMOVCAN2.AsFloat:= Table1TM1NUM1.AsFloat;
          DM1.MovimMOVNRO.AsInteger:=  StrToInt(EdNro.Text);
          DM1.MovimMOVTIP.AsString:=   'I';
          DM1.MovimMOVOBS.AsString:=   'Por Ingreso Nro. ' + EdNro.Text;
          DM1.Movim.Post;

          Table1.Next;
        end;

        DM1.ADOConnection1.CommitTrans;
        FAguarde.Close;
        ShowMessage('Proceso Terminado.');
        RB1.Checked:= false;
        RB2.Checked:= false;
        BCerrarClick(nil);
      except
        DM1.ADOConnection1.RollbackTrans;
        FAguarde.Close;
        ShowMessage('No se pudo completar el proceso.');
      end;
    end;  // del si confirma...
  end else begin //del InTransaction
    ShowMessage('Archivo ocupado... Aguarde un insante e intente nuevamente.');
  end;

  FAguarde.Close;

end;

Neftali [Germán.Estévez] 27-02-2017 08:48:28

Cita:

Empezado por GOG (Mensaje 513749)
Agrego el código (espero que más fácil de leer...)

Para colocar TAG's al código revisa este enlace.
Te recomiendo también que revises la Guía de estilo de los foros, ya que eres nuevo por aquí.

Un saludo.

GOG 27-02-2017 09:21:13

Cita:

Empezado por Neftali (Mensaje 513751)
Para colocar TAG's al código revisa este enlace.
Te recomiendo también que revises la Guía de estilo de los foros, ya que eres nuevo por aquí.

Un saludo.

Gracias Neftali, desde ya pido disculpas... Espero que me puedan guiar en lo que me estoy equivocando y cual es el error en mi código. Desde ya se los agradezco.

Neftali [Germán.Estévez] 27-02-2017 10:11:51

Cita:

Empezado por GOG (Mensaje 513749)
El tema del error es así, cuando graba el primero (de los dos usuarios con los que probé) sale todo bien, termina el proceso y cierra el formulario como se esperaba, pero cuando graba el otro, hace el ADOConection1.CommitTrans, dice que terminó el proceso, pero luego aparece el error: "Ha intentado confirmar o deshacer una transacción sin iniciar una antes"...

Siempre puedes preguntar utilizando el Intransaction, antes de realizar el Commit. De todas formas, en ese caso, habría que saber es qué momento se está cerrando la transacción.
Para eso deberías "debuggar" el código e ir ejecutándolo línea a línea. Tal vez en algún otro evento que se lanza desde aquí, o en algún otro sitio se esté cerrando esa transacción.

AgustinOrtu 27-02-2017 15:01:27

No estoy de acuerdo con lo de preguntar por la transacción. De hacerlo habría que hacerlo antes porque ya modifico los datos de la BD fuera de una transacción y perdes la seguridad que te garantiza y la posibilidad de hacer rollback. Más bien lo justo sería abortar todo si falla el BeginTransaction

Neftali [Germán.Estévez] 27-02-2017 15:14:15

Cita:

Empezado por AgustinOrtu (Mensaje 513762)
...Más bien lo justo sería abortar todo si falla el BeginTransaction

Correcto, pero yo estaba pensando en el caso de que le funciona el Begin, pero le falle en Rollback o el Commit. Eso hay que controlarlo, sea con una excepción o preguntarlo antes.

AgustinOrtu 27-02-2017 16:55:03

Cita:

Empezado por Neftali (Mensaje 513765)
Correcto, pero yo estaba pensando en el caso de que le funciona el Begin, pero le falle en Rollback o el Commit. Eso hay que controlarlo, sea con una excepción o preguntarlo antes.

^\||/^\||/

La forma correcta de hacerlo es esta, y es universal para cualquier componente de acceso a datos (puede haber errores de sintaxis porque estoy escribiendo de memoria)

Código Delphi [-]
  if not ADOConnection.BeginTransaction then
    // politica ante errores: reintentar, lanzar excepcion, mandar a una cola y procesar mas tarde

  try
    // editar o insertar campos
    ADOConnection.CommitTransaction;
  except
    ADOConnection.RollbackTransaction;
    raise;
  end;


Es importantisimo que la sentencia que inicia la transaccion este fuera del bloque try, ya que si se pone dentro del try-except y la transaccion no puede iniciar, y se produce una excepcion por cualquier motivo, el flujo pasa al bloque except y ejecutariamos un Rollback cuando no hay ninguna transaccion

Tambien el Commit suele ser la ultima sentencia del bloque try, aunque puede haber cosas despues de esto (como escribir en un log). Obviamente tiene que estar despues de realizar todas las inserciones, modificaciones, borrados, o ejecucion de cualquier SQL

Y por ultimo el bloque except captura todas las excepciones, realiza el rollback correspondiente (ya que si entramos dentro del try se asegura que estamos en una transaccion) y luego vuelve a elevar la excepcion (no la oculta), para que se propague

GOG 27-02-2017 23:20:12

Muchas Gracias por su tiempo y ayuda!!!
Otra pregunta (si suena a abuso avisen por favor...) si debiera mandarlo a una cola, o volver a intentarlo por que no se haya podido completar el proceso, ¿cómo sería la instrucción o el código?

bitbow 01-03-2017 02:09:15

Si requieres trabajar fuera de linea usa el componente ClientDataset (o realizar cambios y hasta que termines de hacer todos los cambios enviar a la base de datos).

Saludos.

GOG 01-03-2017 23:31:49

Cita:

Empezado por AgustinOrtu (Mensaje 513768)
^\||/^\||/

La forma correcta de hacerlo es esta, y es universal para cualquier componente de acceso a datos (puede haber errores de sintaxis porque estoy escribiendo de memoria)

Código Delphi [-]
  if not ADOConnection.BeginTransaction then
    // politica ante errores: reintentar, lanzar excepcion, mandar a una cola y procesar mas tarde

  try
    // editar o insertar campos
    ADOConnection.CommitTransaction;
  except
    ADOConnection.RollbackTransaction;
    raise;
  end;


Es importantisimo que la sentencia que inicia la transaccion este fuera del bloque try, ya que si se pone dentro del try-except y la transaccion no puede iniciar, y se produce una excepcion por cualquier motivo, el flujo pasa al bloque except y ejecutariamos un Rollback cuando no hay ninguna transaccion

Tambien el Commit suele ser la ultima sentencia del bloque try, aunque puede haber cosas despues de esto (como escribir en un log). Obviamente tiene que estar despues de realizar todas las inserciones, modificaciones, borrados, o ejecucion de cualquier SQL

Y por ultimo el bloque except captura todas las excepciones, realiza el rollback correspondiente (ya que si entramos dentro del try se asegura que estamos en una transaccion) y luego vuelve a elevar la excepcion (no la oculta), para que se propague


Gracias Bitbow, creo que con esto que decía Agustín funciona bien... Saludos


La franja horaria es GMT +2. Ahora son las 02:48:11.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi