Compartir a través de


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

de Scott Mitchell

Descargar PDF

Los TableAdapters dentro de un DataSet tipado se encargan automáticamente de conectarse a la base de datos, emitir comandos y rellenar una tabla de datos con los resultados. Sin embargo, hay ocasiones en las que queremos cuidar estos detalles y, en este tutorial, aprendemos a acceder a la configuración de nivel de comando y conexión de la base de datos en TableAdapter.

Introducción

A lo largo de la serie de tutoriales, hemos usado DataSets tipados para implementar la capa de acceso a datos y los objetos de negocio de nuestra arquitectura en capas. Como se describe en el primer tutorial, las DataTables del DataSet tipado actúan como repositorios de datos, mientras que los TableAdapters actúan como envoltorios para comunicarse con la base de datos y recuperar y modificar los datos subyacentes. TableAdapters encapsula la complejidad implicada en el trabajo con la base de datos y nos evita tener que escribir código para conectarse a la base de datos, emitir un comando o rellenar los resultados en una DataTable.

Sin embargo, hay ocasiones en las que es necesario ensarcar en las profundidades del TableAdapter y escribir código que funciona directamente con los objetos ADO.NET. En el tutorial Incorporación de Modificaciones de Base de Datos Dentro de una Transacción, por ejemplo, agregamos métodos a TableAdapter para comenzar, confirmar y revertir transacciones ADO.NET. Estos métodos usaron un objeto interno creado manualmente SqlTransaction que se asignó a los objetos TableAdapter SqlCommand.

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, agregaremos funcionalidad a ProductsTableAdapter para que permita el acceso a la cadena de conexión subyacente y a la configuración de límite de tiempo para el comando.

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 las clases ADO.NET . Algunas de las clases de ADO.NET están vinculadas a un proveedor de datos determinado. Puede pensar 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 un sistema de base de datos determinado. Por ejemplo, aunque es posible conectarse a una base de datos de Microsoft SQL Server mediante el proveedor OleDb, el proveedor SqlClient es mucho más eficaz, ya que se diseñó y optimicó específicamente para SQL Server.

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

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

Hay clases 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 INSERTcomando , UPDATE, DELETEo SELECT a la base de datos, use la SqlCommand clase .

Excepto para el tutorial Modificar bases de datos dentro de una transacción, no hemos tenido que escribir ningún código de ADO.NET de bajo nivel porque el código generado automáticamente por los TableAdapters incluye la funcionalidad necesaria para conectarse a la base de datos, emitir comandos, recuperar datos y rellenar esos datos en DataTables. Sin embargo, puede haber ocasiones en las que sea necesario personalizar esta configuración de bajo nivel. En los siguientes pasos examinaremos cómo acceder a los objetos ADO.NET utilizados internamente por los TableAdapters.

Paso 1: Examinar con la propiedad de conexión

Cada clase TableAdapter tiene una Connection propiedad que especifica información de conexión de base de datos. El tipo de datos de esta propiedad y su valor ConnectionString vienen determinados por las selecciones realizadas en el asistente de configuración de TableAdapter. Recuerde que cuando por primera vez agregamos un TableAdapter a un DataSet tipado, este asistente nos 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 en las conexiones de datos del Explorador de servidores. Si la base de datos que queremos usar no existe en la lista desplegable, se puede especificar 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.

Primer paso del Asistente para configuración de TableAdapter

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

Dediquemos un momento a inspeccionar el código de la propiedad TableAdapter s Connection . Como se indicó en el tutorial Creación de una capa de acceso a datos , podemos 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 escribiendo Ctrl+Mayús+C). En la mitad superior de la ventana Vista de clases, navegue hasta el NorthwindTableAdapters espacio de nombres y seleccione la clase ProductsTableAdapter. Esto mostrará los miembros de ProductsTableAdapter en la mitad inferior de la vista de clase, como se muestra en la Figura 2. Haga doble clic en la Connection propiedad para ver su código.

Double-Click la Connection Property en el Class View para ver su código generado automáticamente

Figura 2: Double-Click la propiedad de conexión en la vista de clases para ver su código generado automáticamente

Las propiedades de TableAdapter Connection y otro código relacionado con la conexión se muestran a continuación.

private System.Data.SqlClient.SqlConnection _connection;
private void InitConnection() {
    this._connection = new System.Data.SqlClient.SqlConnection();
    this._connection.ConnectionString = 
        ConfigurationManager.ConnectionStrings["NORTHWNDConnectionString"].ConnectionString;
}
internal System.Data.SqlClient.SqlConnection Connection {
    get {
        if ((this._connection == null)) {
            this.InitConnection();
        }
        return this._connection;
    }
    set {
        this._connection = value;
        if ((this.Adapter.InsertCommand != null)) {
            this.Adapter.InsertCommand.Connection = value;
        }
        if ((this.Adapter.DeleteCommand != null)) {
            this.Adapter.DeleteCommand.Connection = value;
        }
        if ((this.Adapter.UpdateCommand != null)) {
            this.Adapter.UpdateCommand.Connection = value;
        }
        for (int i = 0; (i < this.CommandCollection.Length); i = (i + 1)) {
            if ((this.CommandCollection[i] != null)) {
                ((System.Data.SqlClient.SqlCommand)
                    (this.CommandCollection[i])).Connection = value;
            }
        }
    }
}

Cuando se crea una instancia de la clase TableAdapter, la variable _connection miembro es igual a null. Cuando se accede a la Connection propiedad, primero comprueba si se ha creado una instancia de la _connection variable de miembro. Si no es así, se invoca el método InitConnection, que instancia _connection y establece su propiedad ConnectionString en el valor de la cadena de conexión especificado en el primer paso del asistente de configuración de TableAdapter.

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

Paso 2: Exponer la configuración de Connection-Level

La información de conexión debe permanecer encapsulada dentro de TableAdapter y no ser accesible para otras capas de la arquitectura de la aplicación. Sin embargo, puede haber escenarios en los que la información de nivel de conexión del TableAdapter deba ser accesible o personalizable para una consulta, usuario o página de ASP.NET.

Vamos a ampliar ProductsTableAdapter en el Northwind conjunto de datos para incluir una ConnectionString propiedad que pueda usar la capa lógica de negocios para leer o cambiar la cadena de conexión usada por el 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 va a 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 los 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 una capa de acceso a datos , las clases generadas automáticamente de Typed DataSet se pueden ampliar mediante el uso de clases parciales. En primer lugar, cree una subcarpeta en el proyecto denominado ConnectionAndCommandSettings debajo de la ~/App_Code/DAL carpeta.

Agregar una subcarpeta denominada ConnectionAndCommandSettings

Figura 3: Agregar una subcarpeta denominada ConnectionAndCommandSettings

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

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
    public partial class ProductsTableAdapter
    {
        public string ConnectionString
        {
            get
            {
                return this.Connection.ConnectionString;
            }
            set
            {
                this.Connection.ConnectionString = value;
            }
        }
    }
}

Esta clase parcial agrega una public propiedad denominada ConnectionString a la ProductsTableAdapter clase 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 ProductsBLL clase . Vaya a uno de los métodos existentes y escriba Adapter, luego presione la tecla de punto para mostrar IntelliSense. Debería ver la nueva ConnectionString propiedad disponible en IntelliSense, lo que significa que puede leer o ajustar este valor desde el BLL mediante programación.

Exposición del objeto de conexión completo

Esta clase parcial expone solo una propiedad del objeto de conexión subyacente: ConnectionString. Si desea que todo el objeto de conexión esté disponible más allá de los límites de TableAdapter, también puede cambiar el nivel de protección de la Connection propiedad. El código generado automáticamente que hemos examinado en el paso 1 mostró que la propiedad TableAdapter s Connection está marcada como internal, 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 TableAdapter s ConnectionModifier .

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

El nivel de accesibilidad de la propiedad Connection se puede configurar a través de la propiedad ConnectionModifier

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

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

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

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

  • InsertCommand
  • UpdateCommand
  • DeleteCommand

Un SqlCommand objeto es responsable de enviar una consulta determinada a la base de datos y tiene propiedades como : CommandText, que contiene la instrucción SQL ad hoc o el procedimiento almacenado que se va a ejecutar; y Parameters, que es una colección de SqlParameter objetos . 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 invocan, envían 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 del TableAdapter.

Echemos un momento a ver el código generado por ProductsTableAdapter en dataSet Northwind para estas dos propiedades y sus variables de miembro auxiliares y métodos auxiliares:

private System.Data.SqlClient.SqlDataAdapter _adapter;
private void InitAdapter() {
    this._adapter = new System.Data.SqlClient.SqlDataAdapter();
    
    ... Code that creates the InsertCommand, UpdateCommand, ...
    ... and DeleteCommand instances - omitted for brevity ...
}
private System.Data.SqlClient.SqlDataAdapter Adapter {
    get {
        if ((this._adapter == null)) {
            this.InitAdapter();
        }
        return this._adapter;
    }
}
private System.Data.SqlClient.SqlCommand[] _commandCollection;
private void InitCommandCollection() {
    this._commandCollection = new System.Data.SqlClient.SqlCommand[9];
    ... Code that creates the command objects for the main query and the ...
    ... ProductsTableAdapter�s other eight methods - omitted for brevity ...
}
protected System.Data.SqlClient.SqlCommand[] CommandCollection {
    get {
        if ((this._commandCollection == null)) {
            this.InitCommandCollection();
        }
        return this._commandCollection;
    }
}

El código de las Adapter propiedades y CommandCollection imita estrechamente el de la Connection propiedad . Hay variables de miembro que contienen los objetos usados por las propiedades. Los descriptores de acceso de propiedades get comienzan comprobando si la variable miembro correspondiente es null. Si es así, se llama a un método de inicialización que crea una instancia de la variable miembro y asigna las propiedades principales relacionadas con el comando.

Paso 4: Exponer la configuración de Command-Level

Idealmente, la información de nivel de comando debe permanecer encapsulada dentro de la capa de acceso a datos. Sin embargo, si esta información se necesita en otras capas de la arquitectura, se puede 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 Connection propiedad, el código para exponer la configuración de nivel de conexión es bastante sencillo. Las cosas son un poco más complicadas al modificar la configuración de nivel de comando porque TableAdapter puede tener varios objetos de comando: , InsertCommandUpdateCommandy DeleteCommand, junto con un número variable de objetos de comando en la CommandCollection propiedad . 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 el TableAdapter que tardaban un tiempo extraordinariamente largo en ejecutarse. Al utilizar el TableAdapter para ejecutar una de esas consultas, es posible que deseemos aumentar una propiedad del objeto de comando. Esta propiedad especifica el número de segundos que se deben esperar a que el comando se ejecute y el valor predeterminado sea 30.

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

public void SetCommandTimeout(int timeout)
{
    if (this.Adapter.InsertCommand != null)
        this.Adapter.InsertCommand.CommandTimeout = timeout;
    if (this.Adapter.DeleteCommand != null)
        this.Adapter.DeleteCommand.CommandTimeout = timeout;
    if (this.Adapter.UpdateCommand != null)
        this.Adapter.UpdateCommand.CommandTimeout = timeout;
    for (int i = 0; i < this.CommandCollection.Length; i++)
        if (this.CommandCollection[i] != null)
            this.CommandCollection[i].CommandTimeout = timeout;
}

Este método se puede invocar desde el BLL o la capa de presentación para establecer el tiempo de espera del comando para todos los comandos emitidos por esa instancia de TableAdapter.

Nota:

Las Adapter y CommandCollection propiedades están marcadas como private, lo que significa que solo pueden ser accedidas por código dentro del TableAdapter. A diferencia de la Connection propiedad , estos modificadores de acceso no son configurables. Por lo tanto, si necesita exponer propiedades a nivel de comando a otras capas de la arquitectura, debe utilizar el enfoque de clase parcial descrito anteriormente para proporcionar un método o una propiedad public que lea o escriba en los objetos de comando private.

Resumen

Los TableAdapters dentro de un DataSet tipado sirven para encapsular los detalles y la complejidad del acceso a datos. Con TableAdapters, no tenemos que preocuparnos de escribir ADO.NET código 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, puede haber ocasiones en las que sea necesario personalizar los ADO.NET de bajo nivel específicos, como cambiar la cadena de conexión o los valores predeterminados de tiempo de espera de la conexión o del comando. TableAdapter tiene las propiedades Connection, Adapter y CommandCollection generadas automáticamente, pero estas son internal o private de forma predeterminada. Esta información interna se puede exponer al ampliar el TableAdapter mediante clases parciales para incluir métodos o propiedades public. Como alternativa, el modificador de acceso de la propiedad del "TableAdapter" Connection puede configurarse a través de la propiedad del "TableAdapter" ConnectionModifier.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando 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 en 24 horas. Se puede contactar con él en mitchell@4GuysFromRolla.com.

Agradecimientos especiales a

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