none
Tomar la propiedad cmd.commandtext desde fuera de la clase RRS feed

  • Pregunta

  • Hola a todos:

    Me explico mejor, tengo varias instrucciones que pueden funcionar por ejemplo con una select o con un StoredProcedure dependiendo del tipo de base de datos que sea. Para ello he creado una función que me localice el nombre del procedimiento almacenado (puede llevar StoredProcedure o simplemente en este caso una select), y esa función la llamo desde el método que creo la select con lo que me ahorro repetir infinitas veces el mismo código. Paso a explicarlo con el código que utilizo y como lo aplico.

    Función que comprueba y llama al procedimiento almacenado (Puede llevar dentro un storeproc. o bien una select por ejemplo según el tipo de base de datos que sea:

     Public Shared Function NombreProcFormulas(Nombre As String) As String
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
            ' Declaramos una variable Connection
    
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Creamos el Commando
                Dim cmd As DbCommand = cnn.CreateCommand
    
                Select Case Nombre
                     Case "CreacionprocFormulasBalance_5_ClaveMP"
                        If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then
                            cmd.CommandType = CommandType.Text
                            MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP()
                            cmd.CommandText = strprocFormulasBalance5digMP
    
                        Else
                            cmd.CommandType = CommandType.StoredProcedure
                            cmd.CommandText = "procFormulasBalance5digMP"
                        End If
    .../...
    
       End Select
            End Using
    
            Return Nombre
        End Function
    

    Bueno, entonces desde la select "intento" llamarla así:

    Public Shared Sub Formula_ActivoTotal()
            ' Creamos el acceso a datos mediante el nombre de la cadena de conexión existente en el archivo de configuración de la aplicación.
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
            Dim valor As Integer = AccesoLogica.ObtenerPeriodos()
    
            ' Declaramos una variable Connection
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Creamos el Commando
                Dim cmd As DbCommand = cnn.CreateCommand()
    
                cnn.Open()
    
                ReDim ActivoTotal(ActivoTotal.Length - 1)
    
                If VarGlobal.StrCodPais = "34" Then
                    Select Case VarGlobal.StrPlanConta
                        Case "PLAN 2007"
                           'Accedemos al stored procedure o bien a la fórmula del mismo según versión Base de datos.
                            ' MetodosCreacion.NombreProcFormulas("CreacionprocFormulasBalance_5_ClaveMP")
    
                            If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then
                                cmd.CommandType = CommandType.Text
                                MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP()
                                cmd.CommandText = MetodosCreacion.strprocFormulasBalance5digMP
                            Else
                                cmd.CommandType = CommandType.StoredProcedure
                                cmd.CommandText = "procFormulasBalance5digMP"
                            End If
    
                            With cmd.Parameters
                                .Clear()
                                .Add(Configuracion.CreateParameter(cmd, "@empresa", VarGlobal.StrCodEmpresa))
                                .Add(Configuracion.CreateParameter(cmd, "@planconta", "PLAN 2007"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo1", "A2"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo2", "A3"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo3", "A4"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo4", "A4I"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo5", "A5"))
                            End With
    
    ..../....
    
       End Select
                End If
    
                Using dr As DbDataReader = cmd.ExecuteReader()
                    While dr.Read()
                        For i = 0 To valor
                            ActivoTotal(i) += dr.GetDouble(i)
                        Next
                    End While
                End Using
    .../...
    end sub

    El problema es que siempre me salta el error;

     
    Excepción no controlada del tipo 'System.InvalidOperationException' en System.Data.SqlServerCe.dll
    
    Información adicional: Prepare: no se ha inicializado la propiedad CommandText

    Ese error no salta evidentemente cuando cuando el trozo de código:

    If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then
                            cmd.CommandType = CommandType.Text
                            MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP()
                            cmd.CommandText = strprocFormulasBalance5digMP
    
                        Else
                            cmd.CommandType = CommandType.StoredProcedure
                            cmd.CommandText = "procFormulasBalance5digMP"
                        End If
    

    lo pongo directamente en la select. Bueno el problema está en como lo puedo hacer para que me coja el valor del CommandText que tengo en dicha sentencia pero desde el procedimiento en la que lo llamo que es:

     'Accedemos al stored procedure o bien a la fórmula del mismo según versión Base de datos.
                            MetodosCreacion.NombreProcFormulas("CreacionprocFormulasBalance_5_ClaveMP")

    Todo ello es que con una sola línea tendría que llamar a la función y que no me diera error en la propiedad cmd.CommandText que la inicio en el mismo.

    Un cordial saludo a todos y muchas gracias.

    Gemma


    sábado, 13 de febrero de 2016 11:11

Respuestas

  • Hola, Gemma:

    No sé las veces que he leído tu mensaje y todavía no me he enterado lo que deseas hacer. :-(

    > Tomar la propiedad cmd.commandtext desde fuera de la clase

    Para eso, tu clase tendría que implementar alguna propiedad o método que devolviera el valor de la propiedad CommandText que vas a utilizar en un objeto genérico DbCommand, o devolver el propio objeto DbCommand debidamente configurado.

    > Función que comprueba y llama al procedimiento almacenado (Puede llevar
    > dentro un storeproc. o bien una select por ejemplo según el tipo de base
    > de datos que sea:
    >
    > Public Shared Function NombreProcFormulas(Nombre As String) As String
    >
    >    ' Código fuente de la función
    >
    >     Return Nombre
    >
    > End Function

    ¿Esa función tiene algún sentido? Si a la función le pasas un Nombre y devuelve el mismo valor del parámetro Nombre, me vas a disculpar pero para ese "viaje" no hace falta implementar función alguna.

    Parto de que no tengo ni la menor idea de lo que quieres hacer, pero ¿no sería mejor que esa función devolviera un objeto DbCommand debidamente configurado en lugar de devolver el mismo valor que le has pasado?

    > Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)

    Suponiendo que la clase DataAccessInvariant es aquella que te indiqué en su día, y que ésta continúa teniendo el siguiente método:

        ''' <summary>
        ''' Devuelve un objeto Command apropiado al tipo de factoría creada.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateCommand() As DbCommand
    
            ' Crear y devolver un objeto Command.
            '
            Return m_factory.CreateCommand()
    
        End Function

    Tu función NombreProcFormulas bien podría configurar un objeto DbCommand de acuerdo al tipo de factoría con el que estés trabajando, y devolverlo:

    Public Shared Function NombreProcFormulas(nombre As String) As DbCommand Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion) Dim cmd As DbCommand = Nothing If (nombre = "CreacionprocFormulasBalance_5_ClaveMP") Then

    ' Creamos el Commando
                cmd = da.CreateCommand()

    If ((Configuracion.strNombreBaseDeDatos = "PerseoSqlCE") OrElse (Configuracion.strNombreBaseDeDatos = "PerseoSQLite")) Then MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP() cmd.CommandText = strprocFormulasBalance5digMP Else cmd.CommandType = CommandType.StoredProcedure cmd.CommandText = "procFormulasBalance5digMP" End If End If Return cmd End Function


    Fíjate que si el valor pasado a la función es igual a "CreacionprocFormulasBalance_5_ClaveMP", se creará el comando y se asignará el valor de sus propiedad CommandText y CommandType dependiendo del valor que tenga la propiedad o método compartido Configuracion.strNombreBaseDeDatos, devolviéndose el propio objeto DbCommand.

    Pero si el valor pasado a la función no es igual a "CreacionprocFormulasBalance_5_ClaveMP", entonces la función devolverá el valor Nothing, valor éste que tendrás que verificar en el procedimiento que llama a la función "CreacionprocFormulasBalance_5_ClaveMP".

    Por último te comento que observes bien que el objeto DbCommand devuelto por la función NombreProcFormulas, NO tiene asignado una conexión válida, es decir, el valor de su propiedad Connection es Nothing. Te quiero decir con esto que, ANTES de ejecutar el comando propiamente dicho, tendrás que asignarle el objeto DbConnection adecuado al tipo de base de datos que estés utilizando. ¿Me explico? ;-)

    Podrías sobrecargar la función ExecuteAction para que acepte un objeto DbCommand (el comando invariable que deseas ejecutar) y donde le asignarías el objeto Connection de acuerdo al tipo de conexión invariable que en ese momento estés utilizando:

        ''' <summary>
        ''' Ejecuta la consulta SQL de acción existente en el objeto Command
        ''' devolviendo el número de registros afectados.
        ''' </summary>
        ''' <param name="cmd">objeto Command debidamente configurado.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend Shared Function ExecuteAction(cmd As DbCommand) As Integer
    
            Dim result As Integer
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Asignamos la conexión al comando.
                cmd.Connection = cnn
    
                cnn.Open()
    
                result = cmd.ExecuteNonQuery()
    
            End Using
    
            Return result
    
        End Function

    Pero la función ExecuteAction solamente te serviría para ejecutar comandos de acción. Si necesitas ejecutar una consulta SELECT para rellenar un objeto DataTable, también podrías sobrecargar el método GetData:

        ''' <summary>
        ''' Ejecuta la consulta SQL de selección existente en el objeto Command
        ''' devolviendo un objeto DataTable.
        ''' </summary>
        ''' <param name="cmd">objeto Command debidamente configurado.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend Shared Function GetData(cmd As DbCommand) As DataTable
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            Dim dt As DataTable = Nothing
    
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Asignamos la conexión al comando.
                cmd.Connection = cnn
    
                ' Creamos el adaptador de datos invariable.
                Dim adapter As DbDataAdapter = da.CreateDataAdapter()
                adapter.SelectCommand = cmd
    
                dt = New DataTable()
                adapter.Fill(dt)
    
            End Using
    
            Return dt
    
        End Function

    Doy por hecho que conoces de sobra los métodos ExecuteAction y GetData.

    ¡Lo dicho! Que ignoro si me respuesta tiene algo que ver con lo que detallas tanto en el asunto de tu pregunta como en su contenido. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.





    • Marcado como respuesta gemma_campillo sábado, 13 de febrero de 2016 16:25
    • Editado Enrique M. MontejoModerator sábado, 13 de febrero de 2016 17:04 Colocar la creación del comando en la posición correcta.
    sábado, 13 de febrero de 2016 15:01
    Moderador
  • "gemma_campillo" escribió:

    > Bueno según tu estás "ignorante", vale si tu lo dices.....

    Me refería que, conociéndote, ya me parecía a mí extraño que solo hubiera un solo Case. ;-)

    > Maestro, me continúa fallando y creo que el error puede estar en el procedimiento
    > almacenado, ya que ahí crea un cmd.CommandText y no se si después cuando vuelve
    > a la llamada de la función pasa alguna cosa porque el error salta en el mismo sitio.

    ¡Madre mía! ¿De dónde has sacado el procedimiento CreacionprocFormulasBalance_5_ClaveMP? Y, conociéndote, digo yo que como ese habrá otros "miles" de ellos.

    ¿Continuas obteniendo una NullReferenceException? Si no es así, ¿qué error obtienes ahora?

    Si en el nuevo procedimiento CreacionprocFormulasBalance_5_ClaveMP estás ejecutando una consulta SQL, ¿para qué quieres entonces el procedimiento NombreProcFormulas? Gemma, me vas a perdonar, pero ¡me pierdo! :-(

    Me parece a mí que tu solita te estás complicando la vida queriendo hacer compatible tu aplicación con cualquier tipo de base de datos.

    Mientras me respondes, te dejo lo que tenía escrito sobre la función NombreProcFormulas, aunque viendo el nuevo procedimiento CreacionprocFormulasBalance_5_ClaveMP, ya no sé si servirá de algo:

    Observando el código fuente de la imagen que has publicado, me parece a mí que estás REPITIENDO prácticamente el mismo código en cada Case, por lo que habrá que ver la "longitud" final de la función NombreProcFormulas. ;-)

    >  If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse
    >      Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then

    Por la instrucción If, deduzco que deseas conocer si estás trabajando con el proveedor de SQL Server Compact o de SQLite, y me imagino que será para saber si el proveedor acepta o no procedimientos o consultas almacenadas, en el caso de Access.

    Podrías abreviar el código utilizando la siguiente variable Boolean:

        ' Si el nombre de la base de datos es diferente a PerseoSqlCE y a PerseoSQLite, se comprende que
        ' estás usando un proveedor de datos .net que admite procedimientos o consultas almacenadas.
        '
        Dim aceptaProcedimientos As Boolean = ((Configuracion.strNombreBaseDeDatos <> "PerseoSqlCE") AndAlso
            (Configuracion.strNombreBaseDeDatos <> "PerseoSQLite"))

    Si el valor de la variable 'aceptaProcedimientos' es True, será porque el nombre de la base de datos es diferente a PerseoSqlCE y a PerseoSQLite. Con este cambio, ya te ahorras tener que repetir

      If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse 
          Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then

    Si deseas probarlo, mira a ver cómo se quedaría la función implementándola como te indico a continuación:

        Public Shared Function NombreProcFormulas(nombre As String) As DbCommand
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            ' Si el nombre de la base de datos es diferente a PerseoSqlCE y a PerseoSQLite, se comprende que
             ' estás usando un proveedor de datos .net que admite procedimientos o consultas almacenadas.
             '
             Dim aceptaProcedimientos As Boolean = ((Configuracion.strNombreBaseDeDatos <> "PerseoSqlCE") AndAlso
                 (Configuracion.strNombreBaseDeDatos <> "PerseoSQLite"))
    
            Dim commandText As String = String.Empty
    
            Select Case nombre
                 Case "CreacionprocFormulasBalance_5_ClaveMP"
                     MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP()
    
                    ' Si acepta procedimientos, le asignamos el nombre del procedimiento;
                     ' en caso contrario, el valor de la variable correspondiente.
                     '
                     commandText = If(aceptaProcedimientos, "procFormulasBalance5digMP", strprocFormulasBalance5digMP)
    
                Case "CreacionprocFormulasBalance_4_ClaveMP"
                     MetodosCreacion.CreacionprocFormulasBalance_4_ClaveMP()
    
                    ' Si acepta procedimientos, le asignamos el nombre del procedimiento;
                     ' en caso contrario, el valor de la variable correspondiente.
                     '
                     commandText = If(aceptaProcedimientos, "procFormulasBalance4digMP", strprocFormulasBalance4digMP)
    
                ' Resto de apartados Case
    
            End Select
    
            ' Creamos el Commando
             '
             Dim cmd As DbCommand = da.CreateCommand()
             cmd.CommandText = commandText
    
            If (aceptaProcedimientos) Then
                 cmd.CommandType = CommandType.StoredProcedure
             End If
    
            Return cmd
    
        End Function

    Fíjate que solo asignamos el valor CommandType.StoredProcedure si la variable 'aceptaProcedimientos' es True (el nombre de la base de datos es diferente a a PerseoSqlCE y a PerseoSQLite).

    Creo que de ésta manera, aparte de reducir el código lo haces un poco más comprensible. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.



    sábado, 13 de febrero de 2016 17:50
    Moderador

Todas las respuestas

  • Hola, Gemma:

    No sé las veces que he leído tu mensaje y todavía no me he enterado lo que deseas hacer. :-(

    > Tomar la propiedad cmd.commandtext desde fuera de la clase

    Para eso, tu clase tendría que implementar alguna propiedad o método que devolviera el valor de la propiedad CommandText que vas a utilizar en un objeto genérico DbCommand, o devolver el propio objeto DbCommand debidamente configurado.

    > Función que comprueba y llama al procedimiento almacenado (Puede llevar
    > dentro un storeproc. o bien una select por ejemplo según el tipo de base
    > de datos que sea:
    >
    > Public Shared Function NombreProcFormulas(Nombre As String) As String
    >
    >    ' Código fuente de la función
    >
    >     Return Nombre
    >
    > End Function

    ¿Esa función tiene algún sentido? Si a la función le pasas un Nombre y devuelve el mismo valor del parámetro Nombre, me vas a disculpar pero para ese "viaje" no hace falta implementar función alguna.

    Parto de que no tengo ni la menor idea de lo que quieres hacer, pero ¿no sería mejor que esa función devolviera un objeto DbCommand debidamente configurado en lugar de devolver el mismo valor que le has pasado?

    > Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)

    Suponiendo que la clase DataAccessInvariant es aquella que te indiqué en su día, y que ésta continúa teniendo el siguiente método:

        ''' <summary>
        ''' Devuelve un objeto Command apropiado al tipo de factoría creada.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateCommand() As DbCommand
    
            ' Crear y devolver un objeto Command.
            '
            Return m_factory.CreateCommand()
    
        End Function

    Tu función NombreProcFormulas bien podría configurar un objeto DbCommand de acuerdo al tipo de factoría con el que estés trabajando, y devolverlo:

    Public Shared Function NombreProcFormulas(nombre As String) As DbCommand Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion) Dim cmd As DbCommand = Nothing If (nombre = "CreacionprocFormulasBalance_5_ClaveMP") Then

    ' Creamos el Commando
                cmd = da.CreateCommand()

    If ((Configuracion.strNombreBaseDeDatos = "PerseoSqlCE") OrElse (Configuracion.strNombreBaseDeDatos = "PerseoSQLite")) Then MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP() cmd.CommandText = strprocFormulasBalance5digMP Else cmd.CommandType = CommandType.StoredProcedure cmd.CommandText = "procFormulasBalance5digMP" End If End If Return cmd End Function


    Fíjate que si el valor pasado a la función es igual a "CreacionprocFormulasBalance_5_ClaveMP", se creará el comando y se asignará el valor de sus propiedad CommandText y CommandType dependiendo del valor que tenga la propiedad o método compartido Configuracion.strNombreBaseDeDatos, devolviéndose el propio objeto DbCommand.

    Pero si el valor pasado a la función no es igual a "CreacionprocFormulasBalance_5_ClaveMP", entonces la función devolverá el valor Nothing, valor éste que tendrás que verificar en el procedimiento que llama a la función "CreacionprocFormulasBalance_5_ClaveMP".

    Por último te comento que observes bien que el objeto DbCommand devuelto por la función NombreProcFormulas, NO tiene asignado una conexión válida, es decir, el valor de su propiedad Connection es Nothing. Te quiero decir con esto que, ANTES de ejecutar el comando propiamente dicho, tendrás que asignarle el objeto DbConnection adecuado al tipo de base de datos que estés utilizando. ¿Me explico? ;-)

    Podrías sobrecargar la función ExecuteAction para que acepte un objeto DbCommand (el comando invariable que deseas ejecutar) y donde le asignarías el objeto Connection de acuerdo al tipo de conexión invariable que en ese momento estés utilizando:

        ''' <summary>
        ''' Ejecuta la consulta SQL de acción existente en el objeto Command
        ''' devolviendo el número de registros afectados.
        ''' </summary>
        ''' <param name="cmd">objeto Command debidamente configurado.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend Shared Function ExecuteAction(cmd As DbCommand) As Integer
    
            Dim result As Integer
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Asignamos la conexión al comando.
                cmd.Connection = cnn
    
                cnn.Open()
    
                result = cmd.ExecuteNonQuery()
    
            End Using
    
            Return result
    
        End Function

    Pero la función ExecuteAction solamente te serviría para ejecutar comandos de acción. Si necesitas ejecutar una consulta SELECT para rellenar un objeto DataTable, también podrías sobrecargar el método GetData:

        ''' <summary>
        ''' Ejecuta la consulta SQL de selección existente en el objeto Command
        ''' devolviendo un objeto DataTable.
        ''' </summary>
        ''' <param name="cmd">objeto Command debidamente configurado.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend Shared Function GetData(cmd As DbCommand) As DataTable
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            Dim dt As DataTable = Nothing
    
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Asignamos la conexión al comando.
                cmd.Connection = cnn
    
                ' Creamos el adaptador de datos invariable.
                Dim adapter As DbDataAdapter = da.CreateDataAdapter()
                adapter.SelectCommand = cmd
    
                dt = New DataTable()
                adapter.Fill(dt)
    
            End Using
    
            Return dt
    
        End Function

    Doy por hecho que conoces de sobra los métodos ExecuteAction y GetData.

    ¡Lo dicho! Que ignoro si me respuesta tiene algo que ver con lo que detallas tanto en el asunto de tu pregunta como en su contenido. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.





    • Marcado como respuesta gemma_campillo sábado, 13 de febrero de 2016 16:25
    • Editado Enrique M. MontejoModerator sábado, 13 de febrero de 2016 17:04 Colocar la creación del comando en la posición correcta.
    sábado, 13 de febrero de 2016 15:01
    Moderador
  • Hola maestro:

    Gracias como siempre por tu ayuda.

    Entiendo el tema de la función "NombreProcesoFormulas" que devuelva el valor del cmd. Lo que pasa es que cuando llega, a la instrucción (imagen):

    Y la función acaba con

    End Select

    'Creamos el Comando

    cmd = da.CreateCommand()

    Return cmd

    End Function

           

    Entiendo que no le estoy dando valor al objeto cmd ya que es Nothing, pero no se por qué es "Nothing".

    La llamada a la función no la he cambiado desde el método Pubolic Shared SUb Formula_ActivoTotal() por lo que comtinúo haciendo la llamada a la clade "MetodosCreacion" de la siguiente manera:

     Public Shared Sub Formula_ActivoTotal()
            ' Creamos el acceso a datos mediante el nombre de la cadena de conexión existente en el archivo de configuración de la aplicación.
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            Dim valor As Integer = AccesoLogica.ObtenerPeriodos()
    
            ' Declaramos una variable Connection
            Using cnn As DbConnection = da.CreateConnection()
    
                ' Creamos el Commando
                Dim cmd As DbCommand = cnn.CreateCommand()
    
                cnn.Open()
    
                ReDim ActivoTotal(ActivoTotal.Length - 1)
    
                If VarGlobal.StrCodPais = "34" Then
                    Select Case VarGlobal.StrPlanConta
                        Case "PLAN 2007"
                            'Accedemos al stored procedure o bien a la fórmula del mismo según versión Base de datos.
                            cmd = da.CreateCommand()
                            MetodosCreacion.NombreProcFormulas("CreacionprocFormulasBalance_5_ClaveMP")
    
    
                            With cmd.Parameters
                                .Clear()
                                .Add(Configuracion.CreateParameter(cmd, "@empresa", VarGlobal.StrCodEmpresa))
                                .Add(Configuracion.CreateParameter(cmd, "@planconta", "PLAN 2007"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo1", "A2"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo2", "A3"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo3", "A4"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo4", "A4I"))
                                .Add(Configuracion.CreateParameter(cmd, "@codigo5", "A5"))
                            End With
    
    .../....
    No me acabo de aclarar con lo del cmd Enrique, algo no hago bien y no lo encuentro.

    Bueno maestro, la pesada de tu alumna va a seguir investigando que pasa con el cmd.

    Recibe un fuerte abrazo como siempre.

    Gemma

       

    EndFunction

    sábado, 13 de febrero de 2016 16:41
  • ¡Ignorante de mí! :-(

    Creía que tu procedimiento NombreProcFormulas solamente verificaba el siguiente valor:

        If (nombre = "CreacionprocFormulasBalance_5_ClaveMP") Then

    Y pensando en que la función no debería devolver un valor válido si el valor del parámetro Nombre no se corresponde con el valor "CreacionprocFormulasBalance_5_ClaveMP", he colocado la línea donde se crea el comando en una posición que no es la correcta, por tanto, como todavía no se ha creado la instancia del objeto, cualquier acceso a sus propiedades o métodos hace que se obtenga una NullReferenceException. Pero ¡claro! Conociéndote me imagino que tendrás 20.000 Case, y tampoco es cuestión de repetir 20.000 veces la creación del comando. :-))

    Ejecuta la creación del comando al comienzo de la función:

        Public Shared Function NombreProcFormulas(nombre As String) As DbCommand
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            ' Creamos el Commando
            Dim cmd As DbCommand = da.CreateCommand()
    
            Select Case ....
    
     
            End Select
    
            Return cmd
    
        End Function

    En este caso, es difícil ya que la función NombreProcFormulas devuelve un valor Nothing, aunque sí puede devolver un comando donde su propiedad CommandText sea una cadena de longitud cero ("") y su propiedad CommandType sea igual a CommandType.Text (valor por defecto), sobre todo si no hay un apartado Case para el caso que estés verificando. Es por ello que no estaría de más que en el procedimiento llamador verifiques si la propiedad CommandText del objeto DbCommand devuelto por la función NombreProcFormulas, tiene o no un valor String.Empty. Si es éste su valor, algún Case se te ha olvidado especificar en la función NombreProcFormulas.


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.






    sábado, 13 de febrero de 2016 16:57
    Moderador
  • Hola maestro:

    Para ser sábado estoy pesadita. Bueno según tu estás "ignorante", vale si tu lo dices.....

    Maestro, me continúa fallando y creo que el error puede estar en el procedimiento almacenado, ya que ahí crea un cmd.CommandText y no se si después cuando vuelve a la llamada de la función pasa alguna cosa porque el error salta en el mismo sitio. Te lo pongo por si tienes abien mirartelo porque tu si hay algo raro lo ves enseguida, pero yo no lo veo, además, me estoy haciendo un poco de lío con el puñetero CommandText. Se que algo hago mal pero no atino. Bueno yo te lo pongo.

    Public Shared Sub CreacionprocFormulasBalance_5_ClaveMP()
            If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse Configuracion.strNombreBaseDeDatos = "PerseoSQLite") <> True Then
                Dim bln As Boolean = ExisteProcedure("procFormulasBalance5digMP")
                If bln = True Then Return
            End If
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            Using cnn As DbConnection = da.CreateConnection()
                Dim cmd As DbCommand = cnn.CreateCommand
    
                'Creación de procedimiento almacenado para "SELECT"
    
                If Configuracion.strNombreBaseDeDatos = "MiCadenaConexion" = True Then
                    cmd.CommandText = "CREATE PROCEDURE procFormulasBalance5digMP " & _
                                 "([@empresa] Text(3), [@planconta] Text(12), [@codigo1] Text(3), [@codigo2] Text(3), [@codigo3] Text(3), [@codigo4] Text(3), [@codigo5] Text(3))) AS " & _
                                 "SELECT Ejer_01, Ejer_02, Ejer_03, Ejer_04, Ejer_05, Ejer_06, Ejer_07, Ejer_08, Ejer_09, Ejer_10, Ejer_11, Ejer_12 " & _
                                 "FROM Balances WHERE idEmpresa = [@empresa] AND PlanConta = [@planconta] " & _
                                 "AND [Clave_MP] IN ([@codigo1],[@codigo2],[@codigo3],[@codigo4],[@codigo5])"
    
                ElseIf Configuracion.strNombreBaseDeDatos = "PerseoSqlEx" = True Then
                    cmd.CommandText = "CREATE PROCEDURE [dbo].[procFormulasBalance5digMP]  " & _
                                "@empresa Nvarchar(3), @planconta Nvarchar(12), @codigo1 Nvarchar(3), @codigo2 Nvarchar(3), @codigo3 Nvarchar(3), @codigo4 Nvarchar(3), @codigo5 Nvarchar(3) " & _
                                "AS " & _
                                "SELECT Ejer_01, Ejer_02, Ejer_03, Ejer_04, Ejer_05, Ejer_06, Ejer_07, Ejer_08, Ejer_09, Ejer_10, Ejer_11, Ejer_12  " & _
                                "FROM Balances WHERE IdEmpresa = @empresa AND PlanConta = @planconta  " & _
                                "AND Clave_MP IN (@codigo1,@codigo2,@codigo3,@codigo4,@codigo5)"
    
                ElseIf Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" = True Then
                    strprocFormulasBalance5digMP = "SELECT Ejer_01, Ejer_02, Ejer_03, Ejer_04, Ejer_05, Ejer_06, Ejer_07, Ejer_08, Ejer_09, Ejer_10, Ejer_11, Ejer_12  " & _
                                "FROM Balances WHERE IdEmpresa = @empresa AND PlanConta = @planconta  " & _
                                "AND Clave_MP IN (@codigo1,@codigo2,@codigo3,@codigo4,@codigo5)"
                    Exit Sub
    
                ElseIf Configuracion.strNombreBaseDeDatos = "PerseoMySQL" = True Then
                    cmd.CommandText = " CREATE DEFINER=`root`@`localhost` PROCEDURE `procFormulasBalance5digMP`(" & _
                               "empresa Nvarchar(3), planconta Nvarchar(12), codigo1 Nvarchar(3), codigo2 Nvarchar(3), codigo3 Nvarchar(3), codigo4 Nvarchar(3), codigo5 Nvarchar(3)) " & _
                               "BEGIN " & _
                               "SELECT Ejer_01, Ejer_02, Ejer_03, Ejer_04, Ejer_05, Ejer_06, Ejer_07, Ejer_08, Ejer_09, Ejer_10, Ejer_11, Ejer_12  " & _
                               "FROM Balances WHERE IdEmpresa = empresa AND PlanConta = planconta  " & _
                               "AND Clave_MP IN (codigo1,codigo2,codigo3,codigo4,codigo5); " & _
                               "End;"
    
                ElseIf Configuracion.strNombreBaseDeDatos = "PerseoSQLite" = True Then
                    strprocFormulasBalance5digMP = "SELECT Ejer_01, Ejer_02, Ejer_03, Ejer_04, Ejer_05, Ejer_06, Ejer_07, Ejer_08, Ejer_09, Ejer_10, Ejer_11, Ejer_12  " & _
                                "FROM Balances WHERE IdEmpresa = @empresa AND PlanConta = @planconta  " & _
                                "AND Clave_MP IN (@codigo1,@codigo2,@codigo3,@codigo4,@codigo5)"
                    Exit Sub
    
                ElseIf Configuracion.strNombreBaseDeDatos = "PerseoOracle" = True Then
                    cmd.CommandText = " CREATE PROCEDURE procFormulasBalance5digMP(" & _
                              "empresa Nvarchar(3), planconta Nvarchar(12), codigo1 Nvarchar(3), codigo2 Nvarchar(3), codigo3 Nvarchar(3), codigo4 Nvarchar(3), codigo5 Nvarchar(3)) " & _
                              "BEGIN() AS " & _
                              "SELECT Ejer_01, Ejer_02, Ejer_03, Ejer_04, Ejer_05, Ejer_06, Ejer_07, Ejer_08, Ejer_09, Ejer_10, Ejer_11, Ejer_12  " & _
                              "FROM Balances WHERE IdEmpresa = empresa AND PlanConta = planconta  " & _
                              "AND Clave_MP IN (codigo1,codigo2,codigo3,codigo4,codigo5); " & _
                              "End;"
                End If
    
                cnn.Open()
                cmd.ExecuteNonQuery()
            End Using
        End Sub

    Un abrazo querido amigo.

    Gemma


    sábado, 13 de febrero de 2016 17:27
  • "gemma_campillo" escribió:

    > Bueno según tu estás "ignorante", vale si tu lo dices.....

    Me refería que, conociéndote, ya me parecía a mí extraño que solo hubiera un solo Case. ;-)

    > Maestro, me continúa fallando y creo que el error puede estar en el procedimiento
    > almacenado, ya que ahí crea un cmd.CommandText y no se si después cuando vuelve
    > a la llamada de la función pasa alguna cosa porque el error salta en el mismo sitio.

    ¡Madre mía! ¿De dónde has sacado el procedimiento CreacionprocFormulasBalance_5_ClaveMP? Y, conociéndote, digo yo que como ese habrá otros "miles" de ellos.

    ¿Continuas obteniendo una NullReferenceException? Si no es así, ¿qué error obtienes ahora?

    Si en el nuevo procedimiento CreacionprocFormulasBalance_5_ClaveMP estás ejecutando una consulta SQL, ¿para qué quieres entonces el procedimiento NombreProcFormulas? Gemma, me vas a perdonar, pero ¡me pierdo! :-(

    Me parece a mí que tu solita te estás complicando la vida queriendo hacer compatible tu aplicación con cualquier tipo de base de datos.

    Mientras me respondes, te dejo lo que tenía escrito sobre la función NombreProcFormulas, aunque viendo el nuevo procedimiento CreacionprocFormulasBalance_5_ClaveMP, ya no sé si servirá de algo:

    Observando el código fuente de la imagen que has publicado, me parece a mí que estás REPITIENDO prácticamente el mismo código en cada Case, por lo que habrá que ver la "longitud" final de la función NombreProcFormulas. ;-)

    >  If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse
    >      Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then

    Por la instrucción If, deduzco que deseas conocer si estás trabajando con el proveedor de SQL Server Compact o de SQLite, y me imagino que será para saber si el proveedor acepta o no procedimientos o consultas almacenadas, en el caso de Access.

    Podrías abreviar el código utilizando la siguiente variable Boolean:

        ' Si el nombre de la base de datos es diferente a PerseoSqlCE y a PerseoSQLite, se comprende que
        ' estás usando un proveedor de datos .net que admite procedimientos o consultas almacenadas.
        '
        Dim aceptaProcedimientos As Boolean = ((Configuracion.strNombreBaseDeDatos <> "PerseoSqlCE") AndAlso
            (Configuracion.strNombreBaseDeDatos <> "PerseoSQLite"))

    Si el valor de la variable 'aceptaProcedimientos' es True, será porque el nombre de la base de datos es diferente a PerseoSqlCE y a PerseoSQLite. Con este cambio, ya te ahorras tener que repetir

      If (Configuracion.strNombreBaseDeDatos = "PerseoSqlCE" OrElse 
          Configuracion.strNombreBaseDeDatos = "PerseoSQLite") Then

    Si deseas probarlo, mira a ver cómo se quedaría la función implementándola como te indico a continuación:

        Public Shared Function NombreProcFormulas(nombre As String) As DbCommand
    
            Dim da As DataAccessInvariant = DataAccessInvariant.GetDataAccessInvariant(Configuracion.CadenaConexion)
    
            ' Si el nombre de la base de datos es diferente a PerseoSqlCE y a PerseoSQLite, se comprende que
             ' estás usando un proveedor de datos .net que admite procedimientos o consultas almacenadas.
             '
             Dim aceptaProcedimientos As Boolean = ((Configuracion.strNombreBaseDeDatos <> "PerseoSqlCE") AndAlso
                 (Configuracion.strNombreBaseDeDatos <> "PerseoSQLite"))
    
            Dim commandText As String = String.Empty
    
            Select Case nombre
                 Case "CreacionprocFormulasBalance_5_ClaveMP"
                     MetodosCreacion.CreacionprocFormulasBalance_5_ClaveMP()
    
                    ' Si acepta procedimientos, le asignamos el nombre del procedimiento;
                     ' en caso contrario, el valor de la variable correspondiente.
                     '
                     commandText = If(aceptaProcedimientos, "procFormulasBalance5digMP", strprocFormulasBalance5digMP)
    
                Case "CreacionprocFormulasBalance_4_ClaveMP"
                     MetodosCreacion.CreacionprocFormulasBalance_4_ClaveMP()
    
                    ' Si acepta procedimientos, le asignamos el nombre del procedimiento;
                     ' en caso contrario, el valor de la variable correspondiente.
                     '
                     commandText = If(aceptaProcedimientos, "procFormulasBalance4digMP", strprocFormulasBalance4digMP)
    
                ' Resto de apartados Case
    
            End Select
    
            ' Creamos el Commando
             '
             Dim cmd As DbCommand = da.CreateCommand()
             cmd.CommandText = commandText
    
            If (aceptaProcedimientos) Then
                 cmd.CommandType = CommandType.StoredProcedure
             End If
    
            Return cmd
    
        End Function

    Fíjate que solo asignamos el valor CommandType.StoredProcedure si la variable 'aceptaProcedimientos' es True (el nombre de la base de datos es diferente a a PerseoSqlCE y a PerseoSQLite).

    Creo que de ésta manera, aparte de reducir el código lo haces un poco más comprensible. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.



    sábado, 13 de febrero de 2016 17:50
    Moderador
  • Hola maestro:

    Si, llevas razón a veces me lio yo solita. Y claro hay un Case por cada procedimiento almacenado como el que jas visto. Ahora ya funciona bien y desde luego ha quedado perfecto, limpio y totalmente legible. Me ha dado la tabarra por no plegar y ponerme a pensar que pasaba.

    Bueno, contigo siempre aprendo cosas nuevas. Esto de las factorías ya va tomando forma.

    Me pongo a arreglar los métidos de las fórmulas y a ver si acabo y te lo envío.

    Enrique, querido amigo, muchas gracias como siempre por tus clases magistrales.

    Un fuerte abrazo de tu amiga.

    Gemma


    sábado, 13 de febrero de 2016 17:58
  • "gemma_campillo" escribió:

    > Y claro hay un Case por cada procedimiento almacenado como el que has visto.

    Con el debido respeto, me parece una auténtica barbaridad. ¿De verdad crees que vas a aprovechar lo que tienes hecho en Access para aplicarlo a otros motores de datos totalmente diferentes a Access? Eso es un trabajazo enorme.

    ¿No te das cuenta que cualquier modificación que hagas en la estructura de una base de datos, vas a tener que aplicarla a todas aquellas otras bases de datos diferentes? Si vas a trabajar con 28 bases de datos diferentes, todos los cambios que efectúes en la estructura de las tablas, vistas, consultas o procedimientos almacenados, vas a tener que aplicarlas a cada una de las 28 bases de datos.

    A mi modo de ver lo que tu estás queriendo hacer, eso lo tenías que haber tenido en mente desde el primer momento en que elegiste Access como motor de datos de tu aplicación, y en lugar de tener creadas XXXXX consultas o procedimientos almacenados en la base de datos, las tenías que haber implementado como Sub o Function en una capa intermedia de tu aplicación, lo que se conoce como capa lógica o de negocio, de ésta manera, en las bases de datos solamente tendrías las tablas, y en tu aplicación las consultas de selección o acción necesarias para que tu aplicación haga su trabajo. ¿Que necesitas modificar un procedimiento? Lo harías en el código fuente de tu aplicación, pero no en todas las diferentes bases de datos. Aparte que, como te has dado cuenta, unas admiten el esquema PROCEDURES y otras no. ¿Qué haces con aquellas bases de datos que no admiten procedimientos almacenados? Digo yo que tendrás que escribir en tu aplicación la consulta SQL que hay que ejecutar en un momento determinado, que es lo que me imagino estás haciendo con los procedimientos del tipo CreacionprocFormulasBalance_5_ClaveMP que me has mostrado.

    Si tu aplicación tuviera los procedimientos necesarios para ejecutar todas las consultas que tienes en tu base de datos de Access, tan solo sería cuestión de llamar al procedimiento adecuado, con independencia que estés usando Access, Oracle o el tipo de base de datos que desees, admitan o no procedimientos almacenados, porque para eso estás trabajando con "factorías",  por lo que el objeto DbConnection que utilices para conectarte con el origen de datos para ejecutar el comando, está debidamente configurado para saber qué proveedor de datos .net tiene que utilizar y qué cadena de conexión deberá de usar.

    > If Configuracion.strNombreBaseDeDatos = "MiCadenaConexion" = True Then

    Por cierto, para conocer si el valor es True, tan solo tienes que ejecutar:

    If (Configuracion.strNombreBaseDeDatos = "MiCadenaConexion") Then

    Sobra el = True. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.



    sábado, 13 de febrero de 2016 18:08
    Moderador
  • Hola Enrique:

    Todos los procesos almacenados que puedan haber en las diferentes base de datos, están hechos desde código, nunca he creado el procedimiento almacenado directamente en la base de datos, tratando de vitar el problema que eso conllevaría el día de que quieras añadir un nuevo procedimiento o consulta a las bases de datos, de esta forma, solo creo el nuevo procedimiento por código y se ejecuta e instala en las bases de datos. No tengo que tocar las base de datos del usuario para nada, lo que se tenga que hacer se lo regulo por código.

    Por otro lado, si la select está dentro del procedimiento de creación del store procedure, tampoco me va mal (lo tengo que acabar de probar), para las que no llevan procedimiento almacenado, de hecho esto que me has arreglado es para los motores de compact y sqLite.

    Ya verás que cuando se acabe este tema funcinará a la perfección.

    Bueno maestro, de cualquier manera y como siempre tomo nora de tus argumentos y explicaciones tal como lo he hecho siempre.

    Te envío un fuerte abrazo y descansa o ve una película o lo que quieras, que siempre estás enganchado al ordenador.

    Gemma

    sábado, 13 de febrero de 2016 18:32