none
Realizar un update a través de una lista genérica. RRS feed

  • Pregunta

  • Hola a todos:

    Precisaría saber si puedo realizar un update a través de List Of, tal como lo tengo en el ejemplo siguiente aplicado a Insert Into y que funciona perfectamente. Lo pongo como ejemplo, pero lo que no se es como sería (si es posible) hacerlo para un UPDATE que voy repitiendo por cada registro y es tedioso hacerlo así.

    Private Shared Sub AgregarRegistroTablaVariosAuditoriaEspaña()
            Dim lst As New List(Of VariosAuditoria)()
    
            Dim empresa As String
            empresa = VarGlobal.StrCodEmpresa
    
            ' Añadimos registros a la lista
            lst.Add(New VariosAuditoria(empresa, "150", "ACIDTEST", "A) REALIZABLE Y TESORERÍA"))
            lst.Add(New VariosAuditoria(empresa, "155", "DISPORD", "A) TESORERÍA O DISPONIBLIDAD ORDINARIA"))
            lst.Add(New VariosAuditoria(empresa, "160", "FONEXIS", "A) ACTIVO CORRIENTE"))
    
     ' 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)
    
            Using cnn As DbConnection = da.CreateConnection()
                cnn.Open()
    
                For Each item As VariosAuditoria In lst
    
                    ' Creamos el Commando
                    Dim cmd As DbCommand = cnn.CreateCommand()
    
                    cmd.CommandText =
                    String.Format("INSERT INTO VariosAuditoria (Cod_Empresa, Orden, Grupo, Descripcion) VALUES ({0})", item.ToString())
    
                    cmd.ExecuteNonQuery()
    
                Next
            End Using
    
        End Sub

    y la clase creada a tal efecto es la siguiente:

    Friend Class VariosAuditoria
    
        Private m_empresa As String
        Private m_orden As String
        Private m_grupo As String
        Private m_descripcion As String
    
        Public Sub New(empresa As String, orden As String, grupo As String, descripcion As String)
            m_empresa = empresa
            m_orden = orden
            m_grupo = grupo
            m_descripcion = descripcion
        End Sub
    
        Public ReadOnly Property empresa As String
            Get
                Return m_empresa
            End Get
        End Property
    
        Public ReadOnly Property orden As String
            Get
                Return m_orden
            End Get
        End Property
    
        Public ReadOnly Property grupo As String
            Get
                Return m_grupo
            End Get
        End Property
    
        Public ReadOnly Property descripcion As String
            Get
                Return m_descripcion
            End Get
        End Property
    
        Public Overrides Function ToString() As String
    
            ' El procedimiento da por hecho que el único valor que puede contener
            ' comillas simples es el valor del campo llamado m_descripcion.
            '
            Dim descripcion As String = String.Empty
            If ((Not String.IsNullOrWhiteSpace(m_descripcion)) AndAlso (m_descripcion.Contains("'"c))) Then
                descripcion = m_descripcion.Replace("'", "''")
            End If
    
            ' Devolver una cadena con los valores de los 4 campos encerrados entre comillas simples.
            Return String.Format("'{0}', '{1}', '{2}', '{3}'", m_empresa, m_orden, m_grupo, m_descripcion)
    
        End Function
    
    End Class

    Bueno, muchas gracias a todos.

    Gemma


    • Editado gemma_campillo miércoles, 27 de abril de 2016 4:53 sintaxis
    miércoles, 27 de abril de 2016 4:53

Respuestas

  • No hay diferencia, para un Update puedes aplicar el mismo mecanismo que estás usando para el Insert, solo con cambiar la sentencia SQL que tienes en el CommandText.

    Hago sin embargo un par de críticas al código que tienes para el Insert:

    - Te bastaría con instanciar el SqlCommand una única vez fuera del bucle, y simplemente cambiarle el commandText dentro del bucle.

    - Tal como lo tienes hecho, con la sentencia sin parametrizar y simplemente concatenando los parámetros, es vulnerable ante ataques de inyección de SQL, no soporta comillas en los campos de texto, requiere que formatees a mano los números con decimales y las fechas, y poluciona el caché de procedimientos en el servidor SQL debido a las sentencias ad-hoc. En resumidas cuentas, es preferible que parametrices la sentencia.

    • Marcado como respuesta gemma_campillo miércoles, 27 de abril de 2016 5:39
    miércoles, 27 de abril de 2016 5:37
  • En la sentencia tendrías que hacer algo así:

    cmd.CommandText = "UPDATE VariosAuditoria (Cod_Empresa, Orden, Grupo, Descripcion) VALUES (@Cod_Empresa, @Orden, @Grupo, @Descripcion) WHERE ID=@ID"

    Siendo ID el campo que estés usando como clave primaria para la tabla, es decir, el que indica cuál es el registro que quieres actualizar.

    Evidentemente, ahora necesitas pasarle esos parámetros al Command, por lo que ya no vale con que los ítems de la lista genérica implementen un .ToString que los devuelva todos concatenados en una cadena, sino que tienes que usar el propio ítem para sacar sus campos. Sería algo así:

    cmd.Parameters.AddWithValue("@Cod_Empresa", item.Empresa)

    y así sucesivamente con los otros campos que se necesiten para la sentencia.

    • Marcado como respuesta gemma_campillo miércoles, 27 de abril de 2016 9:34
    miércoles, 27 de abril de 2016 9:11

Todas las respuestas

  • No hay diferencia, para un Update puedes aplicar el mismo mecanismo que estás usando para el Insert, solo con cambiar la sentencia SQL que tienes en el CommandText.

    Hago sin embargo un par de críticas al código que tienes para el Insert:

    - Te bastaría con instanciar el SqlCommand una única vez fuera del bucle, y simplemente cambiarle el commandText dentro del bucle.

    - Tal como lo tienes hecho, con la sentencia sin parametrizar y simplemente concatenando los parámetros, es vulnerable ante ataques de inyección de SQL, no soporta comillas en los campos de texto, requiere que formatees a mano los números con decimales y las fechas, y poluciona el caché de procedimientos en el servidor SQL debido a las sentencias ad-hoc. En resumidas cuentas, es preferible que parametrices la sentencia.

    • Marcado como respuesta gemma_campillo miércoles, 27 de abril de 2016 5:39
    miércoles, 27 de abril de 2016 5:37
  • Hola Alberto:

    Muchas gracias como siempre por ayudarme.

    Tomo buena nota de lo que me has indicado y voy a ir corrigiendo lo que me has dicho.

    Bueno, voy a luchar un rato construyendo las listas igual que las tengo (corregidas) y ya está.

    Un fuerte abrazo y reitero mis gracias.

    Gemma

    miércoles, 27 de abril de 2016 6:06
  • Hola Alberto:

    Estoy siguiendo tus instrucciones y este apartado no lo entiendo, también te comento que no tengo mucha práctica con las listas genéricas. Si fueras tan amable y tienes un ratito, me podrías indicar como tengo que poner los parámetros, tal cual me indicas en:

    - Tal como lo tienes hecho, con la sentencia sin parametrizar y simplemente concatenando los parámetros, es vulnerable ante ataques de inyección de SQL, no soporta comillas en los campos de texto, requiere que formatees a mano los números con decimales y las fechas, y poluciona el caché de procedimientos en el servidor SQL debido a las sentencias ad-hoc. En resumidas cuentas, es preferible que parametrices la sentencia.

    Es que no se si te refieres al String.Format("INSERT INTO VariosAuditoria (Cod_Empresa, Orden, Grupo, Descripcion) VALUES ({0}) ya que hay le he puesto parámetros a los campos y me da error de que el campo no existe, pero seguro que lo estoy haciendo mal.

    Bueno querido amigo, si puedes te lo miras por favor.

    Un abrazo.

    Gemma

    miércoles, 27 de abril de 2016 6:43
  • En la sentencia tendrías que hacer algo así:

    cmd.CommandText = "UPDATE VariosAuditoria (Cod_Empresa, Orden, Grupo, Descripcion) VALUES (@Cod_Empresa, @Orden, @Grupo, @Descripcion) WHERE ID=@ID"

    Siendo ID el campo que estés usando como clave primaria para la tabla, es decir, el que indica cuál es el registro que quieres actualizar.

    Evidentemente, ahora necesitas pasarle esos parámetros al Command, por lo que ya no vale con que los ítems de la lista genérica implementen un .ToString que los devuelva todos concatenados en una cadena, sino que tienes que usar el propio ítem para sacar sus campos. Sería algo así:

    cmd.Parameters.AddWithValue("@Cod_Empresa", item.Empresa)

    y así sucesivamente con los otros campos que se necesiten para la sentencia.

    • Marcado como respuesta gemma_campillo miércoles, 27 de abril de 2016 9:34
    miércoles, 27 de abril de 2016 9:11
  • Hola Alberto:

    Ahora lo he entendido, es que para eminencias como tu es fácil, pero a mí me cuesta lo suyo, pero bueno, con vuestra ayuda todo se soluciona.

    Gracias querido Alberto.

    Gemma

    miércoles, 27 de abril de 2016 9:36
  • "Alberto Poblacion" escribió:

    > - Tal como lo tienes hecho, con la sentencia sin parametrizar y simplemente concatenando
    > los parámetros, es vulnerable ante ataques de inyección de SQL, ...

    Hola, Alberto:

    Sin entrar en detalles si es adecuado o no utilizar una lista genérica para conforme se recorre la misma ir ejecutando consultas SQL de acción, me pregunto cómo es posible ejecutar un ataque de inyección SQL cuando los valores que se desean insertar, eliminar o actualizar, se encuentran incluidos en los propios elementos de la lista genérica, que se añaden sin intervención alguna del usuario, y dentro de un procedimiento privado compartido que no tiene ningún parámetro de entrada en su firma:

        

    Private Shared Sub AgregarRegistroTablaVariosAuditoriaEspaña() --> SIN PARÁMETROS DE ENTRADA Dim lst As New List(Of VariosAuditoria)() ' Añadimos registros a la lista: NO EXISTE INTERVENCIÓN DEL SUPUESTO ATACANTE
    ' ya que los valores alfanuméricos los escribe directamente el propio programador.
    ' lst.Add(New VariosAuditoria(empresa, "150", "ACIDTEST", "A) REALIZABLE Y TESORERÍA")) lst.Add(New VariosAuditoria(empresa, "155", "DISPORD", "A) TESORERÍA O DISPONIBLIDAD ORDINARIA")) lst.Add(New VariosAuditoria(empresa, "160", "FONEXIS", "A) ACTIVO CORRIENTE")) For Each item As VariosAuditoria In lst ' Creamos el Commando Dim cmd As DbCommand = cnn.CreateCommand() ' CONSTRUIR LA CONSULTA DE ACCIÓN DONDE TAMPOCO INTERVIENE EL SUPUESTO ATACANTE cmd.CommandText = String.Format("INSERT INTO VariosAuditoria (Cod_Empresa, Orden, Grupo, Descripcion) VALUES ({0})", item.ToString()) cmd.ExecuteNonQuery() Next End Sub


    La verdad es que no veo por dónde puede venir un supuesto ataque de inyección de SQL al procedimiento llamado AgregarRegistroTablaVariosAuditoriaEspaña que no tiene ni un sólo parámetro de entrada. ¿?

    Soy consciente que mediante reflexión se puede invocar a un procedimiento no público, esté o no compartido. Si los valores de los elementos de la lista genérica dependieran de los valores pasados al procedimiento, o de aquellos otros que pudieran estar compartidos en la clase o en el propio ensamblado, otro gallo cantaría, pero no observo yo que se den esas circunstancias en el procedimiento que nos muestra nuestra amiga Gemma, salvo que el día de mañana permita que el procedimiento AgregarRegistroTablaVariosAuditoriaEspaña admita parámetros de entrada para rellenar los valores de los elementos de la lista genérica, por lo que entonces habría que ver qué tipo de datos está pasando al procedimiento y si en sus valores existe intervención por parte del usuario de la aplicación, claro está. ;-)

    Un saludo


    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.



    miércoles, 27 de abril de 2016 11:16
    Moderador
  • [...]  me pregunto cómo es posible ejecutar un ataque de inyección SQL cuando los valores que se desean insertar, eliminar o actualizar, se encuentran incluidos en los propios elementos de la lista genérica, que se añaden sin intervención alguna del usuario

    No, no es cierto que esos datos se introduzcan sin intervención alguna de un usuario. La lista tiene datos de tipo cadena tales como Grupo y Descripcion, que necesariamente en algún momento ha tenido que teclear un usuario (desde algún procedimiento de entrada que no es visible en los ejemplos de código que tenemos a la vista). Si el usuario, por ejemplo, entregó un fichero CSV que luego se importa a la Lista (varios días después de haber sido entregado), y el fichero contenía una sentencia SQL dentro del campo descripción, entonces se ejecutará el ataque de inyección en el momento en que esa Lista pase por la rutina que tenemos entre manos para grabarlo en la BD.
    miércoles, 27 de abril de 2016 12:43
  • "Alberto Poblacion" escribió:

    > No, no es cierto que esos datos se introduzcan sin intervención alguna
    > de un usuario. La lista tiene datos de tipo cadena tales como Grupo y
    > Descripcion, que necesariamente en algún momento ha tenido que teclear
    > un usuario (desde algún procedimiento de entrada que no es visible en
    > los ejemplos de código que tenemos a la vista). Si el usuario, por ejemplo,
    > entregó un fichero CSV que luego se importa a la Lista (varios días después
    > de haber sido entregado), y el fichero contenía una sentencia SQL dentro del
    > campo descripción, entonces se ejecutará el ataque de inyección en el momento
    > en que esa Lista pase por la rutina que tenemos entre manos para grabarlo en la BD.

    Conociendo la aplicación de Gemma, sigo pensando que no hay intervención del usuario, ya que la lista genérica se crea "manualmente" dentro del propio procedimiento privado, sin ni siquiera leer un simple archivo de texto delimitado. Fíjate que las propiedades de la clase VariosAuditoria son de sólo lectura, ya que es en el constructor de la clase donde le pasa los valores.

    Si la lista se rellenara de elementos procedentes de un archivo CSV, puede que alguien malintencionado escribiera valores susceptibles de un ataque de inyección SQL, pero tal cual está escrito el procedimiento, perdóname pero yo no lo veo.

    Te lo he preguntado porque llevo un tiempo que la gente no hace más que preguntarme por ataques de inyección SQL, como si todo el mundo estuviera "histérico" con dichos ataques, y aunque está bien no bajar la guardia y tenerlos siempre en cuenta, entiendo que también hay que tener en cuenta dónde se puede producir y dónde no, y la verdad sea dicha, yo no veo que el código que se ejecuta en el procedimiento AgregarRegistroTablaVariosAuditoriaEspaña, si es ese realmente el que se ejecuta, sea susceptible de un ataque de inyección de SQL. ;-)


    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.

    miércoles, 27 de abril de 2016 12:56
    Moderador
  • Fíjate que las propiedades de la clase VariosAuditoria son de sólo lectura, ya que es en el constructor de la clase donde le pasa los valores.

    Da igual. En algún momento en alguna otra parte del programa se han tenido que recoger esos valores para pasárselos al constructor de la clase. Y en aquél sitio en el que se hayan recogido, tiene que haber habido una intervención del usuario, ya que hay datos como "Descripción" que alguien ha tenido que teclear, no creo que el programa se esté inventando mencánicamente las descripciones. Si en esa pantalla donde el usuario teclea la Descripción se introduce un fragmento de SQL, el resultado final es que se acabará ejecutando dentro de la sentencia de inserción puesto que se concatena con ella, con independencia de todos los pasos intermedios (constructores, propiedades de solo-lectura) por los que haya atravesado. Y es más, el atacante ni siquiera tiene que conocer esos pasos, le basta con teclear el ataque en la pantalla donde se pregunta la descripción. Por eso son tan peligrosos estos ataques, más de lo que habitualente se cree.
    miércoles, 27 de abril de 2016 13:45
  • Hola queridos amigos:

    Después de la conversación maestra con la que me habéis obsequiado, únicamente decir que el usuario no tiene acceso a la descripción ni a ningún campo de los mostrados en la lista, ya que esos se los relleno yo y se los muestro en un datagridview.

    Nada más era hacer esa precisión.

    Un saludo a los dos-

    Gema


    miércoles, 27 de abril de 2016 14:35
  • [...] se los relleno yo y se los muestro en un datagridview.

    Cuando dices que se los rellenas tú... ¿vas a ser siempre tú en persona, o puede ocurrir que algún día delegues esta tarea en algún empleado tuyo? De ser así, ese empleado podría hacer un ataque de inyección de SQL contra la base de datos. Incluso aunque no sea este tu caso, hablando en sentido genérico sobre una aplicación cualquiera, es factible atacarla desde las pantallas de introducción de datos si el código no se protege contra los ataques de SQL.

    miércoles, 27 de abril de 2016 15:04
  • "Alberto Población" escribió:

    > Cuando dices que se los rellenas tú... ¿vas a ser siempre tú en persona, o
    > puede ocurrir que algún día delegues esta tarea en algún empleado tuyo? De
    > ser así, ese empleado podría hacer un ataque de inyección de SQL contra la
    > base de datos.

    ¡Bueno! Vamos a pensar que los empleados son honrados y honestos, porque por esa misma regla de tres, no nos podríamos fiar de nadie que contratásemos para el trabajo de desarrollo. ;-)

    Aparte, no creo que un empleado sea tan tonto (por no utilizar otro adjetivo calificativo peor) de arriesgarse, como mínimo a un despido justificado, y no sé si a una posible responsabilidad penal por los daños que pudiera haber ocasionado al introducir intencionadamente un ataque de inyección de SQL, lo que no exime de responsabilidad alguna a la empresa o empleador del trabajador.

    > Incluso aunque no sea este tu caso, hablando en sentido genérico sobre una
    > aplicación cualquiera, es factible atacarla desde las pantallas de introducción
    > de datos si el código no se protege contra los ataques de SQL.

    Efectivamente, es el clásico ejemplo donde un atacante puede escribir el código de inyección en un simple control TextBox.

    Que conste que no estoy en contra de proteger las aplicaciones contra el código de inyección de SQL, ni de cualquier otros ataques diferentes. Como te he comentado antes, simplemente hay que ser consciente del peligro e identificar qué partes de nuestro código son susceptibles de cualquier ataque y que otras partes no lo son. Cuantas más partes tengamos seguras, mucho mejor, y proteger aquellas donde se podría producir un ataque cualquiera. ;-)


    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.




    miércoles, 27 de abril de 2016 15:38
    Moderador
  • ¡Bueno! Vamos a pensar que los empleados son honrados y honestos

    Sí, eso es lo que tiende a pensar todo el mundo... Hace cinco años las estadísticas indicaban que el 70% de los ataques informáticos sufridos por las empresas provenían de sus empleados, frente a un 30% provenientes del exterior. Aunque hoy en día la proporción está cambiando, aún así todavía tenemos del orden de un 50% de ataques provenientes de los empleados.
    miércoles, 27 de abril de 2016 17:13