Configurar las opciones de nivel comando y de conexión de la capa de acceso a datos (VB)

por Scott Mitchell

Descargar PDF

Los TableAdapter que están dentro de un conjunto de datos con tipo se encargan automáticamente de conectarse a la base de datos, emitir comandos y rellenar una DataTable con los resultados. Sin embargo, habrá ocasiones en las que queramos cuidar estos detalles y, en este tutorial, aprenderemos a acceder a la configuración de nivel de comando y conexión de la base de datos del TableAdapter.

Introducción

A lo largo de la serie de tutoriales, se usaron conjuntos de datos con tipo para implementar la capa de acceso a datos y los objetos empresariales de la arquitectura superpuesta. Como se describe en el primer tutorial, los DataTables del conjunto de datos con tipo actúan como repositorios de datos, mientras que los TableAdapter actúan como contenedores para comunicarse con la base de datos para recuperar y modificar los datos subyacentes. Los TableAdapter encapsulan la complejidad implicada en el trabajo con la base de datos y nos evita tener que escribir código para conectar con la base de datos, emitir un comando o rellenar los resultados en un DataTable.

Sin embargo, hay ocasiones en las que es necesario sumergirse en las profundidades del TableAdapter y escribir código que funcione directamente con objetos de ADO.NET. En el tutorial Ajuste de modificaciones de base de datos en una transacción, por ejemplo, se agregan métodos a TableAdapter para comenzar, confirmar y revertir transacciones de ADO.NET. Estos métodos usaron un objeto SqlTransaction interno creado manualmente que se asignó a los objetos SqlCommand de TableAdapter.

En este tutorial, examinaremos cómo acceder a la configuración de nivel de comando y conexión de base de datos en TableAdapter. En concreto, se agregará funcionalidad al ProductsTableAdapter que permite el acceso a la configuración de tiempo de espera de comandos y cadena de conexión subyacentes.

Trabajar con datos mediante ADO.NET

Microsoft .NET Framework contiene una gran cantidad de clases diseñadas específicamente para trabajar con datos. Estas clases, que se encuentran en el System.Data espacio de nombres, se conocen como clases de ADO.NET. Algunas de las clases que están bajo el paraguas de ADO.NET están vinculadas a un proveedor de datos determinado. Piense en un proveedor de datos como un canal de comunicación que permite que la información fluya entre las clases de ADO.NET y el almacén de datos subyacente. Hay proveedores generalizados, como OleDb y ODBC, así como proveedores especialmente diseñados para sistemas de base de datos determinados. Por ejemplo, aunque es posible conectarse a una base de datos de Microsoft SQL Server mediante el proveedor OleDb, el proveedor SqlClient resulta mucho más eficaz, ya que se diseñó y optimizó específicamente para SQL Server.

Cuando se accede mediante programación a los datos, se suele usar el siguiente patrón:

  1. Establezca una conexión a la base de datos.
  2. Emita un comando.
  3. En el caso de las consultas SELECT, trabaje con los registros resultantes.

Hay clases de ADO.NET independientes para realizar cada uno de estos pasos. Para conectarse a una base de datos mediante el proveedor SqlClient, por ejemplo, use la SqlConnection clase. Para emitir un comando INSERT, UPDATE, DELETE o SELECT a la base de datos, use la clase SqlCommand.

Excepto para las modificaciones de base de datos de ajuste dentro de un tutorial de transacción, no hubo que escribir ningún código de nivel bajo de ADO.NET porque el código generado automáticamente de TableAdapters incluye la funcionalidad necesaria para conectarse a la base de datos, emitir comandos, recuperar datos y rellenar esos datos en DataTables. Sin embargo, podría haber ocasiones en las que fuera necesario personalizar esta configuración de bajo nivel. En los siguientes pasos examinaremos cómo pulsar en los objetos de ADO.NET utilizados internamente por TableAdapters.

Paso 1: examinar con la propiedad Connection

Cada clase TableAdapter tiene una propiedad Connection que especifica información de conexión de base de datos. El valor ConnectionString y el tipo de datos de esta propiedad vienen determinados por las selecciones realizadas en el Asistente para configuración de TableAdapter. Recuerde que cuando se agrega por primera vez un TableAdapter a un DataSet con tipo, este asistente pide el origen de la base de datos (vea la figura 1). La lista desplegable de este primer paso incluye esas bases de datos especificadas en el archivo de configuración, así como cualquier otra base de datos de las conexiones de datos del Explorador de servidores. Si la base de datos que queremos usar no existiera en la lista desplegable, especifique una nueva conexión de base de datos haciendo clic en el botón Nueva conexión y proporcionando la información de conexión necesaria.

The First Step of the TableAdapter Configuration Wizard

Figura 1: primer paso del Asistente para configuración de TableAdapter (Haga clic para ver la imagen a tamaño completo)

Dediquemos un momento a inspeccionar el código de la propiedad Connection de TableAdapter. Como se indicó en el tutorial Creación de una capa de acceso a datos, es posible ver el código TableAdapter generado automáticamente. Para ello, vaya a la ventana Vista de clases, explore en profundidad la clase adecuada y, a continuación, haga doble clic en el nombre del miembro.

Vaya a la ventana Vista de clases. Para ello, vaya al menú Ver y elija Vista de clases (o escriba Ctrl+Mayús+C). En la mitad superior de la ventana Vista de clases, explore en profundidad el espacio de nombres NorthwindTableAdapters y seleccione la clase ProductsTableAdapter. Se mostrarán los miembros de ProductsTableAdapter en la mitad inferior de la Vista de clases, tal y como se muestra en la figura 2. Haga doble clic en la propiedad Connection para ver su código.

Double-Click the Connection Property in the Class View to View Its Auto-Generated Code

Figura 2: haga doble clic en la propiedad Connection de la Vista de clases para ver su código generado automáticamente

A continuación, se muestra la propiedad Connection de TableAdapter y otro código relacionado con la conexión:

Private _connection As System.Data.SqlClient.SqlConnection
Private Sub InitConnection()
    Me._connection = New System.Data.SqlClient.SqlConnection
    Me._connection.ConnectionString = _
        ConfigurationManager.ConnectionStrings("NORTHWNDConnectionString").ConnectionString
End Sub
Friend Property Connection() As System.Data.SqlClient.SqlConnection
    Get
        If (Me._connection Is Nothing) Then
            Me.InitConnection
        End If
        Return Me._connection
    End Get
    Set
        Me._connection = value
        If (Not (Me.Adapter.InsertCommand) Is Nothing) Then
            Me.Adapter.InsertCommand.Connection = value
        End If
        If (Not (Me.Adapter.DeleteCommand) Is Nothing) Then
            Me.Adapter.DeleteCommand.Connection = value
        End If
        If (Not (Me.Adapter.UpdateCommand) Is Nothing) Then
            Me.Adapter.UpdateCommand.Connection = value
        End If
        Dim i As Integer = 0
        Do While (i < Me.CommandCollection.Length)
            If (Not (Me.CommandCollection(i)) Is Nothing) Then
                CType(Me.CommandCollection(i), _
                    System.Data.SqlClient.SqlCommand).Connection = value
            End If
            i = (i + 1)
        Loop
    End Set
End Property

Cuando se crea una instancia de la clase TableAdapter, la variable de miembro _connection es igual a Nothing. Cuando se accede a la propiedad Connection, primero comprueba si se creó una instancia de la variable de miembro _connection. Si no la tuviera, se invocará el método InitConnection, que creará instancias de _connection y establecerá su propiedad ConnectionString en el valor de cadena de conexión especificado desde el primer paso del Asistente para configuración de TableAdapter.

La propiedad Connection también se puede asignar a un objeto SqlConnection. Al hacerlo, se asociará el nuevo objeto SqlConnection a cada uno de los objetos SqlCommand de TableAdapter.

Paso 2: exponer la configuración de nivel de conexión

La información de conexión debería permanecer encapsulada dentro de TableAdapter y no ser accesible para otras capas de la arquitectura de la aplicación. Sin embargo, podría haber escenarios en los que la información de nivel de conexión de TableAdapter tendrá que ser accesible o personalizable para consultas, usuarios o páginas ASP.NET.

Extendamos ProductsTableAdapter en el conjunto de datos Northwind para incluir una propiedad ConnectionString que pueda usar la capa lógica de negocios para leer o cambiar la cadena de conexión usada por TableAdapter.

Nota:

Una cadena de conexión es una cadena que especifica información de conexión de base de datos, como el proveedor que se usará, la ubicación de la base de datos, las credenciales de autenticación y otras configuraciones relacionadas con la base de datos. Para obtener una lista de patrones de cadena de conexión usados por una variedad de almacenes de datos y proveedores, consulte ConnectionStrings.com.

Como se describe en el tutorial Creación de capas de acceso a datos, las clases generadas automáticamente del conjunto de datos con tipo se pueden ampliar mediante el uso de clases parciales. En primer lugar, cree una subcarpeta en el proyecto denominada ConnectionAndCommandSettings debajo de la carpeta ~/App_Code/DAL.

Add a Subfolder Named ConnectionAndCommandSettings

Figura 3: agregar una subcarpeta denominada ConnectionAndCommandSettings

Agregue un nuevo archivo de clase denominado ProductsTableAdapter.ConnectionAndCommandSettings.vb y escriba el código siguiente:

Namespace NorthwindTableAdapters
    Partial Public Class ProductsTableAdapter
        Public Property ConnectionString() As String
            Get
                Return Me.Connection.ConnectionString
            End Get
            Set(ByVal value As String)
                Me.Connection.ConnectionString = value
            End Set
        End Property
    End Class
End Namespace

Esta clase parcial agrega una propiedad Public denominada ConnectionString a la clase ProductsTableAdapter que permite que cualquier capa lea o actualice la cadena de conexión de la conexión subyacente de TableAdapter.

Con esta clase parcial creada (y guardada), abra la clase ProductsBLL. Vaya a uno de los métodos existentes y escriba Adapter y, a continuación, presione la clave de punto para abrir IntelliSense. Debería ver la nueva propiedad ConnectionString disponible en IntelliSense, lo que significa que es posible leer o ajustar mediante programación este valor de BLL.

Exposición del objeto de conexión completo

Esta clase parcial expone solo una propiedad del objeto de conexión subyacente: ConnectionString. Si quisiera que todo el objeto de conexión esté disponible más allá de los límites de TableAdapter, también podría cambiar el nivel de protección de la propiedad Connection. El código generado automáticamente que se examinó en el paso 1 mostró que la propiedad Connection de TableAdapter está marcada como Friend, lo que significa que solo las clases del mismo ensamblado pueden tener acceso a ella. Sin embargo, esto se puede cambiar a través de la propiedad ConnectionModifier de TableAdapter.

Abra el conjunto de datos Northwind, haga clic en ProductsTableAdapter, en el diseñador, y vaya a la ventana Propiedades. Allí verá el ConnectionModifier establecido en su valor predeterminado, Assembly. Para que la propiedad Connection esté disponible fuera del ensamblado del conjunto de datos con tipo, cambie la propiedad ConnectionModifier a Public.

The Connection Property s Accessibility Level Can Be Configured via the ConnectionModifier Property

Figura 4: el nivel de accesibilidad de la propiedad Connection se puede configurar a través de la propiedad ConnectionModifier (Haga clic para ver la imagen a tamaño completo)

Guarde el conjunto de datos y vuelva a la clase ProductsBLL. Como antes, vaya a uno de los métodos existentes y escriba Adapter y, a continuación, presione la clave de punto para abrir IntelliSense. La lista debe incluir una propiedad Connection, lo que significa que ya es posible leer o asignar mediante programación cualquier configuración de nivel de conexión de BLL.

Un TableAdapter consta de una consulta principal que, de forma predeterminada, tiene instrucciones INSERT, UPDATE y DELETE generadas automáticamente. Estas instrucciones INSERT, UPDATE y DELETE de la consulta principal se implementan en el código de TableAdapter como un objeto de adaptador de datos de ADO.NET a través de la propiedad Adapter. Al igual que con su propiedad Connection, el proveedor de datos utilizado determina el tipo de datos de la propiedad Adapter. Dado que estos tutoriales usan el proveedor SqlClient, la propiedad Adapter es de tipo SqlDataAdapter.

La propiedad Adapter de TableAdapter tiene tres propiedades de tipo SqlCommand que usa para emitir las instrucciones INSERT, UPDATE y DELETE:

  • InsertCommand
  • UpdateCommand
  • DeleteCommand

Un objeto SqlCommand es responsable de enviar consultas determinadas a la base de datos y tiene propiedades como: CommandText, que contiene la instrucción SQL ad hoc o el procedimiento almacenado que se vaya a ejecutar; y Parameters, que es una colección de objetos SqlParameter. Como vimos en el tutorial Creación de una capa de acceso a datos, estos objetos de comando se pueden personalizar a través de la ventana Propiedades.

Además de su consulta principal, TableAdapter puede incluir un número variable de métodos que, cuando se invoquen, enviarán un comando especificado a la base de datos. El objeto de comando de la consulta principal y los objetos de comando para todos los métodos adicionales se almacenan en la propiedad CommandCollection de TableAdapter.

Dediquemos un momento para ver el código generado por ProductsTableAdapter en el conjunto de datos Northwind para estas dos propiedades y sus variables de miembro auxiliares y métodos asistentes:

Private WithEvents _adapter As System.Data.SqlClient.SqlDataAdapter
Private Sub InitAdapter()
    Me._adapter = New System.Data.SqlClient.SqlDataAdapter
    
    ... Code that creates the InsertCommand, UpdateCommand, ...
    ... and DeleteCommand instances - omitted for brevity ...
End Sub
Private ReadOnly Property Adapter() As System.Data.SqlClient.SqlDataAdapter
    Get
        If (Me._adapter Is Nothing) Then
            Me.InitAdapter
        End If
        Return Me._adapter
    End Get
End Property
Private _commandCollection() As System.Data.SqlClient.SqlCommand
Private Sub InitCommandCollection()
    Me._commandCollection = New System.Data.SqlClient.SqlCommand(8) {}
    ... Code that creates the command objects for the main query and the ...
    ... ProductsTableAdapter�s other eight methods - omitted for brevity ...
End Sub
Protected ReadOnly Property CommandCollection() As System.Data.SqlClient.SqlCommand()
    Get
        If (Me._commandCollection Is Nothing) Then
            Me.InitCommandCollection
        End If
        Return Me._commandCollection
    End Get
End Property

El código de las propiedades Adapter y CommandCollection imita estrechamente el de la propiedad Connection. Hay variables de miembro que contienen los objetos usados por las propiedades. Los descriptores de acceso Get de las propiedades comienzan comprobando que la variable miembro correspondiente sea Nothing. Si fuera así, se llamará a un método de inicialización que creará una instancia de la variable miembro y asignará las propiedades principales relacionadas con el comando.

Paso 4: exponer la configuración de nivel de comando

Idealmente, la información de nivel de comando debería permanecer encapsulada dentro de la capa de acceso a datos. Sin embargo, si esta información se necesitase en otras capas de la arquitectura, se podrá exponer a través de una clase parcial, al igual que con la configuración de nivel de conexión.

Dado que TableAdapter solo tiene una sola propiedad Connection, el código para exponer la configuración de nivel de conexión resulta bastante sencillo. Las cosas se complican un poco más al modificar la configuración de nivel de comando porque TableAdapter puede tener varios objetos de comando: InsertCommand, UpdateCommand y DeleteCommand, junto con un número variable de objetos de comando en la propiedad CommandCollection. Al actualizar la configuración de nivel de comando, esta configuración deberá propagarse a todos los objetos de comando.

Por ejemplo, imagine que había ciertas consultas en TableAdapter que tardaron de forma poco usual mucho tiempo en ejecutarse. Al usar TableAdapter para ejecutar una de esas consultas, es posible que deseemos aumentar la propiedad CommandTimeout del objeto de comando. Esta propiedad especifica el número de segundos que se deben esperar hasta que el comando se ejecute y su valor predeterminado es 30.

Para permitir que el BLL ajuste la propiedad CommandTimeout, agregue el siguiente método Public a ProductsDataTable mediante el uso del archivo de clase parcial creado en el paso 2 (ProductsTableAdapter.ConnectionAndCommandSettings.vb):

Public Sub SetCommandTimeout(ByVal timeout As Integer)
    If Me.Adapter.InsertCommand IsNot Nothing Then
        Me.Adapter.InsertCommand.CommandTimeout = timeout
    End If
    If Me.Adapter.DeleteCommand IsNot Nothing Then
        Me.Adapter.DeleteCommand.CommandTimeout = timeout
    End If
    If Me.Adapter.UpdateCommand IsNot Nothing Then
        Me.Adapter.UpdateCommand.CommandTimeout = timeout
    End If
    For i As Integer = 0 To Me.CommandCollection.Length - 1
        If Me.CommandCollection(i) IsNot Nothing Then
            Me.CommandCollection(i).CommandTimeout = timeout
        End If
    Next
End Sub

Este método se puede invocar desde el BLL o la capa de presentación para que la instancia de TableAdapter establezca el tiempo de espera del comando para todas las incidencias de comandos.

Nota:

Las propiedades Adapter y CommandCollection se marcan como Private, lo que significa que solo se puede tener acceso a ellas desde el código dentro de TableAdapter. A diferencia de la propiedad Connection, estos modificadores de acceso no son configurables. Por lo tanto, si fuera necesario exponer propiedades de nivel de comando a otras capas de la arquitectura, deberá usar el enfoque de clase parcial descrito anteriormente para proporcionar un método o propiedad Public que lea o escriba en los objetos de comando Private.

Resumen

TableAdapters dentro de un conjunto de datos con tipo sirve para encapsular la complejidad y los detalles del acceso a datos. Con TableAdapters, no hay que preocuparse de escribir código de ADO.NET para conectarse a la base de datos, emitir un comando o rellenar los resultados en una DataTable. Todo se controla automáticamente para nosotros.

Sin embargo, podría haber ocasiones en las que fuera necesario personalizar los detalles de ADO.NET de bajo nivel, como cambiar la cadena de conexión o los valores de tiempo de espera de conexión o comando predeterminados. TableAdapter tiene propiedades Connection, Adapter y CommandCollection generadas automáticamente, pero son Friend o Private de forma predeterminada. Esta información interna se puede exponer mediante la extensión de TableAdapter mediante clases parciales para incluir métodos o propiedades Public. Como alternativa, el modificador de acceso a la propiedad Connection de TableAdapter se puede configurar a través de la propiedad ConnectionModifier de TableAdapter.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha trabajado con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él a través de mitchell@4GuysFromRolla.com. o de su blog, que se puede encontrar en http://ScottOnWriting.NET.

Agradecimientos especiales a

Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Burnadette Leigh, S ren Jacob Lauritsen, Teresa Murphy y Hilton Geisenow. ¿Le interesaría revisar mis próximos artículos de MSDN? Si fuera así, escríbame a mitchell@4GuysFromRolla.com.