En un traspaso de datos desde DBF a SQL Server, quizás nos veamos en la necesidad
de llamar a un procedimiento almacenado, donde necesitamos enviar parámetros y
además necesitamos obtener datos que se modifican dentro del procedimiento almacenado.
Para ello, me he creado un par de clases, partiendo de las de Jose Luis Capel,
que nos permitirá llamar a un procedimiento almacenado, pasando valores y
obteniendo valores.
Por ejemplo algo como esto en el procedimiento almacenado ;
- Code: Select all Expand view
ALTER PROCEDURE [dbo].[SP_CREAR_EJEMPO]
@pCODIGO VARCHAR(3),
@pPRECIO INT,
@pDESCRIPCION VARCHAR(40),
@pID INT output
El ejemplo para llamar a un procedimiento es el siguiente;
Creamos el procedimiento almacenado llamado SP_CREAR_EJEMPO a traves de la clase TAdoStoreProc.
Nota: oConnection es la clase TAdConnect de Jose Luis Capel.
o
- Code: Select all Expand view
Cmd := TAdoStoreProc():New( "SP_CREAR_EJEMPO", oConnection )
oAdoCmd := oCmd:GetADOCommand() // Obtemos el objeto ADO Command.
A partir de este momento, solamente vamos a alimentar a nuestras variables
del procedimiento almacenado;
o
- Code: Select all Expand view
ADOCmd:Parameters:Item('@CODIGO'):Value := '001'
oADOCmd:Parameters:Item('@pPRECIO'):Value := 100.56
oADOCmd:Parameters:Item('@pDESCRIPCION'):Value := 'Producto varios'
oAdoCmd:Execute()
Ahora imaginemos que en el procedimiento Almacenado tenemos una variable
llamada por ejemlo @pID, que no es más que un Identity del último INSERT, que
nos servirá para hacer SELECT hacia otra tablas relacionadas, que nos a creado
el SP( Store Procedure ), por poner un ejemplo;
n
- Code: Select all Expand view
ID := oADOCmd:Parameters:Item('@pID'):Value // Obtenemos el ID insertado
cSql := [ SELECT * FROM TABLA WHERE ID_MASTER = ] + str( nID )
Este es un simple ejemplo de lo que podría suceder.*( Basado en hechos reales )
Bueno, ahora pondré el código fuente
- Code: Select all Expand view
#define adCmdStoredProc 4
---------------------------------------------------------------------
//---------------------------------------------------------------------
CLASS tAdoCommand
DATA oCmd //Objeto ADO Command
DATA oParameters
DATA oError
METHOD New( oConnection ) CONSTRUCTOR
METHOD Type( nType ) INLINE ::oCmd:CommandType := nType
METHOD Text( cText ) INLINE ::oCmd:CommandText := cText
METHOD GetADOCommand() INLINE ::oCmd
END CLASS
METHOD New( oConnection ) CLASS tAdoCommand
LOCAL oHErr
::oError := TAdoError():New()
TRY
::oCmd := CreateObject("ADODB.Command")
CATCH oHErr
::oError:Connection( oConnection )
Throw( oHErr )
END
::oCmd:ActiveConnection := oConnection:oConnection
RETURN Self
//---------------------------------------------------------------------
CLASS TAdoStoreProc FROM TAdoCommand
METHOD New( cText, oConnection ) CONSTRUCTOR
METHOD Execute()
END CLASS
METHOD New( cText, oConnection ) CLASS tAdoStoreProc
Local oHErr
try
Super:New( oConnection )
::Type( adCmdStoredProc )
::Text( cText )
::oCmd:Parameters:Refresh()
catch oHErr
::oError:Connection( oConnection )
Throw( oHErr )
end
RETURN Self
METHOD Execute( ) CLASS tAdoStoreProc
LOCAL oError, cError, oErr
LOCAL lResult
lResult := .T.
TRY
::oCmd:Execute()
CATCH oErr
::oError:Connection( ::oConnection )
lResult := .F.
Throw( oErr )
END
RETURN lResult
//---------------------------------------------------------------------
CLASS TAdoError
DATA aErrors
DATA oErrorDB
METHOD New()
METHOD Clean() INLINE ::aErrors := {}
METHOD Connection( oConnection )
METHOD ShowErrors()
END CLASS
METHOD New( lCreateError ) CLASS TAdoError
::aErrors := {}
/* TODO: Pendiente de I+D
if lCreateError
::oErrorDB := CreateObject( "ADODB.Error")
endif
*/
RETURN Self
METHOD Connection( oConnection ) CLASS TAdoError
Local oError, cError := ""
::Clean()
For Each oError In oConnection:Errors
cError := "Error #" + cValtoChar( oError:Number ) + CRLF +;
" " + oError:Description + CRLF +;
" (Source: " + oError:Source + ")" + CRLF +;
" (SQL State: " + oError:SQLState + ")" + CRLF +;
" (NativeError: " + cValToChar( oError:NativeError ) + ")"
AADD( ::aErrors, cError )
Next
RETURN NIL
METHOD ShowErrors( ) CLASS TAdoError
Local cError := ""
For Each cError In ::aErrors
MsgStop( cError )
next
RETURN NIL
Bueno, yo controlo los 'posibles' errores desde el nivel más alto, con try /catch.
Las clases si os fijais hacen Throw( oErr ), por si fuera un error de Harbour, despues discrimino si el error es de ADO o de Harbour, no vaya a ser que sume un numerico + "ALAAA" y le estemos echando la culpa a ADO
cuando realmente no tiene la culpa.
Por ejemplo;
- Code: Select all Expand view
try
oConnection:BeginTrans()
Cmd := TAdoStoreProc():New( "SP_CREAR_EJEMPO", oConnection )
oAdoCmd := oCmd:GetADOCommand() // Obtemos el objeto ADO Command.
oADOCmd:Parameters:Item('@pHASTA'):Value := DTOC( oDbMPrecios:Hasta )
oCmd:Execute()
catch oError
oConnection:RollBackTrans()
? "ERROR. Se ha realizado ROLLBACK." + CRLF )
lResult := .F.
if !ShowError( oError, oConnection )
? "Falla el procedimiento / Sentencia SQL"
endif
end
#define NTRIM(n) ( LTrim( Str( n ) ) )
Function ShowError( oError, oConnection )
Local cErrorLog := "", n, aStack := {}
Local lError := .F.
if Empty( oConnection:oError:aErrors ) // No son errores de ADO
lError := .T.
cErrorLog += "Operation: "+ oError:Operation + CRLF
cErrorLog += "Error description: "+ oError:Description + CRLF
if ValType( oError:Args ) == "A"
cErrorLog += " Args:" + CRLF
for n := 1 to Len( oError:Args )
cErrorLog += " [" + Str( n, 4 ) + "] = " + ValType( oError:Args[ n ] ) + ;
" " + cValToChar( oError:Args[ n ] ) + CRLF
next
endif
cErrorLog += CRLF + "Stack Calls" + CRLF
n := 1
while ( n < 5 ) // Las ultimas 5
if ! Empty(ProcName( n ) )
AAdd( aStack, " Called from: " + ProcFile( n ) + " => " + Trim( ProcName( n ) ) + ;
"(" + NTRIM( ProcLine( n ) ) + ")" )
cErrorLog += ATail( aStack ) + CRLF
endif
n++
end
MsgStop( cErrorLog , "Error Harbour" )
endif
oConnection:oError:ShowErrors() // Enseña algo, si tienes que enseñar
return lError
También existe otra forma, CreateParameter(), pero no he profundizado en ello, os lo dejo como ejercicio.
Bueno, dar las gracias a J.Capel por compartir su clases de ADO, esto es un complemento a ese trabajo.
Saludos
Rafa Carmona