none
Problemas con la conexión hacia servidor externo RRS feed

  • Pregunta

  • Estimados, les cuento que tengo un sistema andando en vb.net y sql server 2008. El tema es que llevo tiempo usandolo en un servidor estadounidense (domain.com) y he tenido muchos problemas con la conexión.

    La verdad no se si es netamente problema de ellos (al ser un servidor compartido y mas económico) o tal vez sea problema en mi conexión?

    Este mensaje me aparece, no siempre, al momento de realizar un login en el sistema:


    dejo los códigos pertinentes:

    Public Shared Function SQL_Conexion() As SqlConnection
    
            Dim oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
            Try
                oconn.Open()
                Return oconn
            Catch ex As Exception
                oconn = Nothing
                Return oconn
            End Try
        End Function

    Acá realizo el código para la conexión y los datos de estos los paso por el webconfig.

    Espero me puedan ayudar con esto, se los agradecería mucho.

    Saludos Cordiales


    • Editado n4n2 martes, 17 de mayo de 2016 19:45
    martes, 17 de mayo de 2016 19:42

Respuestas

  • Es que lo has puesto mal. El método Open no devuelve una conexión, como aparentemente esperas a la vista del código que has escrito. Lo que hace el Open es que deja abierta la instancia sobre la que lo ejecutas. No devuelve ningún resultado. En otras palabras, solo hay que poner oConn.Open() en una línea por sí solo, y luego en la siguiente línea .Connection=oConn.

    • Marcado como respuesta n4n2 viernes, 20 de mayo de 2016 14:34
    jueves, 19 de mayo de 2016 18:04

Todas las respuestas

  • Deberías cambiar la forma en la que abres la conexión, porque tal como la tienes el error exacto queda oculto. Si al abrir la conexión da cualquier error, la subrutina devuelve Nothing, y cuando luego en otro sitio se intenta usar ese resultado, da un error de que la conexión no está inicializada (porque vale Nothing), así que no vemos cuál fue de verdad el error que se produjo al abrirla, que es el que nos daría información sobre cuál es en realidad el problema.

    Quita el try...catch para que en caso de error dé un error en la subrutina SQL_Conexion(), y así veremos cuál es el problema que se produce al abrirla.

    martes, 17 de mayo de 2016 21:21
  • Debería quedar algo como esto?

    Public Shared Function SQL_Conexion() As SqlConnection
    
            Dim oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
    
                oconn.Open()
                Return oconn
    
    End Function

    martes, 17 de mayo de 2016 22:58
  • Eso es. Ahora ejecútalo de esa manera, y cuando se produzca un error en el Open() el mensaje de error debería detallarnos la causa del error.
    miércoles, 18 de mayo de 2016 5:47
  • Eso es. Ahora ejecútalo de esa manera, y cuando se produzca un error en el Open() el mensaje de error debería detallarnos la causa del error.

    Me apareció este error ahora que quite el try catch:

    Dejo un par de ejemplos de los metodos que utilizo en vb.net. Al parecer no los estoy cerrando omo debería...

    Public Shared Function Ingresar_Beneficiario(ByVal rutbeneficiario As String, _ ByVal nombres As String, _ ByVal apellidos As String, _ ByVal fechaactual As Date, _ ByVal fechanacimiento As Date, _ ByVal direccion As String, _ ByVal ciudad As Integer, _ ByVal sistemasalud As Integer, _ ByVal tramoisapre As Integer, _ ByVal genero As String, _ ByVal correo As String, _ ByVal celular As String, _ ByVal estadocivil As Integer, _ ByVal ocupacion As String, _ ByVal categoria As Integer, _ ByVal fonofijo As String) As Boolean Dim comando As New SqlCommand With comando .Connection = SQL_Conexion() .Parameters.Clear() .CommandType = Data.CommandType.StoredProcedure .CommandText = "sp_InsertarBeneficiario" .Parameters.Add("@rutbeneficiario", Data.SqlDbType.Char, 12).Direction = Data.ParameterDirection.Input .Parameters.Add("@nombres", Data.SqlDbType.Char, 30).Direction = Data.ParameterDirection.Input .Parameters.Add("@apellidos", Data.SqlDbType.Char, 30).Direction = Data.ParameterDirection.Input .Parameters.Add("@fechaactual", Data.SqlDbType.DateTime).Direction = Data.ParameterDirection.Input .Parameters.Add("@fechanacimiento", Data.SqlDbType.Date).Direction = Data.ParameterDirection.Input .Parameters.Add("@direccion", Data.SqlDbType.Char, 100).Direction = Data.ParameterDirection.Input .Parameters.Add("@ciudad", Data.SqlDbType.Int).Direction = Data.ParameterDirection.Input .Parameters.Add("@sistemasalud", Data.SqlDbType.Int).Direction = Data.ParameterDirection.Input .Parameters.Add("@tramoisapre", Data.SqlDbType.Int).Direction = Data.ParameterDirection.Input .Parameters.Add("@genero", Data.SqlDbType.Char, 9).Direction = Data.ParameterDirection.Input .Parameters.Add("@correo", Data.SqlDbType.Char, 100).Direction = Data.ParameterDirection.Input .Parameters.Add("@celular", Data.SqlDbType.Char, 8).Direction = Data.ParameterDirection.Input .Parameters.Add("@estadocivil", Data.SqlDbType.Int).Direction = Data.ParameterDirection.Input .Parameters.Add("@ocupacion", Data.SqlDbType.Char, 100).Direction = Data.ParameterDirection.Input .Parameters.Add("@categoria", Data.SqlDbType.Int).Direction = Data.ParameterDirection.Input .Parameters.Add("@fonofijo", Data.SqlDbType.Char, 10).Direction = Data.ParameterDirection.Input .Parameters.Add("@estcod", Data.SqlDbType.Int).Direction = Data.ParameterDirection.Output .Parameters.Add("@estmsg", Data.SqlDbType.Char, 200).Direction = Data.ParameterDirection.Output .Parameters("@rutbeneficiario").Value = rutbeneficiario .Parameters("@nombres").Value = nombres .Parameters("@apellidos").Value = apellidos .Parameters("@fechaactual").Value = fechaactual .Parameters("@fechanacimiento").Value = fechanacimiento .Parameters("@direccion").Value = direccion .Parameters("@ciudad").Value = ciudad .Parameters("@sistemasalud").Value = sistemasalud .Parameters("@tramoisapre").Value = tramoisapre .Parameters("@genero").Value = genero .Parameters("@correo").Value = correo .Parameters("@celular").Value = celular .Parameters("@estadocivil").Value = estadocivil .Parameters("@ocupacion").Value = ocupacion .Parameters("@categoria").Value = categoria .Parameters("@fonofijo").Value = fonofijo .ExecuteNonQuery()

    [ACA DEBERIA USAR ESTE CODIGO PARA CERRAR LA CONEXION? = .Connection.Close()]

    mensajeBDC = .Parameters("@estmsg").Value.ToString() If .Parameters("@estcod").Value = 0 Then Return True Else Return False End If End With


    'Función encargada de mostrar el total de pacientes
        Public Shared Function TotalPacientes() As DataSet
            Dim dset As New DataSet
            Dim commando As New SqlCommand
            Dim da As New SqlDataAdapter(commando)
            With commando
                .Connection = clsConexion.SQL_Conexion
                .Parameters.Clear()
                .CommandType = CommandType.StoredProcedure
                .CommandText = "sp_label_TotalPacientes"
                .ExecuteNonQuery()
                da.SelectCommand = commando
                da.Fill(dset, "Beneficiario")
                .Dispose()
    
                [ACA DEBERIA USAR ESTE CODIGO PARA CERRAR LA CONEXION? = .Connection.Close()]
    
            End With
            Return dset
        End Function

    Deje un mensaje en cada uno de los codigos para saber si es eso lo que me falta en cada funcion?

    Saludos


    • Editado n4n2 miércoles, 18 de mayo de 2016 21:09 actualizacion
    miércoles, 18 de mayo de 2016 20:37
  • hola

    lo que noto incorrecto es que al usar el metodo SQL_Conexion() cada invocacion crea un objeto de conexion nuevo sin hacer uso de Application pool administrado por ado.net

    se recomienda que la conexion debes definirse dentro de un bloque

    Using oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
    
      'aqui codigo
    
    End Using

    el tema es que de esta forma no vas a poder devolver el objeto de conexion, porque el codigo debe estar dentro del using

    lo que recomendaria es que no definas ningun metodo SQL_Conexion() ni ningun tipo de funcionalidad similar

    si quieres encapsular la conexion programa una capa de datos, pero no estas clases que son para problema

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    miércoles, 18 de mayo de 2016 21:20
  • hola

    lo que noto incorrecto es que al usar el metodo SQL_Conexion() cada invocacion crea un objeto de conexion nuevo sin hacer uso de Application pool administrado por ado.net

    se recomienda que la conexion debes definirse dentro de un bloque

    Using oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
    
      'aqui codigo
    
    End Using

    el tema es que de esta forma no vas a poder devolver el objeto de conexion, porque el codigo debe estar dentro del using

    lo que recomendaria es que no definas ningun metodo SQL_Conexion() ni ningun tipo de funcionalidad similar

    si quieres encapsular la conexion programa una capa de datos, pero no estas clases que son para problema

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    Ya veo mas claro el problema.

    Entonces como podría hacerlo para abrir y cerrar la conexión cada vez que hago un insert o un select para llenar un gridview sin usar la clase clsConexión en cada clase que utilice dichos métodos y funciones?

    Por ejemplo en esta funcion que me carga el gridview, quedaría algo así?:

    'Función encargada de cargar información en la grilla dgvPaciente
        Public Shared Function llenar_grilla_Paciente(ByVal idBeneficiario As Integer) As DataSet
            Dim dset As New DataSet
            Dim commando As New SqlCommand
            Dim da As New SqlDataAdapter(commando)
            With commando
    
                Using oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
    
                .Parameters.Clear()
                .CommandType = CommandType.StoredProcedure
                .CommandText = "sp_llenar_GrillaPaciente"
                .Parameters.Add("@idBeneficiario", SqlDbType.Int).Direction = ParameterDirection.Input
                .Parameters("@idBeneficiario").Value = idBeneficiario
                .ExecuteNonQuery()
                da.SelectCommand = commando
                da.Fill(dset, "dgvPaciente")
                .Dispose()
                
                End Using
                SqlConnection.ClearAllPools()
    
            End With
            Return dset
        End Function

    miércoles, 18 de mayo de 2016 22:32
  • Deje un mensaje en cada uno de los codigos para saber si es eso lo que me falta en cada funcion?


    Sí. Para que entiendas lo que está pasando: cada vez que abres una conexión, esta se añade al Pool. Cuando la cierras, no se cierra de verdad, sino que se devuelve al Pool. La siguiente vez que abres una conexión, no se abre de verdad, sno que se toma del Pool la que ya estaba abierta. Estas operaciones de tomar del pool y devolver al pool son muy rápidas, por lo que es más eficiente que abrir y cerrar realmente las conexiones.

    Pero, ¿qué pasa si no las cierras? Pues que el Pool va creciendo poco a poco hasta que alcanza el número máximo de conexiones. La siguiente vez que quieres abrir una, se queda parado esperando a que se cierre alguna de las anteriores. Pero como nunca las cierras, al cabo de un rato da un Timeout, que es precisamente lo que dice el mensaje de error que estás viendo.

    Conclusión: tienes que garantizar que siempre que abres una conexión, después la cierres. Puedes ser cuidadosísimo y escribir a mano todos los Close() en todos los sitios adecuados. Pero es más recomendable usar para ello la construcción "using", como ya te han sugerido en otras respuestas anteriores.

    jueves, 19 de mayo de 2016 6:25
  • Deje un mensaje en cada uno de los codigos para saber si es eso lo que me falta en cada funcion?


    Sí. Para que entiendas lo que está pasando: cada vez que abres una conexión, esta se añade al Pool. Cuando la cierras, no se cierra de verdad, sino que se devuelve al Pool. La siguiente vez que abres una conexión, no se abre de verdad, sno que se toma del Pool la que ya estaba abierta. Estas operaciones de tomar del pool y devolver al pool son muy rápidas, por lo que es más eficiente que abrir y cerrar realmente las conexiones.

    Pero, ¿qué pasa si no las cierras? Pues que el Pool va creciendo poco a poco hasta que alcanza el número máximo de conexiones. La siguiente vez que quieres abrir una, se queda parado esperando a que se cierre alguna de las anteriores. Pero como nunca las cierras, al cabo de un rato da un Timeout, que es precisamente lo que dice el mensaje de error que estás viendo.

    Conclusión: tienes que garantizar que siempre que abres una conexión, después la cierres. Puedes ser cuidadosísimo y escribir a mano todos los Close() en todos los sitios adecuados. Pero es más recomendable usar para ello la construcción "using", como ya te han sugerido en otras respuestas anteriores.

    Muchas gracias por la aclaración. Ahora se como funciona el tema de las conexiones con mayor profundidad.

    Probe usando el using que me recomiendan, en la clase clsConexión, que es la que importo en cada clase que utilizo llamadas a la base de datos, pero al momento de ejecutar mi aplicación me arroja un mensaje diciendo que no esta abierta dicha conexión...

    Entonces no debería cerrar el using en la misma clase clsConexion vdd?

    Tendría que usar el using en cada metodo y funcion de mi sistema?

    Disculpen la insistencia, pero me gustaría entender mas sobre el tema.



    • Editado n4n2 jueves, 19 de mayo de 2016 14:21
    jueves, 19 de mayo de 2016 14:21
  • Tendría que usar el using en cada metodo y funcion de mi sistema?

    Así es. El "using" garantiza que siempre se cierra la conexión en cuanto la ejecución abandona el bloque que está detrás del Using. Por lo tanto, si lo haces en una clase, en cuanto sales de la clase se cierra la conexión. Por lo tanto, al programa que usaba la clase le llega la conexión cerrada.

    Si se quiere se puede hacer "Using laConexion as SqlConnection = Miclase.MiMetodo()", y que MiMetodo devuelva una conexión abierta. Pero el Using tiene que estar en el método que va a usar la conexión, no en el método que la devuelve.

    jueves, 19 de mayo de 2016 14:29
  • Tendría que usar el using en cada metodo y funcion de mi sistema?

    Así es. El "using" garantiza que siempre se cierra la conexión en cuanto la ejecución abandona el bloque que está detrás del Using. Por lo tanto, si lo haces en una clase, en cuanto sales de la clase se cierra la conexión. Por lo tanto, al programa que usaba la clase le llega la conexión cerrada.

    Si se quiere se puede hacer "Using laConexion as SqlConnection = Miclase.MiMetodo()", y que MiMetodo devuelva una conexión abierta. Pero el Using tiene que estar en el método que va a usar la conexión, no en el método que la devuelve.

    Intente usar el using en cada metodo y funcion como me recomendaron, pero no se que estaré haciendo mal que me arroja un problema de conexión cerrada.

    Dejo captura del error y del código que modifique:

    Public Shared Function TotalPacientes() As DataSet
            Using oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
                Dim dset As New DataSet
                Dim commando As New SqlCommand
                Dim da As New SqlDataAdapter(commando)
                With commando
                    .Connection = oconn
                    .Parameters.Clear()
                    .CommandType = CommandType.StoredProcedure
                    .CommandText = "sp_label_TotalPacientes"
                    .ExecuteNonQuery()
                    da.SelectCommand = commando
                    da.Fill(dset, "Beneficiario")
                    .Dispose()
    
                End With
                Return dset
            End Using
        End Function


    jueves, 19 de mayo de 2016 15:59
  •  me arroja un problema de conexión cerrada.

    Fíjate en el código que has escrito. Te falta poner el Open() de la conexión.

    No sería necesario si únicamente usaras el DataAdapter, que ya abre él solo la conexión si la encuentra cerrada. Pero antes has puesto un ExecuteNonQuery, y éste sí que requiere que antes abras la conexión.

    Por cierto, es inútil poner primero el ExecuteNonQuery y luego hacer un Fill con el DataAdapter. Eso ejecuta dos veces el procedimiento almacenado, y los resultados de la primera vez los "tira" y no valen para nada.

    jueves, 19 de mayo de 2016 16:31
  •  me arroja un problema de conexión cerrada.

    Fíjate en el código que has escrito. Te falta poner el Open() de la conexión.

    No sería necesario si únicamente usaras el DataAdapter, que ya abre él solo la conexión si la encuentra cerrada. Pero antes has puesto un ExecuteNonQuery, y éste sí que requiere que antes abras la conexión.

    Por cierto, es inútil poner primero el ExecuteNonQuery y luego hacer un Fill con el DataAdapter. Eso ejecuta dos veces el procedimiento almacenado, y los resultados de la primera vez los "tira" y no valen para nada.

    No agregue el open() xq me arroja el siguiente warning:

    Tu dices que mejor quito el ExecuteNonQuery y dejo solo el Fill?

    Seria mucha la molestia si me pudieras guiar en el código correcto de ese método?

    Muchas Gracias!

    jueves, 19 de mayo de 2016 17:58
  • Es que lo has puesto mal. El método Open no devuelve una conexión, como aparentemente esperas a la vista del código que has escrito. Lo que hace el Open es que deja abierta la instancia sobre la que lo ejecutas. No devuelve ningún resultado. En otras palabras, solo hay que poner oConn.Open() en una línea por sí solo, y luego en la siguiente línea .Connection=oConn.

    • Marcado como respuesta n4n2 viernes, 20 de mayo de 2016 14:34
    jueves, 19 de mayo de 2016 18:04
  • Es que lo has puesto mal. El método Open no devuelve una conexión, como aparentemente esperas a la vista del código que has escrito. Lo que hace el Open es que deja abierta la instancia sobre la que lo ejecutas. No devuelve ningún resultado. En otras palabras, solo hay que poner oConn.Open() en una línea por sí solo, y luego en la siguiente línea .Connection=oConn.

    Ahora si me funcionó, muchas gracias!

    dejo el código modificado:

    Public Shared Function TotalPacientes() As DataSet
            Using oconn As New SqlConnection(ConfigurationManager.ConnectionStrings("Conexion").ConnectionString)
                Dim dset As New DataSet
                Dim commando As New SqlCommand
                Dim da As New SqlDataAdapter(commando)
                With commando
                    oconn.Open()
                    .Connection = oconn
                    .Parameters.Clear()
                    .CommandType = CommandType.StoredProcedure
                    .CommandText = "sp_label_TotalPacientes"
                    .ExecuteNonQuery()
                    da.SelectCommand = commando
                    da.Fill(dset, "Beneficiario")
                    .Dispose()
                End With
                Return dset
            End Using
        End Function

    Con respecto al ExecuteNonQuery() y el da.Fill. Como podría mejorar ese paso, ya que me dices que estoy haciendo un paso demas e innecesario al usar el primero?

    Debería quitar ExecuteNonQuery()?


    • Editado n4n2 jueves, 19 de mayo de 2016 19:35
    jueves, 19 de mayo de 2016 19:35
  • Sí, deberías quitar el ExecuteNonQuery.

    Y una vez que lo hayas quitado, si quieres puedes también quitar el Open, que ya no es necesario porque el Fill lo hace internamente (aunque no tiene ninguna desventaja si optas por dejarlo).

    Idealmente, todo esto no deberías hacerlo "a ciegas", sino que sería muy conveniente que te te dieras cuenta de cómo funciona y comprendieses por qué es superfluo ese ExecuteNonQuery. Si no entiendes por qué sobra, probablemente cometerás errores similares en otros sitios de tu código.

    viernes, 20 de mayo de 2016 5:24
  • Sí, deberías quitar el ExecuteNonQuery.

    Y una vez que lo hayas quitado, si quieres puedes también quitar el Open, que ya no es necesario porque el Fill lo hace internamente (aunque no tiene ninguna desventaja si optas por dejarlo).

    Idealmente, todo esto no deberías hacerlo "a ciegas", sino que sería muy conveniente que te te dieras cuenta de cómo funciona y comprendieses por qué es superfluo ese ExecuteNonQuery. Si no entiendes por qué sobra, probablemente cometerás errores similares en otros sitios de tu código.

    Muchas gracias por tu ayuda Alberto. Tendré en consideración tu consejo y le dedicare mas tiempo a comprender bien el código.

    Saludos!

    viernes, 20 de mayo de 2016 14:33