Vincular objetos como orígenes de datos en aplicaciones .NET Framework mediante Visual Studio

Nota:

Los conjuntos de datos y las clases relacionadas son tecnologías heredadas de .NET Framework de principios de la década de 2000 que permiten a las aplicaciones trabajar con datos en memoria mientras están desconectadas de la base de datos. Son especialmente útiles para las aplicaciones que permiten a los usuarios modificar los datos y conservar los cambios en la base de datos. Aunque los conjuntos de datos han demostrado ser una tecnología de gran éxito, se recomienda que las nuevas aplicaciones de .NET usen Entity Framework Core. Entity Framework proporciona una manera más natural de trabajar con datos tabulares como modelos de objetos y tiene una interfaz de programación más sencilla.

Visual Studio proporciona herramientas en tiempo de diseño para trabajar con objetos personalizados como origen de datos en la aplicación. Cuando quiera almacenar datos de una base de datos en un objeto que enlace a controles de interfaz de usuario, el enfoque recomendado es usar Entity Framework para generar la clase, o las clases. Entity Framework genera automáticamente todo el código reutilizable de seguimiento de cambios, lo que significa que los cambios que se realicen en los objetos locales se conservarán automáticamente en la base de datos al llamar a AcceptChanges en el objeto DbSet. Para más información, consulte la documentación de Entity Framework.

Sugerencia

Los métodos para el enlace de objetos que se indican en este artículo solo se deben tener en cuenta si la aplicación ya está basada en conjuntos de datos. Estos métodos también pueden utilizarse si ya se está familiarizado con los conjuntos de datos y los datos que se van a procesar son tabulares y no demasiado complejos o demasiado grandes. Para ver un ejemplo aún más sencillo, que implica cargar datos directamente en objetos mediante DataReader y actualizar manualmente la interfaz de usuario sin enlace de datos, consulte Creación de una aplicación de datos sencilla mediante ADO.NET.

Requisitos de objetos

El único requisito para que los objetos personalizados funcionen con las herramientas de diseño de datos en Visual Studio es que el objeto necesita tener al menos una propiedad pública.

Por lo general, los objetos personalizados no requieren interfaces, constructores ni atributos específicos para actuar como origen de datos en una aplicación. Sin embargo, si desea arrastrar el objeto desde la ventana Orígenes de datos a una superficie de diseño para crear un control enlazado a datos y si el objeto implementa la interfaz ITypedList o IListSource, el objeto debe tener un constructor predeterminado. De lo contrario, Visual Studio no puede crear instancias del objeto de origen de datos y muestra un error al arrastrar el elemento a la superficie de diseño.

Ejemplos de uso de objetos personalizados como orígenes de datos

Aunque hay innumerables maneras de implementar la lógica de la aplicación al trabajar con objetos como origen de datos, en el caso de las bases de datos SQL hay algunas operaciones estándar que se pueden simplificar mediante el uso de objetos TableAdapter generados por Visual Studio. En esta página se explica cómo implementar estos procesos estándar mediante objetos TableAdapter. No está pensado como guía para crear objetos personalizados. Por ejemplo, normalmente realizará las siguientes operaciones estándar, independientemente de la implementación específica de los objetos o de la lógica de la aplicación:

  • Cargar datos en objetos (normalmente desde una base de datos).

  • Crear una colección tipada de objetos.

  • Agregar objetos a una colección y quitarlos de ella.

  • Mostrar los datos de los objetos a los usuarios en un formulario.

  • Cambiar o editar los datos de los objetos.

  • Volver a guardar los datos de los objetos en la base de datos.

Carga de datos en objetos

En este ejemplo, se usan objetos TableAdapter para cargar datos en los objetos. De forma predeterminada, los objetos TableAdapter se crea con dos tipos de métodos que capturan datos de una base de datos y rellenan tablas de datos.

  • El método TableAdapter.Fill rellena una tabla de datos existente con los datos devueltos.

  • El método TableAdapter.GetData devuelve una nueva tabla de datos rellenada con datos.

La manera más fácil de cargar los objetos personalizados con datos es llamar al método TableAdapter.GetData, recorrer en bucle la colección de filas de la tabla de datos devuelta y rellenar cada objeto con los valores de cada fila. Puede crear un método GetData que devuelva una tabla de datos rellenada para cualquier consulta agregada a un objeto TableAdapter.

Nota

Visual Studio asigna nombres a las consultas de TableAdapter Fill y GetData de forma predeterminada, pero puede cambiar esos nombres por cualquier nombre de método válido.

En el ejemplo siguiente se muestra cómo recorrer en bucle las filas de una tabla de datos y rellenar un objeto con datos:

private void LoadCustomers()
{
    NorthwindDataSet.CustomersDataTable customerData = 
        customersTableAdapter1.GetTop5Customers();
    
    foreach (NorthwindDataSet.CustomersRow customerRow in customerData)
    {
        Customer currentCustomer = new Customer();
        currentCustomer.CustomerID = customerRow.CustomerID;
        currentCustomer.CompanyName = customerRow.CompanyName;

        if (customerRow.IsAddressNull() == false)
        {
            currentCustomer.Address = customerRow.Address;
        }

        if (customerRow.IsCityNull() == false)
        {
            currentCustomer.City = customerRow.City;
        }

        if (customerRow.IsContactNameNull() == false)
        {
            currentCustomer.ContactName = customerRow.ContactName;
        }

        if (customerRow.IsContactTitleNull() == false)
        {
            currentCustomer.ContactTitle = customerRow.ContactTitle;
        }

        if (customerRow.IsCountryNull() == false)
        {
            currentCustomer.Country = customerRow.Country;
        }

        if (customerRow.IsFaxNull() == false)
        {
            currentCustomer.Fax = customerRow.Fax;
        }

        if (customerRow.IsPhoneNull() == false)
        {
            currentCustomer.Phone = customerRow.Phone;
        }

        if (customerRow.IsPostalCodeNull() == false)
        {
            currentCustomer.PostalCode = customerRow.PostalCode;
        }

        if (customerRow.IsRegionNull() == false)
        {
            currentCustomer.Region = customerRow.Region;
        }

        LoadOrders(currentCustomer);
        customerBindingSource.Add(currentCustomer);
    }
}

Creación de una colección tipada de objetos

Puede crear clases de colección para los objetos o usar las colecciones con tipo que el componente BindingSource proporciona automáticamente.

Al crear una clase de colección personalizada para objetos, se recomienda heredarla de BindingList<T>. Esta clase genérica proporciona la funcionalidad necesaria para administrar la colección, así como la capacidad de generar eventos que envían notificaciones a la infraestructura de enlace de datos en Windows Forms.

La colección generada automáticamente en BindingSource utiliza BindingList<T> para su colección con tipo. Si la aplicación no requiere ninguna funcionalidad adicional, puede mantener la colección dentro de BindingSource. Para más información, consulte la propiedad List de la clase BindingSource.

Nota

Si la colección requiere funcionalidad no proporcionada por la implementación base de BindingList<T>, debe crear una colección personalizada para poder agregarla a la clase según sea necesario.

En el código siguiente se muestra cómo crear la clase para una colección fuertemente tipada de objetos Order:

/// <summary>
/// A collection of Orders
/// </summary>
public class Orders: System.ComponentModel.BindingList<Order>
{
    // Add any additional functionality required by your collection.
}

Adición de objetos a una colección

Para agregar objetos a una colección es preciso llamar al método Add de la clase de colección personalizada o de BindingSource.

Nota

El método Add se proporciona automáticamente para la colección personalizada cuando se hereda de BindingList<T>.

En el código siguiente se muestra cómo agregar objetos a la colección con tipo en BindingSource:

Customer currentCustomer = new Customer();
customerBindingSource.Add(currentCustomer);

En el código siguiente se muestra cómo agregar objetos a un colección con tipo que hereda de BindingList<T>:

Nota

Por ejemplo, la colección Orders es una propiedad del objeto Customer.

Order currentOrder = new Order();
currentCustomer.Orders.Add(currentOrder);

Eliminación de objetos de una colección

Para quitar objetos de una colección es preciso llamar a los métodos Remove o RemoveAt de la clase de colección personalizada o de BindingSource.

Nota

Los métodos Remove y RemoveAt se proporcionan automáticamente para una colección personalizada cuando esta se hereda de BindingList<T>.

En el código siguiente se muestra cómo buscar y quitar objetos de la colección con tipo en BindingSource con el método RemoveAt:

int customerIndex = customerBindingSource.Find("CustomerID", "ALFKI");
customerBindingSource.RemoveAt(customerIndex);

Presentación de datos de objeto a los usuarios

Para mostrar los datos de los objetos a los usuarios se utiliza el Asistente para configuración del origen de datos para crear un origen de datos de objeto y, después, se arrastra todo el objeto o propiedades individuales al formulario desde la ventana Orígenes de datos.

Modificación de los datos de los objetos

Para editar los datos de objetos personalizados enlazados a datos de controles de Windows Forms, solo hay que editar los datos del control enlazado (o directamente en las propiedades del objeto). La arquitectura de enlace de datos actualiza los datos del objeto.

Si la aplicación requiere el seguimiento de los cambios y la reversión de los cambios propuestos a sus valores originales, esta funcionalidad se debe implementar en el modelo de objetos. Para obtener ejemplos de la forma en que las tablas de datos realizan un seguimiento de los cambios propuestos, consulte DataRowState, HasChangesy GetChanges.

Almacenamiento de objetos en la base de datos

Para volver a guardar los datos en la base de datos pase los valores del objeto a los métodos DBDirect de TableAdapter.

Visual Studio crea métodos DBDirect que se pueden ejecutar directamente en la base de datos. Estos métodos no requieren objetos DataSet ni DataTable.

Método de TableAdapter DBDirect Descripción
TableAdapter.Insert Agrega nuevos registros a una base de datos, lo que le permite usar valores de columna individuales como parámetros de método.
TableAdapter.Update Actualiza los registros existentes en una base de datos. El método Update utiliza los valores de columna originales y nuevos como parámetros. Los valores originales se usan para localizar el registro original, mientras que los nuevos se usan para actualizar ese registro.

El método TableAdapter.Update también se usa para conciliar los cambios de un conjunto de datos de nuevo en la base de datos, para lo que se toman DataSet, DataTable, DataRow, o una matriz de DataRow como parámetros.
TableAdapter.Delete Elimina los registros existentes de la base de datos en función de los valores de columna originales usados como parámetros del método.

Para guardar datos de una colección de objetos, recorra en bucle la colección de objetos (por ejemplo, mediante un bucle for-next). Envíe los valores de cada objeto a la base de datos mediante los métodos DBDirect de TableAdapter.

En el ejemplo siguiente se muestra cómo usar el método DBDirect TableAdapter.Insert para agregar un nuevo cliente directamente a la base de datos:

private void AddNewCustomers(Customer currentCustomer)
{
    customersTableAdapter.Insert( 
        currentCustomer.CustomerID, 
        currentCustomer.CompanyName, 
        currentCustomer.ContactName, 
        currentCustomer.ContactTitle, 
        currentCustomer.Address, 
        currentCustomer.City, 
        currentCustomer.Region, 
        currentCustomer.PostalCode, 
        currentCustomer.Country, 
        currentCustomer.Phone, 
        currentCustomer.Fax);
}