Вы находитесь на странице: 1из 624

Contents

ADO.NET
Novedades de ADO.NET
Información general sobre ADO.NET
Arquitectura de ADO.NET
Directrices y opciones de tecnología de ADO.NET
LINQ y ADO.NET
Proveedores de datos .NET Framework
Objetos DataSet de ADO.NET
Ejecución en paralelo en ADO.NET
Ejemplos de código de ADO.NET
Proteger aplicaciones de ADO.NET
Información general sobre seguridad
Acceso seguro a datos
Aplicaciones cliente seguras
Seguridad de acceso del código y ADO.NET
Privacidad y seguridad de datos
Asignaciones de tipos de datos en ADO.NET
Asignaciones de tipos de datos de SQL Server
Asignaciones de tipos de datos de OLE DB
Asignaciones de tipos de datos de ODBC
Asignaciones de tipos de datos de Oracle
Números de punto flotante
Recuperar y modificar datos en ADO.NET
Conectarse a un origen de datos
Establecer la conexión
Eventos de Connection
Cadenas de conexión
Generadores de cadenas de conexión
Cadenas de conexión y archivos de configuración
Sintaxis de cadenas de conexión
Proteger la información de conexión
Agrupación de conexiones
Agrupación de conexiones de SQL Server (ADO.NET)
Agrupación de conexiones de OLE DB, ODBC y Oracle
Comandos y parámetros
Ejecutar un comando
Configurar parámetros y tipos de datos de parámetros
Generar comandos con objetos CommandBuilder
Obtener un valor único de una base de datos
Usar comandos para modificar datos
Actualizar datos de un origen de datos
Realizar operaciones de catálogo
Objetos DataAdapter y DataReader
Recuperar datos utilizando un objeto DataReader
Rellenar un conjunto de datos desde un objeto DataAdapter
Parámetros de DataAdapter
Agregar restricciones existentes a un conjunto de datos
Correspondencias de DataTable y DataColumn en un objeto DataAdapter
Paginar un resultado de consulta
Actualizar orígenes de datos con objetos DataAdapter
Controlar eventos de DataAdapter
Realizar operaciones por lotes utilizando objetos DataAdapter
Transacciones y simultaneidad
Transacciones locales
Transacciones distribuidas
Integración de System.Transactions con SQL Server
Simultaneidad optimista
Recuperar valores autonuméricos y de identidad
Recuperar datos binarios
Modificar datos con procedimientos almacenados
Recuperar información del esquema de la base de datos
GetSchema y colecciones de esquema
GetSchema y colecciones de esquema
Restricciones de esquema
Colecciones de esquemas comunes
Colecciones de esquemas de SQL Server
Colecciones de esquemas de Oracle
Colecciones de esquemas de ODBC
Colecciones de esquemas de OLE DB
Objetos DbProviderFactory
Información general sobre el modelo de fábrica
Obtener un objeto DbProviderFactory
DbConnection, DbCommand y DbException
Modificar datos con un objeto DbDataAdapter
Traza de datos en ADO.NET
Contadores de rendimiento
Programación asincrónica
Compatibilidad de transmisión de datos de SqlClient
LINQ to DataSet
Introducción
Información general de LINQ to DataSet
Cargar datos en un conjunto de datos
Descargar bases de datos de ejemplo
Procedimiento para crear un proyecto de LINQ to DataSet en Visual Studio
Guía de programación
Consultas en LINQ to DataSet
Consulta de conjuntos de datos
Consultas de tabla única
Consultas entre tablas
Consultar objetos DataSet con tipo
Comparación de objetos DataRow
Creación de un objeto DataTable a partir de una consulta
Procedimiento para implementar CopyToDataTable<T> cuando el tipo genérico T
no es un objeto DataRow
Métodos genéricos Field y SetField
Enlace de datos y LINQ to DataSet
Creación de un objeto DataView
Filtrado con DataView
Ordenación con DataView
Consultar la colección de DataRowView en un objeto DataView
Rendimiento de DataView
Procedimiento para enlazar un objeto DataView a un control DataGridView de
formularios Windows Forms
Depurar consultas de LINQ to DataSet
Seguridad
Ejemplos de LINQ to DataSet
Ejemplos de expresiones de consultas
Proyección
Restricción
Particionamiento
Ordenación
Operadores de elementos
Operadores de agregado
Operadores de combinación
Ejemplos de consultas basadas en métodos
Proyección
Particionamiento
Ordenación
Operadores de conjuntos
Operadores de conversión
Operadores de elementos
Operadores de agregado
Join
Ejemplos de operadores específicos de conjuntos de datos
Entity Data Model
Conceptos clave de Entity Data Model
Entity Data Model: Espacios de nombres
Entity Data Model: tipos de datos primitivos
Entity Data Model: Herencia
extremo de asociación
multiplicidad de extremo de asociación
conjunto de asociaciones
extremo del conjunto de asociaciones
tipo de asociación
tipo complejo
contenedor de entidades
clave de entidad
conjunto de entidades
tipo de entidad
facet
propiedad de clave externa
función declarada por el modelo
función definida por el modelo
propiedad de navegación
propiedad
restricción de integridad referencial
Oracle y ADO.NET
Requisitos del sistema
Objetos BFILE de Oracle
Objetos LOB de Oracle
Parámetros REF CURSOR de Oracle
Ejemplos de REF CURSOR
Parámetros REF CURSOR en un objeto OracleDataReader
Recuperar datos desde varios parámetros REF CURSOR utilizando un objeto
OracleDataReader
Rellenar un conjunto de datos utilizando uno o varios parámetros REF CURSOR
Tipos de Oracle
Secuencias de Oracle
Asignaciones de tipos de datos de Oracle
Transacciones distribuidas de Oracle
ADO.NET Entity Framework
SQL Server y ADO.NET
Objetos DataSet, DataTable y DataView
ADO.NET
10/02/2020 • 3 minutes to read • Edit Online

ADO.NET es un conjunto de clases que exponen servicios de acceso a datos para programadores de .NET
Framework. ADO.NET ofrece abundancia de componentes para la creación de aplicaciones de uso compartido de
datos distribuidas. Constituye una parte integral de .NET Framework y proporciona acceso a datos relacionales,
XML y de aplicaciones. ADO.NET satisface diversas necesidades de desarrollo, como la creación de clientes de base
de datos front-end y objetos empresariales de nivel medio que utilizan aplicaciones, herramientas, lenguajes o
exploradores de Internet.

En esta sección
Novedades de ADO.NET
Presenta características nuevas en ADO.NET.
Información general sobre ADO.NET
Proporciona una introducción al diseño y a los componentes de ADO.NET.
Entity Framework
Describe cómo crear aplicaciones con Entity Framework.
Proteger aplicaciones de ADO.NET
Describe algunas recomendaciones de codificación segura para utilizar ADO.NET.
Asignaciones de tipos de datos en ADO.NET
Describe las asignaciones de tipos de datos entre los tipos de datos de .NET Framework y los proveedores de datos
.NET Framework.
Objetos DataSet, DataTable y DataView
Describe cómo crear y usar DataSets , DataSets con establecimiento de tipos, DataTables y DataViews .
LINQ to DataSet
Proporciona información sobre LINQ to DataSet, incluidos ejemplos de programación.
Recuperar y modificar datos en ADO.NET
Describe cómo conectarse a un origen de datos y cómo recuperar y modificar datos mediante Commands ,
DataReaders y DataAdapters .

SQL Server y ADO.NET


Describe cómo trabajar con las características y la funcionalidad específicas de SQL Server.
Oracle y ADO.NET
Describe características y comportamientos específicos del proveedor de datos .NET Framework para Oracle.

Secciones relacionadas
Language Integrated Query (LINQ) (C#)
Proporciona vínculos a temas y ejemplos de LINQ C#mediante.
Language Integrated Query (LINQ) (Visual Basic)
Proporciona vínculos a temas y ejemplos de LINQ mediante Visual Basic.
Servicios de datos de WCF 4.5
Describe cómo usar WCF Data Services para implementar servicios de datos en Internet o en una intranet que
implementa el Open Data Protocol (OData).
Guía de desarrollo para .NET Framework
Proporciona vínculos a información acerca de las tareas de desarrollo estándar en .NET Framework.
Ejemplos y tutoriales
Proporciona una lista de ejemplos y tutoriales de .NET.

Consulte también
Obtener acceso a los datos en Visual Studio
Información general sobre ADO.NET
Novedades de ADO.NET
05/02/2020 • 5 minutes to read • Edit Online

Las siguientes características son nuevas en ADO.NET en el .NET Framework 4,5.

Proveedor de datos SqlClient


Las siguientes características son nuevas en el proveedor de datos de .NET Framework para SQL Server en .NET
Framework 4,5:
Las palabras clave de cadena de conexión ConnectRetryCount y ConnectRetryInterval (ConnectionString)
permiten controlar la característica de resistencia de conexión inactiva.
La compatibilidad con streaming desde SQL Server a una aplicación admite escenarios en los que los datos
del servidor no están estructurados. Consulte compatibilidad con la transmisión por secuencias de SqlClient
para obtener más información.
Se ha agregado compatibilidad con programación asincrónica. Vea programación asincrónica para obtener
más información.
Los errores de conexión se guardarán ahora en el registro de eventos extendidos. Para obtener más
información, consulte Traza de datos en ADO.NET.
SqlClient ahora tiene compatibilidad con la característica de recuperación ante desastres, AlwaysOn de SQL
Server. Para obtener más información, vea compatibilidad de SqlClient con la alta disponibilidad y la
recuperación ante desastres.
Se puede pasar una contraseña como SecureString al utilizar la autenticación de SQL Server. Vea
SqlCredential para obtener más información.
Cuando TrustServerCertificate es false y Encrypt es true, el nombre del servidor (o dirección IP) de un
certificado SSL de SQL Server debe coincidir exactamente con el nombre del servidor (o la dirección IP)
especificado en la cadena de conexión. De lo contrario, se producirá un error en el intento de conexión. Para
obtener más información, vea la descripción de la opción de conexión Encrypt en ConnectionString.
Si este cambio hace que una aplicación existente ya no pueda conectarse, puede corregir la aplicación
usando uno de los siguientes:
Emita un certificado que especifique el nombre corto en el campo Nombre común (CN) o Nombre
alternativo del sujeto (SAN). Esta solución funcionará para la creación de reflejo de la base de datos.
Agregue un alias que asigne el nombre corto al nombre de dominio completo.
Use el nombre de dominio completo en la cadena de conexión.
SqlClient admite Protección ampliada. Para obtener más información acerca de la protección ampliada,
consulte conexión a la motor de base de datos mediante la protección ampliada.
SqlClient admite conexiones a bases de datos LocalDB. Para obtener más información, vea compatibilidad
de SqlClient con LocalDB.
Type System Version=SQL Server 2012; es el nuevo valor para pasar a la propiedad de conexión
Type System Version . El valor Type System Version=Latest; ahora está obsoleto y se ha hecho equivalente a
Type System Version=SQL Server 2008; . Para obtener más información, vea ConnectionString.
SqlClient proporciona compatibilidad adicional para columnas dispersas, una característica que se agregó
en SQL Server 2008. Si su aplicación ya accede a los datos de una tabla que usa columnas dispersas, debe
observar un incremento del rendimiento. La columna IsColumnSet de GetSchemaTable indica si una
columna es una columna dispersa que es miembro de un conjunto de columnas. GetSchema indica si una
columna es una columna dispersa (vea SQL Server colecciones de esquemas para obtener más
información). Para obtener más información sobre las columnas dispersas, vea usar columnas dispersas.
El ensamblado Microsoft.SqlServer.Types.dll, que contiene los tipos de datos espaciales, se ha actualizado de
la versión 10.0 a la versión 11.0. Las aplicaciones que hacen referencia a este ensamblado pueden producir
errores. Para obtener más información, vea cambios importantes en las características de motor de base de
datos.

ADO.NET Entity Framework


El .NET Framework 4,5 agrega API que permiten nuevos escenarios cuando se trabaja con el Entity Framework 5,0.
Para obtener más información sobre las mejoras y características que se agregaron al Entity Framework 5,0, vea
los temas siguientes: novedades y Entity Framework versiones y control de versiones.

Vea también
ADO.NET
Información general sobre ADO.NET
SQL Server y ADO.NET
Novedades de WCF Data Services 5,0
Información general sobre ADO.NET
05/02/2020 • 5 minutes to read • Edit Online

ADO.NET proporciona acceso coherente a orígenes de datos como SQL Server y XML, así como a
orígenes de datos expuestos mediante OLE DB y ODBC. Las aplicaciones de consumidor que
comparten datos pueden utilizar ADO.NET para conectar a estos orígenes de datos y recuperar,
controlar y actualizar los datos contenidos.
ADO.NET separa el acceso a datos de la manipulación de datos y crea componentes discretos que se
pueden utilizar por separado o conjuntamente. ADO.NET incluye proveedores de datos .NET
Framework para conectarse a una base de datos, ejecutar comandos y recuperar resultados. Los
resultados se procesan directamente o se colocan en un objeto DataSet de ADO.NET con el fin de
exponerlos al usuario para un propósito específico, combinados con datos de varios orígenes, o de
pasarlos entre niveles. El objeto DataSet de ADO.NET también puede utilizarse independientemente de
un proveedor de datos .NET Framework para administrar datos que son locales de la aplicación o que
proceden de un origen XML.
Las clases de ADO.NET se encuentran en System.Data.dll y se integran con las clases de XML incluidas
en System.Xml.dll. Para ver el código de ejemplo que se conecta a una base de datos, recupera datos de
la misma y, a continuación, muestra esos datos en una ventana de la consola, consulte ejemplos de
código ADO.net.
ADO.NET proporciona funcionalidad a los desarrolladores que escriben código administrado similar a
la funcionalidad que los objetos ADO (ActiveX Data Objects) proporcionan a los desarrolladores de
modelo de objetos componentes (COM) nativo. Se recomienda utilizar ADO.NET, y no ADO, para
obtener acceso a datos de aplicaciones .NET.
ADO.NET proporciona el método más directo de acceso a datos de .NET Framework. Para obtener una
abstracción de nivel superior que permita que las aplicaciones funcionen con un modelo conceptual en
lugar del modelo de almacenamiento subyacente, vea el Entity Framework ADO.net.
Declaración de privacidad : los ensamblados System. Data. dll, System. Data. Design. dll, System.
Data. OracleClient. dll, System. Data. SqlXml. dll, System. Data. Linq. dll, System. Data. SqlServerCe. dll y
System. Data. DataSetExtensions. dll no distinguen entre los datos privados de un usuario y los datos
no privados. Estos ensamblados no recopilan, almacenan o transportan datos privados del usuario. No
obstante, las aplicaciones de terceros podrían recopilar, almacenar o transportar datos privados de
usuario valiéndose de dichos ensamblados.

Esta sección
Arquitectura de ADO.NET
Proporciona información general sobre la arquitectura y los componentes de ADO.NET.
Directrices y opciones de tecnología de ADO.NET
Describe los productos y tecnologías incluidos con la plataforma de datos de entidad.
LINQ y ADO.NET
Describe cómo se implementa Language-Integrated Query (LINQ) en ADO.NET y proporciona vínculos
a los temas pertinentes.
Proveedores de datos .NET Framework
Proporciona información general acerca del diseño del proveedor de datos .NET Framework y de los
proveedores de datos .NET Framework incluidos en ADO.NET.
Objetos DataSet de ADO.NET
Proporciona información general acerca del diseño y los componentes de DataSet .
Ejecución en paralelo en ADO.NET
Analiza las diferencias que existen entre las distintas versiones de ADO.NET y cómo afectan a la
ejecución en paralelo y a la compatibilidad entre aplicaciones.
Ejemplos de código de ADO.NET
Proporciona ejemplos de código que recuperan datos mediante proveedores de datos de ADO.NET.

Secciones relacionadas
Novedades de ADO.NET
Presenta características nuevas en ADO.NET.
Proteger aplicaciones de ADO.NET
Describe algunas recomendaciones de codificación segura para utilizar ADO.NET.
Asignaciones de tipos de datos en ADO.NET
Describe las asignaciones de tipos de datos entre los tipos de datos de .NET Framework y los
proveedores de datos .NET Framework.
Recuperar y modificar datos en ADO.NET
Describe cómo conectarse a un origen de datos, y cómo recuperar y modificar datos. Incluye
DataReaders y DataAdapters .

Vea también
ADO.NET
Obtener acceso a los datos en Visual Studio
Arquitectura de ADO.NET
05/02/2020 • 9 minutes to read • Edit Online

Tradicionalmente, el procesamiento de datos ha dependido principalmente de un modelo de dos niveles basado en


una conexión. A medida que aumenta el uso que hace el procesamiento de datos de arquitecturas de varios niveles,
los programadores están pasando a un enfoque sin conexión con el fin de proporcionar una mejor escalabilidad a
sus aplicaciones.

Componentes de ADO.NET
Los dos componentes principales de ADO.NET para tener acceso a los datos y manipularlos son los .NET
Framework proveedores de datos y el DataSet.
Proveedores de datos .NET Framework
Los proveedores de datos .NET Framework son componentes diseñados explícitamente para la manipulación de
datos y el acceso rápido a datos de solo lectura y solo avance. El objeto Connection proporciona conectividad a un
origen de datos. El objeto Command permite tener acceso a comandos de base de datos para devolver datos,
modificar datos, ejecutar procedimientos almacenados y enviar o recuperar información sobre parámetros.
DataReader proporciona un flujo de datos de alto rendimiento desde el origen de datos. Por último, el objeto
DataAdapter proporciona el puente entre el objeto DataSet y el origen de datos. DataAdapter utiliza objetos
Command para ejecutar comandos SQL en el origen de datos tanto para cargar DataSet con datos y reconciliar en
el origen de datos los cambios aplicados a los datos incluidos en el DataSet . Para obtener más información, vea
.NET Framework proveedores de datos y recuperar y modificar datos en ADO.net.
DataSet
DataSet de ADO.NET está expresamente diseñado para el acceso a datos independientemente del origen de datos.
Como resultado, se puede utilizar con múltiples y distintos orígenes de datos, con datos XML o para administrar
datos locales de la aplicación. DataSet contiene una colección de uno o más objetos DataTable formados por filas y
columnas de datos, así como información sobre claves principales, claves externas, restricciones y de relación
relacionada con los datos incluidos en los objetos DataTable . Para obtener más información, vea conjuntos de
datos , tablas de datos y vistasde datos.
En el diagrama siguiente se ilustra la relación entre un proveedor de datos de .NET Framework y un DataSet .

Arquitectura de ADO.NET
Elegir un DataReader o un DataSet
Cuando decida si la aplicación debe usar un DataReader (vea recuperación de datos mediante un DataReader) o un
DataSet (vea conjuntos de datos , DataTables y vistasde datos), tenga en cuenta el tipo de funcionalidad que
requiere la aplicación. Use un DataSet para hacer lo siguiente:
Almacene datos en la memoria caché de la aplicación para poder manipularlos. Si solamente necesita leer
los resultados de una consulta, el DataReader es la mejor elección.
Utilizar datos de forma remota entre un nivel y otro o desde un servicio Web XML.
Interactuar con datos dinámicamente, por ejemplo para enlazar con un control de Windows Forms o para
combinar y relacionar datos procedentes de varios orígenes.
Realizar procesamientos exhaustivos de datos sin necesidad de tener una conexión abierta con el origen de
datos, lo que libera la conexión para que la utilicen otros clientes.
Si no necesita la funcionalidad proporcionada por el DataSet , puede mejorar el rendimiento de su aplicación si
utiliza el DataReader para devolver sus datos de solo avance y de solo lectura. Aunque el DataAdapter usa el
DataReader para rellenar el contenido de un DataSet (vea rellenar un conjunto de resultados de un DataAdapter),
mediante el DataReader , puede mejorar el rendimiento porque ahorrará memoria que consumirá el DataSet y
evitará el procesamiento necesario para crear y rellenar el contenido de la DataSet .

LINQ to DataSet
LINQ to DataSet proporciona capacidades de consulta y comprobación de tipo en tiempo de compilación de los
datos almacenados en caché de un objeto DataSet. Permite escribir consultas en uno de los lenguajes de desarrollo
de .NET Framework, como C# o Visual Basic. Para más información, vea LINQ to DataSet.

LINQ to SQL
LINQ to SQL admite consultas en un modelo de objetos asignado a las estructuras de datos de una base de datos
relacional sin utilizar un modelo conceptual intermedio. Cada tabla se representa mediante una clase distinta,
acoplando de manera precisa el modelo de objetos al esquema de la base de datos relacional. LINQ to SQL
convierte las consultas de Language-Integrated Query del modelo de objetos a Transact-SQL y las envía a la base
de datos para su ejecución. Cuando la base de datos devuelve los resultados, LINQ to SQL los vuelve a traducir a
objetos. Para más información, vea LINQ to SQL.

ADO.NET Entity Framework


ADO.NET Entity Framework está diseñado para permitir que los desarrolladores creen aplicaciones de acceso a los
datos programando en un modelo de aplicación conceptual en lugar de programar directamente en un esquema
de almacenamiento relacional. El objetivo es reducir la cantidad de código y mantenimiento que se necesita para
las aplicaciones orientadas a datos. Para obtener más información, consulte ADO.NET Entity Framework.

Servicios de datos de WCF


WCF Data Services se utiliza para implementar servicios de datos en Internet o en una intranet. Los datos se
estructuran como entidades y relaciones de acuerdo a las especificaciones de Entity Data Model. Los datos
implementados en este modelo se pueden direccionar mediante el protocolo HTTP estándar. Para obtener más
información, vea WCF Data Services 4.5.

XML y ADO.NET
ADO.NET aprovecha la eficacia de XML para proporcionar acceso sin conexión a los datos. ADO.NET se diseñó a
mano con las clases XML del .NET Framework; ambos son componentes de una única arquitectura.
ADO.NET y las clases XML del .NET Framework convergen en el objeto DataSet . DataSet se puede rellenar con
datos procedentes de un origen XML, ya sea éste un archivo o una secuencia XML. DataSet se puede escribir como
XML conforme al consorcio World Wide Web (W3C), que incluye su esquema como esquema lenguaje de
definición de esquemas XML, independientemente del origen de los datos incluidos en DataSet . Puesto que el
formato nativo de serialización del DataSet es XML, es un medio excelente para mover datos de un nivel a otro,
por lo que DataSet es idóneo para usar datos y contextos de esquemas de interacción remota desde y hacia un
servicio Web XML. Para obtener más información, vea XML Documents and Data (Documentos y datos XML).

Vea también
Información general sobre ADO.NET
Directrices y opciones de tecnología de ADO.NET
10/02/2020 • 6 minutes to read • Edit Online

La plataforma de datos ADO.NET es una estrategia para varias versiones mediante la cual se reduce la cantidad de
código y el mantenimiento que necesitan los programadores con el fin de poder programar en modelos de datos
de entidades conceptuales. Esta plataforma incluye ADO.NET Entity Framework y tecnologías relacionadas.

Entity Framework
ADO.NET Entity Framework está diseñado para permitir que los desarrolladores creen aplicaciones de acceso a los
datos programando en un modelo de aplicación conceptual en lugar de programar directamente en un esquema
de almacenamiento relacional. El objetivo es reducir la cantidad de código y mantenimiento que se necesita para
las aplicaciones orientadas a datos. Para obtener más información, consulte ADO.NET Entity Framework.
Entity Data Model (EDM )
Entity Data Model (EDM) es una especificación de diseño que define datos de aplicación como conjuntos de
entidades y relaciones. Los datos de este modelo admiten la asignación relacional de objetos y la capacidad de
programación de los datos entre los límites de aplicación.
Servicios de objeto
Los servicios de objeto permiten a los programadores interactuar con los modelos conceptuales a través de un
conjunto de clases de Common Language Runtime (CLR). Estas clases se pueden generar de manera automática
desde el modelo conceptual o se pueden desarrollar de manera independiente para reflejar la estructura del
modelo conceptual. Los servicios de objeto también proporcionan compatibilidad de infraestructura con Entity
Framework, con servicios como administración de estados, seguimiento de cambios, resolución de identidad,
relaciones de carga y navegación, propagación de cambios de objeto a modificaciones de base de datos y
compatibilidad de creación de consultas para Entity SQL. Para más información, vea Información general de
Servicios de objeto (Entity Framework).
LINQ to Entities
LINQ to Entities es una implementación de Language-Integrated Query (LINQ) que permite a los desarrolladores
crear consultas fuertemente tipadas en el contexto de objeto de Entity Framework mediante expresiones de LINQ y
operadores de consulta estándar de LINQ. LINQ to Entities permite a los desarrolladores trabajar con un modelo
conceptual con una asignación flexible relacional de objetos entre Microsoft SQL Server y bases de datos de
terceros. Para obtener más información, vea LINQ to Entities.
Entity SQL
Entity SQL es un lenguaje de consulta basado en texto diseñado para interactuar con un Entity Data Model. Entity
SQL es un dialecto de SQL que contiene construcciones para la consulta en términos de conceptos de creación de
patrones de nivel superior, como herencia, tipos complejos y relaciones explícitas. Los programadores pueden
utilizar también Entity SQL directamente con los servicios de objetos. Para obtener más información, vea Entity
SQL Language.
EntityClient
EntityClient es un nuevo proveedor de datos .NET Framework utilizado para interactuar con un Entity Data Model.
EntityClient sigue el patrón de proveedor de datos .NET Framework de exponer objetos EntityConnection y
EntityCommand que devuelven EntityDataReader. EntityClient funciona con el lenguaje Entity SQL, que
proporciona una asignación flexible a los proveedores de datos específicos de almacenamiento. Para obtener más
información, consulte Proveedor de EntityClient para Entity Framework.
Herramientas de Entity Data Model
Entity Framework proporciona herramientas de línea de comandos, asistentes y diseñadores para facilitar la
compilación de aplicaciones EDM. El control EntityDataSource admite casos de enlace de datos basándose en el
EDM. La superficie de programación del control EntityDataSource es similar a otros controles de origen de datos de
Visual Studio. Para obtener más información, consulte ADO.NET Entity Data Model Tools.

LINQ a SQL
LINQ to SQL es una implementación relacional de objetos (OR/M) que le permite modelar una base de datos de
SQL Server mediante las clases de .NET Framework. LINQ to SQL permite consultar la base de datos mediante
LINQ, así como actualizar, insertar y eliminar datos de ella. LINQ to SQL admite transacciones, vistas y
procedimientos almacenados, proporcionando una forma fácil de integrar la validación de datos y las reglas de la
lógica de negocios en el modelo de datos. Puede usar Object Relational Designer (O/R Designer) para modelar
clases de entidad y asociaciones basadas en los objetos de una base de datos. Para obtener más información, vea
LINQ to SQL Tools en Visual Studio.

Servicios de datos de Microsoft WCF


WCF Data Services implementa los servicios de datos en Internet o en una intranet. Los datos se estructuran como
entidades y relaciones de acuerdo a las especificaciones de Entity Data Model. Los datos implementados en este
modelo se pueden direccionar mediante el protocolo HTTP estándar. Para obtener más información, vea WCF Data
Services 4.5.

Consulte también
Información general sobre ADO.NET
Novedades de ADO.NET
LINQ y ADO.NET
10/02/2020 • 8 minutes to read • Edit Online

En la actualidad, muchos desarrolladores empresariales deben usar dos (o más) lenguajes de programación: un
lenguaje de alto nivel para los niveles de presentación y lógica C# empresarial (como Visual o Visual Basic) y un
lenguaje de consulta para interactuar con la base de datos (como TRANSACT-SQL). Esto requiere que el
programador tenga conocimientos de varios idiomas para ser efectivo y también causa discrepancias de idiomas
en el entorno de desarrollo. Por ejemplo, una aplicación que utiliza API de acceso a datos para ejecutar una
consulta en una base de datos especifica la consulta como un literal de cadena usando comillas. Esta cadena de
consulta no es legible para el compilador y no se comprueba si hay errores, como una sintaxis no válida o si
existen las columnas o filas a las que hace referencia. No hay ninguna comprobación de tipo de los parámetros de
consulta y tampoco hay compatibilidad con IntelliSense .
Language-Integrated Query (LINQ) permite a los desarrolladores crear consultas basadas en conjuntos en su
código de aplicación, sin tener que usar un lenguaje de consulta independiente. Puede escribir consultas LINQ en
varios orígenes de datos enumerables (es decir, un origen de datos que implementa la interfaz IEnumerable), como
estructuras de datos en memoria, documentos XML, bases de datos SQL y objetos DataSet. Aunque esos orígenes
de datos enumerables se implementan de varias formas, todos revelan las mismas construcciones de lenguaje y
sintaxis. Como las consultas se pueden formar en el lenguaje de programación mismo, no es necesario utilizar otro
lenguaje de consultas que esté incrustado como literales de cadena que el compilador no pueda entender o
comprobar. La integración de consultas en el lenguaje de programación también permite que los programadores
de Visual Studio sean más productivos al proporcionar comprobación de sintaxis y tipos en tiempo de
compilación, y IntelliSense . Estas características reducen la necesidad de depuración y corrección de errores de
consultas.
La transferencia de datos de las tablas de SQL a objetos de memoria a menudo es una tarea tediosa y propensa a
errores. El proveedor LINQ implementado por LINQ to DataSet y LINQ to SQL convierte los datos de origen en
colecciones de objetos basadas en IEnumerable. El programador siempre ve los datos como una colección de
IEnumerable cuando se realiza la consulta y la actualización. Se proporciona compatibilidad completa con
IntelliSense para escribir consultas en esas colecciones.

Hay tres tecnologías de ADO.NET Language Integrated Query (LINQ) independientes: LINQ to DataSet, LINQ to
SQLy LINQ to Entities. LINQ to DataSet proporciona consultas más enriquecidas y optimizadas en el DataSet y
LINQ to SQL le permite consultar directamente esquemas de base de datos de SQL Server y LINQ to Entities le
permite consultar una Entity Data Model.
El siguiente diagrama proporciona una visión general de cómo se relacionan las tecnologías ADO.NET LINQ con
lenguajes de programación de alto nivel y orígenes de datos habilitados para LINQ.
Para obtener más información sobre LINQ, vea Language Integrated Query (LINQ).
En las secciones siguientes se proporciona más información acerca de LINQ to DataSet, LINQ to SQLy LINQ to
Entities.

LINQ to DataSet
El DataSet es un elemento clave del modelo de programación desconectado en el que se basa ADO.NET y se usa
ampliamente. LINQ to DataSet permite a los desarrolladores crear funciones de consulta más enriquecidas en
DataSet mediante el mismo mecanismo de formulación de consultas que está disponible para muchos otros
orígenes de datos. Para más información, vea LINQ to DataSet.

LINQ a SQL
LINQ to SQL es una herramienta útil para programadores que no requieren la asignación a un modelo conceptual.
Con LINQ to SQL, puede utilizar el modelo de programación LINQ directamente sobre el esquema de la base de
datos existente. LINQ to SQL permite a los desarrolladores generar .NET Framework clases que representan datos.
En lugar de la asignación a un modelo de datos conceptual, esas clases generadas se asignan directamente a tablas
de bases de datos, vistas, procedimientos almacenados y funciones definidas por el usuario.
Con LINQ to SQL, los desarrolladores pueden escribir código directamente en el esquema de almacenamiento
mediante el mismo modelo de programación de LINQ que las colecciones en memoria y el DataSet, además de
otros orígenes de datos como XML. Para obtener más información, consulta LINQ to SQL.

LINQ to Entities
La mayor parte de las aplicaciones se escriben actualmente utilizando bases de datos relacionales. En algún punto,
estas aplicaciones tendrán que interactuar con los datos representados en un formato relacional. Los esquemas de
base de datos no siempre son ideales para crear aplicaciones y los modelos conceptuales de aplicación no son
iguales que los modelos lógicos de bases de datos. La Entity Data Model es un modelo de datos conceptual que se
puede usar para modelar los datos de un dominio determinado para que las aplicaciones puedan interactuar con
los datos como objetos. Para obtener más información, consulte ADO.NET Entity Framework.
A través del modelo Entity Data Model, los datos relacionales se exponen como objetos en el entorno .NET. Esto
hace de la capa de objetos un objetivo idóneo para la compatibilidad con LINQ, ya que permite a los
programadores formular consultas en la base de datos con el lenguaje usado para compilar la lógica empresarial.
Esta funcionalidad se conoce como LINQ to Entities. Para obtener más información, vea LINQ to Entities.

Consulte también
LINQ to DataSet
LINQ to SQL
LINQ to Entities
Language-Integrated Query (LINQ)
Información general sobre ADO.NET
Proveedores de datos .NET Framework
08/01/2020 • 17 minutes to read • Edit Online

Un proveedor de datos de .NET Framework se utiliza para conectarse a una base de datos, ejecutar comandos y
recuperar resultados. Esos resultados se procesan directamente, se colocan en un DataSet con el fin de que el
usuario pueda verlos cuando los necesite, se combinan con datos de varios orígenes o se utilizan de forma remota
entre niveles. .NET Framework proveedores de datos son ligeros, creando una capa mínima entre el origen de
datos y el código, lo que aumenta el rendimiento sin sacrificar la funcionalidad.
En la tabla siguiente se enumeran los proveedores de datos que se incluyen en el .NET Framework.

P RO VEEDO R DE DATO S DE . N ET F RA M EW O RK ( . N ET
F RA M EW O RK DATA P RO VIDER) DESC RIP C IÓ N

Proveedor de datos .NET Framework para SQL Server Proporciona acceso de datos para Microsoft SQL Server.
Utiliza el espacio de nombres System.Data.SqlClient .

Proveedor de datos .NET Framework para OLE DB Para orígenes de datos que se exponen mediante OLE DB.
Utiliza el espacio de nombres System.Data.OleDb .

Proveedor de datos de .NET Framework para ODBC Para orígenes de datos que se exponen mediante ODBC.
Utiliza el espacio de nombres System.Data.Odbc .

Proveedor de datos de .NET Framework para Oracle Para orígenes de datos de Oracle. El proveedor de datos de
.NET Framework para Oracle es compatible con la versión
8.1.7 y posteriores del software cliente de Oracle y utiliza el
espacio de nombres System.Data.OracleClient.

Proveedor para EntityClient Proporciona acceso a datos para las aplicaciones de Entity
Data Model (EDM). Utiliza el espacio de nombres
System.Data.EntityClient .

.NET Framework proveedor de datos para SQL Server Proporciona acceso a datos para Microsoft SQL Server
Compact 4,0. Compact 4,0. Usa el espacio de nombres
System.Data.SqlServerCe .

Objetos principales de los proveedores de datos .NET Framework


En la tabla siguiente se describen los cuatro objetos principales que componen un .NET Framework proveedor de
datos.

O B JETO DE DESC RIP C IÓ N

Connection Establece una conexión a un origen de datos determinado. La


clase base para todos los objetos Connection es
DbConnection .

Command Ejecuta un comando en un origen de datos. Expone


Parameters y puede ejecutarse en el ámbito de un objeto
Transaction desde Connection . La clase base para todos
los objetos Command es DbCommand .
O B JETO DE DESC RIP C IÓ N

DataReader Lee un flujo de datos de solo avance y solo lectura desde un


origen de datos. La clase base para todos los objetos
DataReader es DbDataReader .

DataAdapter Llena un DataSet y realiza las actualizaciones necesarias en


el origen de datos. La clase base para todos los objetos
DataAdapter es DbDataAdapter .

Además de las clases principales enumeradas en la tabla anterior de este documento, un proveedor de datos de
.NET Framework también contiene las clases que se muestran en la tabla siguiente.

O B JETO DE DESC RIP C IÓ N

Transaction Incluye comandos en las transacciones que se realizan en el


origen de datos. La clase base para todos los objetos
Transaction es DbTransaction . ADO.NET es también
compatible con las transacciones que usan clases en el espacio
de nombres System.Transactions .

CommandBuilder Un objeto del asistente que genera automáticamente las


propiedades de comando de un DataAdapter o que obtiene
de un procedimiento almacenado información acerca de
parámetros con la que puede rellenar la colección
Parameters de un objeto Command . La clase base para
todos los objetos CommandBuilder es DbCommandBuilder .

ConnectionStringBuilder Un objeto del asistente que proporciona un modo sencillo de


crear y administrar el contenido de las cadenas de conexión
utilizadas por los objetos Connection . La clase base para
todos los objetos ConnectionStringBuilder es
DbConnectionStringBuilder .

Parameter Define los parámetros de entrada, salida y valores devueltos


para los comandos y procedimientos almacenados. La clase
base para todos los objetos Parameter es DbParameter .

Exception Se devuelve cuando se detecta un error en el origen de datos.


En el caso de un error detectado en el cliente, .NET Framework
proveedores de datos inician una excepción .NET Framework.
La clase base para todos los objetos Exception es
DbException .

Error Expone la información relacionada con una advertencia o


error devueltos por un origen de datos.

ClientPermission Se proporciona para los atributos de seguridad de acceso del


código del proveedor de datos .NET Framework. La clase base
para todos los objetos ClientPermission es
DBDataPermission .

Proveedor de datos .NET Framework para SQL Server (SqlClient)


El proveedor de datos de .NET Framework para SQL Server (SqlClient) usa su propio protocolo para comunicarse
con SQL Server. Es ligero y funciona bien porque está optimizado para tener acceso a una SQL Server
directamente sin agregar una capa de conectividad de base de datos (ODBC) OLE DB o abierta. En la siguiente
ilustración se compara el proveedor de datos de .NET Framework para SQL Server con el proveedor de datos de
.NET Framework para OLE DB. El proveedor de datos de .NET Framework para OLE DB se comunica con un origen
de datos de OLE DB a través del componente de servicio OLE DB, que proporciona agrupación de conexiones y
servicios de transacción, y el proveedor de OLE DB para el origen de datos.

NOTE
El proveedor de datos .NET Framework para ODBC tiene una arquitectura similar a la del proveedor de datos .NET
Framework para OLE DB; por ejemplo, llama a un componente de servicio ODBC.

Comparación del proveedor de datos .NET Framework para SQL Server y el proveedor de datos .NET Framework
para OLE DB
El proveedor de datos de .NET Framework para las clases de SQL Server se encuentra en el espacio de nombres
System.Data.SqlClient.
El proveedor de datos de .NET Framework para SQL Server admite transacciones locales y distribuidas. Para las
transacciones distribuidas, el proveedor de datos de .NET Framework para SQL Server, de forma predeterminada,
se da de alta automáticamente en una transacción y obtiene los detalles de la transacción de los servicios de
componentes de Windows o System.Transactions. Para obtener más información, consulte transacciones y
simultaneidad.
En el siguiente ejemplo de código se muestra cómo puede incluir el espacio de nombres System.Data.SqlClient
en sus aplicaciones.

Imports System.Data.SqlClient

using System.Data.SqlClient;

Proveedor de datos .NET Framework para OLE DB


El proveedor de datos de .NET Framework para OLE DB (OleDb) utiliza OLE DB nativa a través de la
interoperabilidad COM para habilitar el acceso a los datos. El proveedor de datos de .NET Framework para OLE DB
admite transacciones locales y distribuidas. Para las transacciones distribuidas, el proveedor de datos de .NET
Framework para OLE DB, de forma predeterminada, se da de alta automáticamente en una transacción y obtiene
los detalles de la transacción de los servicios de componentes de Windows. Para obtener más información,
consulte transacciones y simultaneidad.
En la tabla siguiente se muestran los proveedores probados con ADO.NET.

C O N T RO L A DO R P RO VEEDO R

SQLOLEDB Proveedor de OLE DB de Microsoft para SQL Server

MSDAORA Proveedor Microsoft OLE DB para Oracle


C O N T RO L A DO R P RO VEEDO R

Microsoft.Jet.OLEDB.4.0 Proveedor OLE DB para Microsoft Jet

NOTE
No se recomienda utilizar una base de datos de Access (jet) como origen de datos para las aplicaciones multiproceso, como
las aplicaciones ASP.NET. Si debe usar jet como origen de datos para una aplicación de ASP.NET, tenga en cuentan que las
aplicaciones de ASP.NET que se conectan a una base de datos de Access pueden encontrar problemas de conexión.

El proveedor de datos de .NET Framework para OLE DB no es compatible con las interfaces OLE DB versión 2,5.
OLE DB proveedores que requieran compatibilidad con las interfaces de OLE DB 2,5 no funcionarán correctamente
con el proveedor de datos de .NET Framework para OLE DB. Entre ellos se incluye el proveedor Microsoft OLE DB
para Exchange y el proveedor Microsoft OLE DB para la publicación en Internet.
El proveedor de datos de .NET Framework para OLE DB no funciona con el proveedor de OLE DB para ODBC
(MSDASQL). Para obtener acceso a un origen de datos ODBC mediante ADO.NET, use el proveedor de datos de
.NET Framework para ODBC.
.NET Framework proveedor de datos para OLE DB clases se encuentran en el espacio de nombres
System.Data.OleDb. En el siguiente ejemplo de código se muestra cómo puede incluir el espacio de nombres
System.Data.OleDb en sus aplicaciones.

Imports System.Data.OleDb

using System.Data.OleDb;

Proveedor de datos de .NET Framework para ODBC


El proveedor de datos de .NET Framework para ODBC (ODBC) utiliza el administrador de controladores ODBC
(DM) nativo para habilitar el acceso a los datos. El proveedor de datos de ODBC admite tanto transacciones locales
como transacciones distribuidas. En el caso de las transacciones distribuidas, el proveedor de datos OBDC se
inscribe de forma predeterminada y automática en una transacción y obtiene los detalles a través de los servicios
de componentes de Windows. Para obtener más información, consulte transacciones y simultaneidad.
En la tabla siguiente se muestran los controladores ODBC probados con ADO.NET.

C O N T RO L A DO R

SQL Server

Microsoft ODBC para Oracle

Microsoft Access Driver (*.mdb)

.NET Framework proveedor de datos para las clases ODBC se encuentran en el espacio de nombres
System.Data.Odbc.
En el siguiente ejemplo de código se muestra cómo puede incluir el espacio de nombres System.Data.Odbc en sus
aplicaciones.
Imports System.Data.Odbc

using System.Data.Odbc;

NOTE
El proveedor de datos de .NET Framework para ODBC requiere MDAC 2,6 o una versión posterior, y se recomienda MDAC
2,8 SP1. Puede descargar MDAC 2,8 SP1 desde el centro de descarga de Microsoft.

Proveedor de datos de .NET Framework para Oracle


El proveedor de datos de .NET Framework para Oracle (OracleClient) permite el acceso a los datos de los orígenes
de datos de Oracle a través del software de conectividad de cliente de Oracle. El proveedor de datos es compatible
con la versión 8.1.7 o posterior del software de cliente de Oracle. El proveedor de datos admite tanto transacciones
locales como transacciones distribuidas. Para obtener más información, consulte transacciones y simultaneidad.
El proveedor de datos de .NET Framework para Oracle requiere el software de cliente de Oracle (versión 8.1.7 o
una versión posterior) en el sistema para poder conectarse a un origen de datos de Oracle.
.NET Framework proveedor de datos para las clases de Oracle se encuentran en el espacio de nombres
System.Data.OracleClient y se incluyen en el ensamblado System.Data.OracleClient.dll . Al compilar una
aplicación que utiliza el proveedor de datos, debe hacer referencia tanto a System.Data.dll como a
System.Data.OracleClient.dll .

En el siguiente ejemplo de código se muestra cómo puede incluir el espacio de nombres


System.Data.OracleClient en sus aplicaciones.

Imports System.Data
Imports System.Data.OracleClient

using System.Data;
using System.Data.OracleClient;

Elegir un proveedor de datos .NET Framework


En función del diseño y del origen de datos de la aplicación, la elección de .NET Framework proveedor de datos
puede mejorar el rendimiento, la capacidad y la integridad de la aplicación. En la tabla siguiente se describen las
ventajas y las limitaciones de cada proveedor de datos de .NET Framework.

P RO VEEDO R N OTA S

Proveedor de datos .NET Framework para SQL Server Se recomienda para las aplicaciones de nivel intermedio que
usan Microsoft SQL Server.

Se recomienda para las aplicaciones de un solo nivel que usan


Microsoft Motor de base de datos (MSDE) o SQL Server.

Se recomienda usar el proveedor de OLE DB para SQL Server


(SQLOLEDB) con el proveedor de datos de .NET Framework
para OLE DB.
P RO VEEDO R N OTA S

Proveedor de datos .NET Framework para OLE DB Por SQL Server, se recomienda el proveedor de datos .NET
Framework para SQL Server en lugar de este proveedor.

Recomendado para aplicaciones de nivel único que usan bases


de datos de Microsoft Access. No se recomienda el uso de
bases de datos de Access para una aplicación de nivel medio.

Proveedor de datos de .NET Framework para ODBC Recomendado para aplicaciones de un único nivel y de nivel
medio que utilizan orígenes de datos de ODBC.

Proveedor de datos de .NET Framework para Oracle Recomendado para aplicaciones de un único nivel y de nivel
medio que utilizan orígenes de datos de Oracle.

Proveedor para EntityClient


El proveedor EntityClient se usa para obtener acceso a datos basándose en un Entity Data Model (EDM). A
diferencia de otros proveedores de datos .NET Framework, no interactúa directamente con ningún origen de
datos. En su lugar, usa Entity SQL para comunicarse con el proveedor de datos subyacente. Para obtener más
información, consulte Proveedor de EntityClient para Entity Framework.

Vea también
Información general sobre ADO.NET
Recuperar y modificar datos en ADO.NET
Objetos DataSet de ADO.NET
05/02/2020 • 6 minutes to read • Edit Online

El objeto DataSet es fundamental para admitir escenarios de datos distribuidos y desconectados con ADO.NET. El
DataSet es una representación residente en memoria de los datos que proporciona un modelo de programación
relacional coherente independientemente del origen de datos. Se puede utilizar con muchos y distintos orígenes de
datos, con datos XML o para administrar datos locales de la aplicación. El DataSet representa un conjunto
completo de datos, incluidas las tablas relacionadas, las restricciones y las relaciones entre las tablas. En la
ilustración siguiente se muestra el modelo de objetos DataSet .

Modelo de objetos DataSet


Los métodos y objetos de un conjunto de datos son coherentes con los del modelo de base de datos relacional.
El DataSet también puede conservar y volver a cargar su contenido como XML y su esquema como esquema del
lenguaje de definición de esquemas XML (XSD). Para obtener más información, vea Using XML in a DataSet (Usar
XML en un conjunto de datos).

DataTableCollection
Un conjunto de ADO.net contiene una colección de cero o más tablas representadas por objetos DataTable. La
DataTableCollection contiene todos los objetos DataTable de un DataSet .
Un DataTable se define en el espacio de nombres System.Data y representa una única tabla de datos residentes en
memoria. Contiene una colección de columnas representadas por una DataColumnCollection, así como
restricciones representadas por una ConstraintCollection, que juntas definen el esquema de la tabla. Un objeto
DataTable también contiene una colección de filas representada por el DataRowCollection, que contiene los datos
de la tabla. Una DataRow conserva, junto con su estado actual, sus versiones actual y original para identificar los
cambios en los valores almacenados en la fila.

Clase DataView
Una DataView le permite crear diferentes vistas de los datos almacenados en una DataTable, una capacidad que
suele utilizarse en aplicaciones de enlace a datos. Mediante DataView puede exponer los datos de una tabla con
distintos criterios de ordenación y filtrar los datos por el estado de fila o basándose en una expresión de filtro. Para
obtener más información, vea las vistasde datos.
DataRelationCollection
Un conjunto de objetos contiene relaciones en su objeto DataRelationCollection. Una relación, representada por el
objeto DataRelation, asocia las filas de un DataTable con las filas de otra DataTable . Las relaciones son análogas a
las rutas de acceso de unión que podrían existir entre columnas de claves principales y externas en una base de
datos relacional. Una DataRelation identifica las columnas coincidentes en dos tablas de un DataSet .
Las relaciones permiten la navegación de una tabla a otra en un conjunto de DataSet . Los elementos esenciales
de una DataRelation son el nombre de la relación, el nombre de las tablas relacionadas y las columnas
relacionadas de cada tabla. Es posible crear relaciones con más de una columna por tabla si se especifica una
matriz de objetos DataColumn como columnas de claves. Cuando se agrega una relación a la
DataRelationCollection, opcionalmente se puede Agregar un UniqueKeyConstraint y una
ForeignKeyConstraint para aplicar restricciones de integridad cuando se realicen cambios en los valores de
columna relacionados.
Para obtener más información, vea agregar objetos DataRelation.

XML
Puede rellenar un conjunto de documentos a partir de una secuencia o un documento XML. Puede utilizar la
secuencia o el documento XML para proporcionar al conjunto de datos, información de esquema o ambas cosas.
La información suministrada desde la secuencia o el documento XML puede combinarse con los datos existentes o
la información de esquema que ya está presente en el conjunto de datos. Para obtener más información, vea Using
XML in a DataSet (Usar XML en un conjunto de datos).

ExtendedProperties
Todos los conjuntos de propiedades, DataTable y DataColumn tienen una propiedad ExtendedProper ties .
ExtendedProper ties es un Proper tyCollection en el que puede colocar información personalizada, como la
instrucción SELECT que se usó para generar el conjunto de resultados o la hora a la que se generaron los datos. La
colección ExtendedProper ties se conserva con la información de esquema del conjunto de datos.

LINQ to DataSet
LINQ to DataSet proporciona funciones de consulta integradas en el lenguaje para los datos desconectados
almacenados en un conjunto de datos. LINQ to DataSet usa la sintaxis estándar de LINQ y proporciona
comprobación de sintaxis en tiempo de compilación, tipos estáticos y compatibilidad con IntelliSense cuando se
usa el IDE de Visual Studio.
Para más información, vea LINQ to DataSet.

Vea también
Información general sobre ADO.NET
Objetos DataSet, DataTable y DataView
Recuperar y modificar datos en ADO.NET
Ejecución en paralelo en ADO.NET
05/02/2020 • 10 minutes to read • Edit Online

La ejecución en paralelo en el .NET Framework es la capacidad de ejecutar una aplicación en un equipo que tiene
varias versiones de .NET Framework instaladas, exclusivamente con la versión para la que se compiló la aplicación.
Para obtener información detallada sobre la configuración de la ejecución en paralelo, vea ejecución en paralelo.
Una aplicación compilada con una versión de la .NET Framework puede ejecutarse en una versión diferente de la
.NET Framework. Sin embargo, se recomienda compilar una versión de la aplicación para cada versión instalada de
la .NET Framework y ejecutarlas por separado. En cualquier escenario, debe tener en cuenta los cambios en
ADO.NET entre versiones que pueden afectar a la compatibilidad con versiones posteriores o a la compatibilidad
con versiones anteriores de la aplicación.

Compatibilidad con versiones anteriores y posteriores


La compatibilidad con versiones posteriores significa que una aplicación se puede compilar con una versión
anterior del .NET Framework, pero se seguirá ejecutando correctamente en una versión posterior de la .NET
Framework. El código ADO.NET escrito para la versión 1,1 de .NET Framework es compatible con versiones
posteriores.
La compatibilidad con versiones anteriores significa que una aplicación se compila para una versión más reciente
del .NET Framework, pero continúa ejecutándose en versiones anteriores de la .NET Framework sin pérdida de
funcionalidad. Por supuesto, este no será el caso de las características introducidas en una nueva versión del .NET
Framework.

Proveedor de datos .NET Framework para ODBC


A partir de la versión 1,1, el proveedor de datos .NET Framework para ODBC (System.Data.Odbc) se incluye como
parte de la .NET Framework.
Si tiene una aplicación desarrollada para la .NET Framework versión 1,0 que usa el proveedor de datos de ODBC
para conectarse al origen de datos y desea ejecutar dicha aplicación en la .NET Framework versión 1,1 o posterior,
debe actualizar el espacio de nombres para el proveedor de datos ODBC a System. Data. ODBC . A continuación,
debe volver a compilarla para la versión más reciente de la .NET Framework.
Si tiene una aplicación desarrollada para la .NET Framework versión 2,0 o posterior que usa el proveedor de datos
de ODBC para conectarse al origen de datos y desea ejecutar dicha aplicación en la .NET Framework versión 1,0,
debe descargar el proveedor de datos de ODBC e instalarlo. en el sistema .NET Framework versión 1,0. A
continuación, debe cambiar el espacio de nombres para el proveedor de datos ODBC a Microsoft. Data. ODBC y
volver a compilar la aplicación para la .NET Framework versión 1,0.

Proveedor de datos .NET Framework para Oracle


A partir de la versión 1,1, el proveedor de datos de .NET Framework para Oracle (System.Data.OracleClient) se
incluye como parte de la .NET Framework.
Si tiene una aplicación desarrollada para la .NET Framework versión 2,0 o posterior que usa el proveedor de datos
para conectarse a su origen de datos y desea ejecutar dicha aplicación en la .NET Framework versión 1,0, debe
descargar el proveedor de datos e instalarlo en el. NE T Framework versión 1,0 sistema.

Seguridad de acceso del código


Los proveedores de datos de .NET Framework de la .NET Framework versión 1,0 (System.Data.SqlClient,
System.Data.OleDb) son necesarios para ejecutarse con el permiso FullTrust. Cualquier intento de usar los
proveedores de datos de .NET Framework k de la .NET Framework versión 1,0 en una zona con un permiso menor
que FullTrust produce una SecurityException.
Sin embargo, a partir de la versión 2,0 de .NET Framework, todos los proveedores de datos .NET Framework se
pueden usar en zonas de confianza parcial. Además, se ha agregado una nueva característica de seguridad a los
proveedores de datos de .NET Framework en la .NET Framework versión 1,1. Esta característica le permite restringir
las cadenas de conexión que se pueden utilizar en una zona de seguridad determinada. Es posible también
deshabilitar el uso de contraseñas en blanco para una zona de seguridad determinada. Para obtener más
información, consulta Code Access Security and ADO.NET.
Dado que cada instalación del .NET Framework tiene un archivo Security. config independiente, no hay ningún
problema de compatibilidad con la configuración de seguridad. Sin embargo, si la aplicación depende de las
capacidades de seguridad adicionales de ADO.NET incluidas en la .NET Framework versión 1,1 y versiones
posteriores, no podrá distribuirla a un sistema de la versión 1,0.

Ejecución de SqlCommand
A partir de la versión 1,1 de .NET Framework, se ha cambiado la forma en que ExecuteReader ejecuta los comandos
en el origen de datos.
En el .NET Framework versión 1,0, ExecuteReader ejecutado todos los comandos en el contexto del procedimiento
almacenado sp_executesql . Como resultado, los comandos que influyen en el estado de la conexión (Activar
NOCOUNT, por ejemplo) sólo se aplican a la ejecución del comando actual. Mientras la conexión permanece
abierta, los comandos que se ejecutan posteriormente no modifican el estado de la conexión.
En la .NET Framework versión 1,1 y posteriores, ExecuteReader solo ejecuta un comando en el contexto del
procedimiento almacenado sp_executesql si el comando contiene parámetros, lo que proporciona una ventaja de
rendimiento. Como consecuencia, si un comando que influye en el estado de la conexión se incluye en un comando
sin parámetros, modifica el estado de la conexión de todos los comandos posteriores que se ejecuten mientras la
conexión esté abierta.
Tomemos como ejemplo el siguiente lote de comandos, que se ejecuta en una llamada a ExecuteReader.

SET NOCOUNT ON;


SELECT * FROM dbo.Customers;

En la .NET Framework versión 1,1 y posteriores, NOCOUNT permanecerá en para los comandos posteriores que se
ejecuten mientras la conexión esté abierta. En la versión .NET Framework 1,0, NOCOUNT solo está activado para la
ejecución del comando actual.
Este cambio puede afectar a la compatibilidad con versiones anteriores y posteriores de la aplicación si depende
del comportamiento de ExecuteReader para cualquiera de las versiones de la .NET Framework.
En el caso de las aplicaciones que se ejecutan en versiones anteriores y posteriores de la .NET Framework, puede
escribir el código para asegurarse de que el comportamiento es el mismo independientemente de la versión en la
que se ejecuta. Si desea asegurarse de que un comando modifica el estado de la conexión para todos los comandos
que se ejecuten posteriormente, se recomienda ejecutar el comando usando ExecuteNonQuery. Si desea
asegurarse de que un comando no modifica el estado de la conexión para todos los comandos que se ejecuten
posteriormente, se recomienda incluir los comandos para restablecer el estado de la conexión en el comando. Por
ejemplo:
SET NOCOUNT ON;
SELECT * FROM dbo.Customers;
SET NOCOUNT OFF;

Vea también
Información general sobre ADO.NET
Recuperar y modificar datos en ADO.NET
ejemplos de código ADO.NET
21/03/2020 • 15 minutes to read • Edit Online

Los listados de código de esta página muestran cómo recuperar datos de una base de datos mediante las
siguientes tecnologías de ADO.NET:
Proveedores de datos ADO.NET:
SqlClient System.Data.SqlClient ()
OleDb System.Data.OleDb ()
Odbc System.Data.Odbc ()
OracleClient System.Data.OracleClient ()
ADO.NET Entity Framework:
LINQ to Entities
ObjectQuery con establecimiento de tipos
EntityClient System.Data.EntityClient ()
LINQ a SQL

ejemplos de ADO.NET proveedores de datos


En los listados de código siguientes se muestra cómo recuperar datos de una base de datos usando proveedores
de datos ADO.NET. Los datos se devuelven en DataReader . Para obtener más información, consulte Recuperación
de datos mediante un DataReader.
SqlClient
El código de este ejemplo supone que Northwind puede conectarse a la base de datos de ejemplo en Microsoft
SQL Server. El código crea SqlCommand para seleccionar filas de la tabla Products, que añade SqlParameter para
limitar los resultados a las filas con un UnitPrice mayor que el valor de parámetro especificado, en este caso 5. Se
SqlConnection abre dentro using de un bloque, lo que garantiza que los recursos se cierran y se eliminan cuando
se cierra el código. El código ejecuta el comando utilizando SqlDataReader y muestra los resultados en la ventana
de la consola.
using System;
using System.Data;
using System.Data.SqlClient;

class Program
{
static void Main()
{
string connectionString =
"Data Source=(local);Initial Catalog=Northwind;"
+ "Integrated Security=true";

// Provide the query string with a parameter placeholder.


string queryString =
"SELECT ProductID, UnitPrice, ProductName from dbo.products "
+ "WHERE UnitPrice > @pricePoint "
+ "ORDER BY UnitPrice DESC;";

// Specify the parameter value.


int paramValue = 5;

// Create and open the connection in a using block. This


// ensures that all resources will be closed and disposed
// when the code exits.
using (SqlConnection connection =
new SqlConnection(connectionString))
{
// Create the Command and Parameter objects.
SqlCommand command = new SqlCommand(queryString, connection);
command.Parameters.AddWithValue("@pricePoint", paramValue);

// Open the connection in a try/catch block.


// Create and execute the DataReader, writing the result
// set to the console window.
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}\t{2}",
reader[0], reader[1], reader[2]);
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
Option Explicit On
Option Strict On

Imports System.Data
Imports System.Data.SqlClient

Public Class Program


Public Shared Sub Main()

Dim connectionString As String = _


"Data Source=(local);Initial Catalog=Northwind;" _
& "Integrated Security=true"

' Provide the query string with a parameter placeholder.


Dim queryString As String = _
"SELECT ProductID, UnitPrice, ProductName from dbo.Products " _
& "WHERE UnitPrice > @pricePoint " _
& "ORDER BY UnitPrice DESC;"

' Specify the parameter value.


Dim paramValue As Integer = 5

' Create and open the connection in a using block. This


' ensures that all resources will be closed and disposed
' when the code exits.
Using connection As New SqlConnection(connectionString)

' Create the Command and Parameter objects.


Dim command As New SqlCommand(queryString, connection)
command.Parameters.AddWithValue("@pricePoint", paramValue)

' Open the connection in a try/catch block.


' Create and execute the DataReader, writing the result
' set to the console window.
Try
connection.Open()
Dim dataReader As SqlDataReader = _
command.ExecuteReader()
Do While dataReader.Read()
Console.WriteLine( _
vbTab & "{0}" & vbTab & "{1}" & vbTab & "{2}", _
dataReader(0), dataReader(1), dataReader(2))
Loop
dataReader.Close()

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Using
End Sub
End Class

OleDb
En el código de este ejemplo se asume que puede conectarse a la base de datos de ejemplo Northwind de
Microsoft Access. El código crea OleDbCommand para seleccionar filas de la tabla Products, que añade
OleDbParameter para limitar los resultados a las filas con un UnitPrice mayor que el valor de parámetro
especificado, en este caso 5. OleDbConnection se abre dentro de un bloque using , que garantiza que los recursos
se cierran y se eliminan cuando termina la ejecución del código. El código ejecuta el comando utilizando
OleDbDataReader y muestra los resultados en la ventana de la consola.
using System;
using System.Data;
using System.Data.OleDb;

class Program
{
static void Main()
{
// The connection string assumes that the Access
// Northwind.mdb is located in the c:\Data folder.
string connectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
+ "c:\\Data\\Northwind.mdb;User Id=admin;Password=;";

// Provide the query string with a parameter placeholder.


string queryString =
"SELECT ProductID, UnitPrice, ProductName from products "
+ "WHERE UnitPrice > ? "
+ "ORDER BY UnitPrice DESC;";

// Specify the parameter value.


int paramValue = 5;

// Create and open the connection in a using block. This


// ensures that all resources will be closed and disposed
// when the code exits.
using (OleDbConnection connection =
new OleDbConnection(connectionString))
{
// Create the Command and Parameter objects.
OleDbCommand command = new OleDbCommand(queryString, connection);
command.Parameters.AddWithValue("@pricePoint", paramValue);

// Open the connection in a try/catch block.


// Create and execute the DataReader, writing the result
// set to the console window.
try
{
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}\t{2}",
reader[0], reader[1], reader[2]);
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
Option Explicit On
Option Strict On

Imports System.Data
Imports System.Data.OleDb

Public Class Program


Public Shared Sub Main()

' The connection string assumes that the Access


' Northwind.mdb is located in the c:\Data folder.
Dim connectionString As String = _
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _
& "c:\Data\Northwind.mdb;User Id=admin;Password=;"

' Provide the query string with a parameter placeholder.


Dim queryString As String = _
"SELECT ProductID, UnitPrice, ProductName from Products " _
& "WHERE UnitPrice > ? " _
& "ORDER BY UnitPrice DESC;"

' Specify the parameter value.


Dim paramValue As Integer = 5

' Create and open the connection in a using block. This


' ensures that all resources will be closed and disposed
' when the code exits.
Using connection As New OleDbConnection(connectionString)

' Create the Command and Parameter objects.


Dim command As New OleDbCommand(queryString, connection)
command.Parameters.AddWithValue("@pricePoint", paramValue)

' Open the connection in a try/catch block.


' Create and execute the DataReader, writing the result
' set to the console window.
Try
connection.Open()
Dim dataReader As OleDbDataReader = _
command.ExecuteReader()
Do While dataReader.Read()
Console.WriteLine( _
vbTab & "{0}" & vbTab & "{1}" & vbTab & "{2}", _
dataReader(0), dataReader(1), dataReader(2))
Loop
dataReader.Close()

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Using
End Sub
End Class

Odbc
En el código de este ejemplo se asume que puede conectarse a la base de datos de ejemplo Northwind de
Microsoft Access. El código crea OdbcCommand para seleccionar filas de la tabla Products, que añade
OdbcParameter para limitar los resultados a las filas con un UnitPrice mayor que el valor de parámetro
especificado, en este caso 5. Se OdbcConnection abre dentro using de un bloque, lo que garantiza que los
recursos se cierran y se eliminan cuando se cierra el código. El código ejecuta el comando utilizando
OdbcDataReader y muestra los resultados en la ventana de la consola.
using System;
using System.Data;
using System.Data.Odbc;

class Program
{
static void Main()
{
// The connection string assumes that the Access
// Northwind.mdb is located in the c:\Data folder.
string connectionString =
"Driver={Microsoft Access Driver (*.mdb)};"
+ "Dbq=c:\\Data\\Northwind.mdb;Uid=Admin;Pwd=;";

// Provide the query string with a parameter placeholder.


string queryString =
"SELECT ProductID, UnitPrice, ProductName from products "
+ "WHERE UnitPrice > ? "
+ "ORDER BY UnitPrice DESC;";

// Specify the parameter value.


int paramValue = 5;

// Create and open the connection in a using block. This


// ensures that all resources will be closed and disposed
// when the code exits.
using (OdbcConnection connection =
new OdbcConnection(connectionString))
{
// Create the Command and Parameter objects.
OdbcCommand command = new OdbcCommand(queryString, connection);
command.Parameters.AddWithValue("@pricePoint", paramValue);

// Open the connection in a try/catch block.


// Create and execute the DataReader, writing the result
// set to the console window.
try
{
connection.Open();
OdbcDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}\t{2}",
reader[0], reader[1], reader[2]);
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
}
Option Explicit On
Option Strict On

Imports System.Data
Imports System.Data.Odbc

Public Class Program


Public Shared Sub Main()

' The connection string assumes that the Access


' Northwind.mdb is located in the c:\Data folder.
Dim connectionString As String = _
"Driver={Microsoft Access Driver (*.mdb)};" _
& "Dbq=c:\Data\Northwind.mdb;Uid=Admin;Pwd=;"

' Provide the query string with a parameter placeholder.


Dim queryString As String = _
"SELECT ProductID, UnitPrice, ProductName from Products " _
& "WHERE UnitPrice > ? " _
& "ORDER BY UnitPrice DESC;"

' Specify the parameter value.


Dim paramValue As Integer = 5

' Create and open the connection in a using block. This


' ensures that all resources will be closed and disposed
' when the code exits.
Using connection As New OdbcConnection(connectionString)

' Create the Command and Parameter objects.


Dim command As New OdbcCommand(queryString, connection)
command.Parameters.AddWithValue("@pricePoint", paramValue)

' Open the connection in a try/catch block.


' Create and execute the DataReader, writing the result
' set to the console window.
Try
connection.Open()
Dim dataReader As OdbcDataReader = _
command.ExecuteReader()
Do While dataReader.Read()
Console.WriteLine( _
vbTab & "{0}" & vbTab & "{1}" & vbTab & "{2}", _
dataReader(0), dataReader(1), dataReader(2))
Loop
dataReader.Close()

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Using
End Sub
End Class

OracleClient
En el código de este ejemplo se presupone una conexión a DEMO.CUSTOMER en un servidor Oracle. También debe
agregarse una referencia a System.Data.OracleClient.dll. El código devuelve los datos en OracleDataReader.
using System;
using System.Data;
using System.Data.OracleClient;

class Program
{
static void Main()
{
string connectionString =
"Data Source=ThisOracleServer;Integrated Security=yes;";
string queryString =
"SELECT CUSTOMER_ID, NAME FROM DEMO.CUSTOMER";
using (OracleConnection connection =
new OracleConnection(connectionString))
{
OracleCommand command = connection.CreateCommand();
command.CommandText = queryString;

try
{
connection.Open();

OracleDataReader reader = command.ExecuteReader();

while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}",
reader[0], reader[1]);
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
Option Explicit On
Option Strict On

Imports System.Data
Imports System.Data.OracleClient

Public Class Program


Public Shared Sub Main()

Dim connectionString As String = _


"Data Source=ThisOracleServer;Integrated Security=yes;"

Dim queryString As String = _


"SELECT CUSTOMER_ID, NAME FROM DEMO.CUSTOMER"

Using connection As New OracleConnection(connectionString)


Dim command As OracleCommand = connection.CreateCommand()
command.CommandText = queryString
Try
connection.Open()
Dim dataReader As OracleDataReader = _
command.ExecuteReader()
Do While dataReader.Read()
Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", _
dataReader(0), dataReader(1))
Loop
dataReader.Close()

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
End Sub
End Class

Ejemplos de Entity Framework


En los listados de código siguientes se muestra cómo recuperar los datos de un origen de datos consultando las
entidades de un Entity Data Model (EDM). Estos ejemplos utilizan un modelo basado en la base de datos de
ejemplo Northwind. Para obtener más información acerca de Entity Framework, vea Información general de Entity
Framework.
LINQ to Entities
El código de este ejemplo usa una consulta LINQ para devolver los datos como objetos Categories, que se
proyectan como un tipo anónimo que contiene solo las propiedades CategoryID y CategoryName. Para obtener
más información, vea Información general sobre LINQ to Entities.
using System;
using System.Linq;
using System.Data.Objects;
using NorthwindModel;

class LinqSample
{
public static void ExecuteQuery()
{
using (NorthwindEntities context = new NorthwindEntities())
{
try
{
var query = from category in context.Categories
select new
{
categoryID = category.CategoryID,
categoryName = category.CategoryName
};

foreach (var categoryInfo in query)


{
Console.WriteLine("\t{0}\t{1}",
categoryInfo.categoryID, categoryInfo.categoryName);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

Option Explicit On
Option Strict On

Imports System.Linq
Imports System.Data.Objects
Imports NorthwindModel

Class LinqSample
Public Shared Sub ExecuteQuery()
Using context As NorthwindEntities = New NorthwindEntities()
Try
Dim query = From category In context.Categories _
Select New With _
{ _
.categoryID = category.CategoryID, _
.categoryName = category.CategoryName _
}

For Each categoryInfo In query


Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", _
categoryInfo.categoryID, categoryInfo.categoryName)
Next
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
End Sub
End Class

ObjectQuery con establecimiento de tipos


En el código de este ejemplo se utiliza ObjectQuery<T> para devolver los datos como objetos Categories. Para
obtener más información, consulte Consultas de objetos.

using System;
using System.Data.Objects;
using NorthwindModel;

class ObjectQuerySample
{
public static void ExecuteQuery()
{
using (NorthwindEntities context = new NorthwindEntities())
{
ObjectQuery<Categories> categoryQuery = context.Categories;

foreach (Categories category in


categoryQuery.Execute(MergeOption.AppendOnly))
{
Console.WriteLine("\t{0}\t{1}",
category.CategoryID, category.CategoryName);
}
}
}
}

Option Explicit On
Option Strict On

Imports System.Data.Objects
Imports NorthwindModel

Class ObjectQuerySample
Public Shared Sub ExecuteQuery()
Using context As NorthwindEntities = New NorthwindEntities()
Dim categoryQuery As ObjectQuery(Of Categories) = context.Categories

For Each category As Categories In _


categoryQuery.Execute(MergeOption.AppendOnly)
Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", _
category.CategoryID, category.CategoryName)
Next
End Using
End Sub
End Class

EntityClient
El código de este ejemplo usa EntityCommand para ejecutar una consulta Entity SQL. Esta consulta devuelve una
lista de registros que representan instancias del tipo de entidad Categories. Se usa EntityDataReader para obtener
acceso a los registros de datos del conjunto de resultados. Para obtener más información, consulte Proveedor de
EntityClient para Entity Framework.
using System;
using System.Data;
using System.Data.Common;
using System.Data.EntityClient;
using NorthwindModel;

class EntityClientSample
{
public static void ExecuteQuery()
{
string queryString =
@"SELECT c.CategoryID, c.CategoryName
FROM NorthwindEntities.Categories AS c";

using (EntityConnection conn =


new EntityConnection("name=NorthwindEntities"))
{
try
{
conn.Open();
using (EntityCommand query = new EntityCommand(queryString, conn))
{
using (DbDataReader rdr =
query.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (rdr.Read())
{
Console.WriteLine("\t{0}\t{1}", rdr[0], rdr[1]);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
Option Explicit On
Option Strict On

Imports System.Data
Imports System.Data.Common
Imports System.Data.EntityClient
Imports NorthwindModel

Class EntityClientSample
Public Shared Sub ExecuteQuery()
Dim queryString As String = _
"SELECT c.CategoryID, c.CategoryName " & _
"FROM NorthwindEntities.Categories AS c"

Using conn As EntityConnection = _


New EntityConnection("name=NorthwindEntities")

Try
conn.Open()
Using query As EntityCommand = _
New EntityCommand(queryString, conn)
Using rdr As DbDataReader = _
query.ExecuteReader(CommandBehavior.SequentialAccess)
While rdr.Read()
Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", _
rdr(0), rdr(1))
End While
End Using
End Using
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
End Sub
End Class

LINQ a SQL
El código de este ejemplo usa una consulta LINQ para devolver los datos como objetos Categories, que se
proyectan como un tipo anónimo que contiene solo las propiedades CategoryID y CategoryName. Este ejemplo se
basa en el contexto de datos Northwind. Para obtener más información, consulte Introducción.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Northwind;

class LinqSqlSample
{
public static void ExecuteQuery()
{
using (NorthwindDataContext db = new NorthwindDataContext())
{
try
{
var query = from category in db.Categories
select new
{
categoryID = category.CategoryID,
categoryName = category.CategoryName
};

foreach (var categoryInfo in query)


{
Console.WriteLine("vbTab {0} vbTab {1}",
categoryInfo.categoryID, categoryInfo.categoryName);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

Option Explicit On
Option Strict On

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports Northwind

Class LinqSqlSample
Public Shared Sub ExecuteQuery()
Using db As NorthwindDataContext = New NorthwindDataContext()
Try
Dim query = From category In db.Categories _
Select New With _
{ _
.categoryID = category.CategoryID, _
.categoryName = category.CategoryName _
}

For Each categoryInfo In query


Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", _
categoryInfo.categoryID, categoryInfo.categoryName)
Next
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
End Sub
End Class
Consulte también
Información general de ADO.NET
Recuperar y modificar datos en ADO.NET
Crear aplicaciones de datos
Consultar un Entity Data Model (tareas de Entity Framework)
Cómo: Ejecutar una consulta que devuelve objetos de tipos anónimos
Proteger aplicaciones de ADO.NET
05/02/2020 • 3 minutes to read • Edit Online

Para escribir una aplicación de ADO.NET segura es necesario algo más que evitar los errores de codificación más
comunes, como no validar los datos proporcionados por el usuario. Una aplicación que tiene acceso a datos tiene
muchos puntos débiles potenciales que un agresor puede aprovechar para obtener, manipular o destruir datos
confidenciales. Por eso es importante comprender todos los aspectos de la seguridad, desde el proceso de
modelo de amenazas durante la fase de diseño de su aplicación hasta la implementación y el posterior
mantenimiento.
.NET Framework ofrece muchas clases, servicios y herramientas que resultan muy útiles para proteger y
administrar aplicaciones de base de datos. Common Language Runtime (CLR) proporciona un entorno de
seguridad de tipos en el que ejecutar el código, junto con la seguridad de acceso del código (CAS) para restringir
aún más los permisos del código administrado. Si se siguen las recomendaciones de codificación del acceso
seguro a datos, se reduce el daño que podría provocar un posible agresor.
El código seguro no protege de la vulnerabilidad de seguridad que provoca el propio usuario cuando trabaja con
recursos no administrados como bases de datos. La mayoría de las bases de datos, como SQL Server, tienen sus
propios sistemas de seguridad, que contribuyen a mejorarla cuando se implementan correctamente. Sin
embargo, incluso un origen de datos con un robusto sistema de seguridad puede sufrir un ataque si no se ha
configurado correctamente.

Esta sección
Información general sobre seguridad
Proporciona recomendaciones para diseñar aplicaciones seguras de ADO.NET.
Acceso seguro a datos
Describe cómo trabajar con datos de un origen de datos protegido.
Aplicaciones cliente seguras
Describe consideraciones de seguridad para aplicaciones cliente.
Seguridad de acceso del código y ADO.NET
Describe cómo se puede proteger el código de ADO.NET mediante la seguridad de acceso del código. También
explica cómo trabajar con confianza parcial.
Privacidad y seguridad de datos
Describe las opciones de cifrado para las aplicaciones de ADO.NET.

Secciones relacionadas
Seguridad de SQL Server
Describe las características de seguridad de SQL Server desde la perspectiva del desarrollador.
Consideraciones de seguridad
Describe la seguridad de las aplicaciones de Entity Framework.
Seguridad
Contiene vínculos a temas en los que describen todos los aspectos de la seguridad en .NET.
Herramientas de seguridad
Herramientas de .NET Framework para proteger y administrar las directivas de seguridad.
Recursos para crear aplicaciones seguras
Proporciona vínculos a temas para crear aplicaciones seguras.
Bibliografía sobre seguridad
Proporciona vínculos a recursos externos disponibles en línea e impresos.

Vea también
ADO.NET
Información general sobre ADO.NET
Introducción sobre la seguridad
08/01/2020 • 11 minutes to read • Edit Online

La protección de una aplicación es un proceso continuo. Es prácticamente imposible que se llegue a un momento
en el que un programador pueda garantizar que una aplicación es invulnerable ante todo tipo de ataques, ya que
no es posible predecir los tipos de ataques que surgirán en un futuro con las nuevas tecnologías. Al contrario, el
hecho de que nadie haya detectado (o publicado) aún brechas de seguridad en un sistema, no quiere decir que
éstas no existan o no puedan existir en un futuro. Por lo tanto, es necesario planificar la seguridad durante la fase
de diseño del proyecto, así como la forma en que se mantendrá la seguridad durante el ciclo de vida de la
aplicación.

Diseñar la seguridad
Uno de los mayores problemas de la programación de aplicaciones seguras es el hecho de que a menudo la
seguridad no se planifica con antelación, sino que se implementa cuando el código de un proyecto se ha
completado. Esta práctica genera aplicaciones poco seguras, ya que no se ha dedicado suficiente tiempo a entender
qué hace que una aplicación sea segura.
La implementación de la seguridad de última hora conduce a más errores, ya que el software se interrumpe según
las nuevas restricciones o se tiene que volver a escribir para adaptarse a la funcionalidad imprevista. Cada línea de
código revisado incluye la posibilidad de insertar un error nuevo. En ese sentido, debe tener en cuenta la seguridad
al principio del proceso de programación de manera que se desarrolle junto con la programación de nuevas
características.
Modelo de amenazas
No se puede proteger un sistema de ataques a menos que comprenda todos los posibles ataques a los que está
expuesto. El proceso de evaluación de las amenazas de seguridad, denominados modelado de amenazas, es
necesario para determinar la probabilidad y las consecuencias de las infracciones de seguridad en la aplicación
ADO.net.
El modelo de amenazas está compuesto de tres pasos principales: comprender la visión del adversario, caracterizar
la seguridad del sistema y determinar las amenazas.
El modelo de amenazas es un enfoque repetitivo en la evaluación de las vulnerabilidades de la aplicación, que se
usa para detectar las vulnerabilidades más peligrosas, ya que éstas exponen los datos más confidenciales. Una vez
identificadas, las vulnerabilidades se clasifican en función de su gravedad y se crea un conjunto con prioridades de
medidas para contrarrestar las amenazas.
Para obtener más información, vea los siguientes recursos:

REC URSO DESC RIP C IÓ N

El sitio de modelado de amenazas en el portal de ingeniería de Los recursos de esta página le ayudarán a entender el proceso
seguridad de modelo de amenazas y a desarrollar modelos de amenazas
que puede usar para proteger sus propias aplicaciones

Principio de los privilegios mínimos


Cuando diseñe, compile e implemente la aplicación, debe asumir que ésta será objeto de ataques. Estos ataques
suelen proceder de código malintencionado que se activa con los permisos del usuario que ejecuta el código. Otros
pueden provenir de código no dañino, cuyas vulnerabilidades aprovecha un atacante. Cuando se planifica la
seguridad, siempre hay que asumir que se puede producir la peor situación posible.
Una medida para contrarrestar los ataques consiste en intentar establecer tantos muros en el código como sea
posible mediante la ejecución con los privilegios mínimos. El principio de los privilegios mínimos indica que se
deben conceder privilegios para la menor cantidad de código posible durante el tiempo mínimo necesario para
conseguir que se realice el trabajo.
El procedimiento recomendado para crear aplicaciones seguras consiste en comenzar sin ningún permiso y ir
agregando los mínimos permisos necesarios para la tarea concreta que se lleva a cabo. El enfoque opuesto, es
decir, comenzar con todos los permisos e ir denegando permisos posteriormente de forma individual, proporciona
aplicaciones poco seguras, difíciles de probar y mantener, ya que pueden existir vulnerabilidades de seguridad
causados por concesiones no intencionadas de más permisos de los que son necesarios.
Para obtener más información sobre cómo proteger las aplicaciones, consulte los siguientes recursos:

REC URSO DESC RIP C IÓ N

Proteger aplicaciones Contiene vínculos a temas generales de la seguridad.


Asimismo, incluye vínculos a temas sobre la protección de las
aplicaciones distribuidas, aplicaciones web, aplicaciones
móviles y de escritorio.

Seguridad de acceso del código (CAS)


La seguridad de acceso del código (CAS) es un mecanismo que ayuda a limitar el acceso del código a recursos y
operaciones protegidos. En .NET Framework, CAS realiza las funciones siguientes:
Define permisos y conjuntos de permisos que representan el derecho de acceso a varios recursos del
sistema.
Permite a los administradores configurar la directiva de seguridad mediante la asociación de conjuntos de
permisos a grupos de código.
Permite que el código solicite los permisos que necesita para ejecutarse, así como los permisos que sería útil
tener, y especifica los permisos que nunca debe tener el código.
Concede permisos a cada ensamblado que se carga, basándose en los permisos solicitados por el código y
en las operaciones permitidas por la directiva de seguridad.
Permite que el código exija que sus llamadores tengan permisos específicos.
Permite que el código exija que sus llamadores posean una firma digital, por lo que solo los llamadores de
una organización o un sitio concretos pueden llamar al código protegido.
Impone restricciones en el código en tiempo de ejecución mediante la comparación de los permisos
concedidos a cada llamador en la pila de llamadas con los permisos que deben poseer.
Para reducir los daños que se pueden producir cuando un ataque tiene éxito, elija un contexto de seguridad para el
código de forma que se conceda acceso única y exclusivamente a los recursos necesarios para realizar el trabajo.
Para obtener más información, vea los siguientes recursos:

REC URSO DESC RIP C IÓ N

Seguridad de acceso del código y ADO.NET Describe las interacciones entre la seguridad de acceso del
código, la seguridad basada en funciones y los entornos de
confianza parcial desde la perspectiva de una aplicación
ADO.NET.
REC URSO DESC RIP C IÓ N

Seguridad de acceso del código Contiene vínculos a temas adicionales que describen CAS en
.NET Framework.

Seguridad de base de datos


El principio de privilegios mínimos también se aplica al origen de los datos. A continuación se citan algunas
instrucciones generales para la seguridad de base de datos:
Crear cuentas con los privilegios mínimos posibles.
No permitir que los usuarios obtengan acceso a cuentas administrativas tan solo para que el código
funcione.
No devolver mensajes de error de servidor a las aplicaciones cliente.
Validar todas las entradas, tanto en el cliente como en el servidor.
Usar comandos con parámetros y evitar instrucciones SQL dinámicas.
Habilitar el registro y la auditoría de seguridad en la base de datos que se utiliza, de forma que se reciba una
alerta en caso de infracciones de seguridad.
Para obtener más información, vea los siguientes recursos:

REC URSO DESC RIP C IÓ N

Seguridad de SQL Server Proporciona una introducción general a la seguridad de SQL


Server con escenarios de aplicación que orientan en la
creación de aplicaciones ADO.NET seguras dirigidas a SQL
Server.

Recomendaciones para las estrategias de acceso a datos Proporciona recomendaciones para obtener acceso a datos y
realizar operaciones de base de datos.

Directiva de seguridad y administración


La administración inadecuada de la directiva de seguridad de acceso del código (CAS) puede crear puntos débiles
en la seguridad. Cuando se utiliza una aplicación, deben seguirse las técnicas de supervisión de seguridad y deben
evaluarse los riesgos a medida que surgen nuevas amenazas.
Para obtener más información, vea los siguientes recursos:

REC URSO DESC RIP C IÓ N

Administración de directivas de seguridad Proporciona información sobre la creación y administración de


la directiva de seguridad.

Prácticas recomendadas de la Directiva de seguridad Proporciona vínculos a temas que describen cómo administrar
la directiva de seguridad.

Vea también
Proteger aplicaciones de ADO.NET
Seguridad en .NET
Seguridad de SQL Server
Información general sobre ADO.NET
Acceso seguro a datos
08/01/2020 • 10 minutes to read • Edit Online

Para escribir código de ADO.NET seguro, debe comprender los mecanismos de seguridad disponibles en el
almacén de datos subyacente o en la base de datos. También debe tener en cuenta las implicaciones en la
seguridad de otras características o componentes que pudieran incluirse en la aplicación.

Autenticación, autorización y permisos


Si se conecta a Microsoft SQL Server, puede usar la autenticación de Windows, también conocida como seguridad
integrada, que emplea la identidad del usuario de Windows activo en lugar de pasar un identificador de usuario y
una contraseña. Se recomienda encarecidamente usar la autenticación de Windows, ya que las credenciales del
usuario no están expuestas en la cadena de conexión. Si no puede usar la autenticación de Windows para
conectarse a SQL Server, puede crear cadenas de conexión en tiempo de ejecución mediante
SqlConnectionStringBuilder.
Las credenciales utilizados para la autenticación deben tratarse de manera diferente dependiendo del tipo de
aplicación. Por ejemplo, en una aplicación de Windows Forms, se puede solicitar al usuario que proporcione
información de autenticación o bien, se pueden utilizar las credenciales de Windows del usuario. Sin embargo, una
aplicación web normalmente obtiene acceso a los datos mediante credenciales proporcionadas por la misma
aplicación y no por el usuario.
Una vez que los usuarios se han autenticado, el ámbito de sus acciones depende de los permisos que se les hayan
concedido. Siga siempre el principio de los privilegios mínimos y conceda solo los permisos absolutamente
necesarios.
Para obtener más información, vea los siguientes recursos.

REC URSO DESC RIP C IÓ N

Proteger la información de conexión Describe las prácticas y técnicas recomendadas de seguridad


para proteger la información de conexión, como el uso de
configuración protegida para cifrar cadenas de conexión.

Recomendaciones para las estrategias de acceso a datos Proporciona recomendaciones para obtener acceso a datos y
realizar operaciones de base de datos.

Generadores de cadenas de conexión Describe cómo crear cadenas de conexión a partir de la


entrada del usuario en tiempo de ejecución.

Información general sobre la seguridad de SQL Server Describe la arquitectura de seguridad de SQL Server.

Comandos con parámetros e inyección de código SQL


La utilización de comandos con parámetros ayuda en la protección contra ataques por inyección de código SQL, en
los que un atacante "inyecta" un comando en una instrucción SQL que pone en peligro la seguridad del servidor.
Los comandos con parámetros protegen de ataques por inyección de código SQL ya que garantizan que los
valores recibidos desde un origen externo pasan solo como valores y no como parte de la instrucción de Transact-
SQL. Como resultado, los comandos Transact-SQL insertados en un valor no se ejecutan en el origen de datos. En
cambio, se evalúan únicamente como un valor de parámetro. Además de las ventajas en el aspecto de la seguridad,
los comandos con parámetros proporcionan un método práctico para organizar los valores que se pasan con una
instrucción de Transact-SQL a un procedimiento almacenado.
Para obtener más información sobre el uso de comandos con parámetros, vea los siguientes recursos.

REC URSO DESC RIP C IÓ N

Parámetros de DataAdapter Describe cómo usar parámetros con DataAdapter .

Modificación de datos con procedimientos almacenados Describe cómo especificar parámetros y cómo obtener un
valor devuelto.

Administración de permisos con procedimientos almacenados Describe cómo usar procedimientos almacenados de SQL
en SQL Server Server para encapsular el acceso a datos.

Ataques mediante scripts


Los ataques mediante scripts constituyen otra forma de inyección que usa caracteres malintencionados insertados
en una página web. El explorador no valida los caracteres insertados y los procesa como parte de la página.
Para obtener más información, vea los siguientes recursos.

REC URSO DESC RIP C IÓ N

Introducción a los ataques de scripts Describe cómo protegerse de ataques mediante scripts o
instrucciones SQL.

Ataques mediante sondeo


Es muy frecuente que los atacantes utilicen la información de una excepción, como el nombre del servidor, de la
base de datos o de una tabla, para llevar a cabo un ataque contra el sistema. Como las excepciones pueden
contener información específica sobre la aplicación o el origen de datos, puede mejorar la protección de la
aplicación y del origen de datos exponiendo únicamente la información necesaria al cliente.
Para obtener más información, vea los siguientes recursos.

REC URSO DESC RIP C IÓ N

Controlar y generar excepciones en .NET Describe las formas básicas Try/Catch/Finally del control de
excepciones estructurado.

Procedimientos recomendados para excepciones Describe los procedimientos recomendados para controlar
excepciones.

Proteger orígenes de datos de Access y Excel de Microsoft


Microsoft Access y Microsoft Excel pueden funcionar como almacén de datos para una aplicación de ADO.NET
cuando los requisitos de seguridad son mínimos o no existen. Sus características de seguridad son eficaces para
fines de disuasión, aunque solo es conveniente confiar en ellas para evitar la intromisión por parte de usuarios no
informados. Los archivos de datos físicos de Access y Excel existen en el sistema de archivos y todos los usuarios
deben tener acceso a ellos. Esto los hace vulnerables ante ataques que pueden dar lugar al robo o pérdida de datos,
ya que los archivos se pueden copiar o modificar fácilmente. Cuando sea necesaria una seguridad potente, use SQL
Server u otra base de datos basada en servidor donde los archivos de datos físicos no se puedan leer desde el
sistema de archivos.
Para obtener más información sobre la protección de datos de Access y Excel, vea los siguientes recursos.
REC URSO DESC RIP C IÓ N

Consideraciones de seguridad e instrucciones para el acceso Describe técnicas de seguridad para Access 2007 como el
2007 cifrado de archivos, la administración de contraseñas, la
conversión de bases de datos a los nuevos formatos ACCDB y
ACCDE y el uso de otras opciones de seguridad.

Introducción a Access 2010 Security Proporciona información general sobre las características de
seguridad que ofrece Access 2010.

Enterprise Services
COM+ incluye su propio modelo de seguridad que se basa en las cuentas de Windows NT y en la suplantación de
procesos y subprocesos. El espacio de nombres System.EnterpriseServices proporciona contenedores que
permiten a las aplicaciones .NET integrar código administrado con servicios de seguridad COM+ mediante la clase
ServicedComponent.
Para obtener más información, vea el siguiente recurso.

REC URSO DESC RIP C IÓ N

Seguridad basada en roles Describe cómo integrar código administrado en servicios de


seguridad COM+.

Interoperar con código no administrado


.NET Framework proporciona interacción con código no administrado, incluidos componentes COM, servicios
COM+, bibliotecas de tipos externas y muchos servicios del sistema operativo. El trabajo con código no
administrado supone traspasar el perímetro de seguridad del código administrado. Tanto su código como
cualquier otro código que llame a su código, deben tener el permiso de código no administrado
(SecurityPermission con el marcador UnmanagedCode especificado). El código no administrado puede insertar de
forma involuntaria vulnerabilidades de seguridad en la aplicación. Por tanto, debe evitar la interoperabilidad con
código no administrado a menos que sea absolutamente necesario.
Para obtener más información, vea los siguientes recursos.

REC URSO DESC RIP C IÓ N

Interoperating with Unmanaged Code (Interoperar con código Contiene temas que describen cómo exponer componentes
no administrado) COM a .NET Framework y cómo exponer componentes de
.NET Framework a COM.

Interoperabilidad COM avanzada Contiene temas avanzados como, por ejemplo, los
ensamblados de interoperabilidad principal, los subprocesos y
el cálculo de referencias personalizado.

Vea también
Proteger aplicaciones de ADO.NET
Seguridad de SQL Server
Recomendaciones para las estrategias de acceso a datos
Proteger la información de conexión
Generadores de cadenas de conexión
Información general sobre ADO.NET
Aplicaciones cliente seguras
23/10/2019 • 6 minutes to read • Edit Online

Por lo general las aplicaciones constan de varios elementos que deben estar protegidos ante las vulnerabilidades
que pueden provocar pérdidas de datos o poner en peligro el sistema de cualquier otro modo. La creación de
interfaces de usuario seguras puede impedir un gran número de problemas ya que bloquea a los atacantes antes
de que puedan tener acceso a los datos o a los recursos del sistema.

Validar datos introducidos por el usuario


Al construir una aplicación en la que se obtiene acceso a datos, debe presuponer que todos los datos
proporcionados por el usuario son malintencionados, a no ser que se demuestre lo contrario. De no ser así, la
aplicación puede estar expuesta a ataques. .NET Framework contiene clases que ayudan a exigir un dominio de
valores para los controles de información introducida por el usuario, como la limitación del número de caracteres
que se pueden introducir. Los enlaces de eventos permiten escribir procedimientos para comprobar la validez de
los valores. Los datos introducidos por el usuario se pueden validar y tipar fuertemente, lo que limita la exposición
de una aplicación ante ataques de inyección de script y SQL.

IMPORTANT
También debe validar los datos introducidos por el usuario en el origen de datos, además de la aplicación cliente. Un atacante
puede evitar la aplicación y atacar directamente al origen de datos.

Seguridad e introducción de datos por el usuario


Describe cómo controlar errores sutiles y potencialmente peligrosos relacionados con la introducción de datos por
parte del usuario.
Validación de la entrada del usuario en ASP.NET Web Pages
Información general sobre la validación de datos introducidos por el usuario con controles de validación de
ASP.NET.
Datos proporcionados por el usuario en Windows Forms
Proporciona vínculos e información para validar entrada de mouse y teclado en aplicaciones de Windows Forms.
Expresiones regulares de .NET Framework
Describe cómo utilizar la clase Regex para comprobar la validez de los datos introducidos por el usuario.

Aplicaciones para Windows


En versiones anteriores, las aplicaciones Windows normalmente se ejecutaban con todos los permisos. .NET
Framework proporciona la infraestructura para restringir la ejecución del código en una aplicación Windows
mediante la seguridad de acceso del código (CAS). Sin embargo, CAS no es suficiente por sí solo para proteger la
aplicación.
Windows Forms Security
Describe cómo proteger las aplicaciones de Windows Forms y proporciona vínculos a temas relacionados.
Windows Forms and Unmanaged Applications
Describe cómo interactuar con aplicaciones no administradas en una aplicación de Windows Forms.
Implementación de ClickOnce para Windows Forms
Describe cómo usar la implementación de ClickOnce en una aplicación de Windows Forms y describe las
implicaciones en la seguridad.

ASP.NET y servicios Web XML


Por lo general, las aplicaciones ASP.NET deben restringir el acceso a algunas porciones del sitio web y proporcionan
otros mecanismos para la protección de datos y la seguridad del sitio. Estos vínculos proporcionan información útil
para proteger la aplicación ASP.NET.
Los servicios Web XML proporcionan datos que pueden consumir las aplicaciones ASP.NET, las aplicaciones de
Windows Forms u otros servicios Web. Debe administrar la seguridad del propio servicio Web así como la de la
aplicación cliente.
Para obtener más información, vea los siguientes recursos.

REC URSO DESC RIP C IÓ N

Protección de sitios web de ASP.NET Describe cómo proteger aplicaciones ASP.NET.

Proteger los servicios Web XML creados con ASP.NET Describe cómo implementar la seguridad en un servicio Web
ASP.NET.

Introducción a los ataques de scripts Describe cómo protegerse ante ataques de script, que
intentan insertar caracteres malintencionados en una página
web.

Prácticas de seguridad básicas para aplicaciones Web Información general sobre la seguridad y vínculos para
profundizar en el tema.

Comunicación remota
La comunicación remota de .NET permite crear fácilmente aplicaciones ampliamente distribuidas, tanto si los
componentes de las aplicaciones están todos en un equipo como si están repartidos por el mundo. Puede generar
aplicaciones cliente que utilizan los objetos de otros procesos en el mismo equipo o en cualquier otro equipo que
se puede alcanzar a través de su red. También puede usar .NET Remoting para comunicar con otros dominios de
aplicación en el mismo proceso.

REC URSO DESC RIP C IÓ N

Configuración de aplicaciones remotas Describe cómo configurar aplicaciones de comunicación


remota para evitar problemas habituales.

Seguridad en comunicación remota Describe la autenticación y el cifrado, así como temas


adicionales de seguridad relacionados con la comunicación
remota.

Consideraciones sobre seguridad y comunicación remota Describe problemas de seguridad con objetos protegidos y
con el cruce entre dominios de aplicación.

Vea también
Proteger aplicaciones de ADO.NET
Recomendaciones para las estrategias de acceso a datos
Proteger aplicaciones
Proteger la información de conexión
Información general sobre ADO.NET
Seguridad de acceso del código y ADO.NET
21/03/2020 • 25 minutes to read • Edit Online

.NET Framework ofrece seguridad basada en roles y seguridad de acceso del código (CAS); ambas se implementan
utilizando una infraestructura común proporcionada por Common Language Runtime (CLR). En el mundo del
código no administrado, la mayoría de las aplicaciones se ejecutan mediante los permisos del usuario o de la
entidad de seguridad. Por consiguiente, los sistemas de equipos pueden resultar dañados y se pueden poner en
peligro los datos privados si un usuario con un nivel elevado de privilegios ejecuta software malintencionado o
que contenga errores.
El código administrado que se ejecuta en .NET Framework, sin embargo, incluye seguridad de acceso del código,
que se aplica únicamente al código. El permiso para que el código se ejecute o no depende de su origen o de otros
aspectos de la identidad del código, y no solo de la identidad de la entidad de seguridad. De esta forma, se reduce
la probabilidad de que se utilice el código de manera incorrecta.

Permisos de acceso del código


Cuando se ejecuta el código, éste presenta evidencia de que lo evalúa el sistema de seguridad Common Language
Runtime CLR. Este tipo de evidencia normalmente contiene el origen del código, incluidos la dirección URL, el sitio,
la zona y las firmas digitales que determinan la identidad del ensamblado.
CLR permite que el código realice únicamente las operaciones para las que tiene permiso. El código puede solicitar
permisos y las peticiones se aceptan en función de la directiva de seguridad que haya establecido un
administrador.

NOTE
El código que se ejecuta en el CLR no se puede conceder permisos a sí mismo. Por ejemplo, el código puede solicitar y que
se le asignen menos permisos de los que concede una directiva de seguridad, pero no se le concederán más permisos. A la
hora de conceder permisos, comience sin ningún permiso y agregue los mínimos permisos necesarios para la tarea concreta
que se lleva a cabo. Si se comienza con todos los permisos y posteriormente se deniegan permisos de forma individual, se
obtienen aplicaciones poco seguras que pueden contener vulnerabilidades de seguridad no intencionadas debido a la
concesión de más permisos de los que son necesarios. Para obtener más información, consulte Configuración de la directiva
de seguridad y la administración de directivasde seguridad .

Hay tres tipos de permisos de acceso a código:


Los Code access permissions derivan de la clase CodeAccessPermission. Se requieren permisos para tener
acceso a recursos protegidos, como archivos y variables de entorno, y para realizar operaciones protegidas,
como el acceso a código no administrado.
Los Identity permissions representan características que identifican un ensamblado. Los permisos se
conceden al ensamblado en función de la evidencia, que puede incluir elementos como una firma digital o
el origen del código. Los permisos de identidad también derivan de la clase base CodeAccessPermission.
Los Role-based security permissions se basan en el hecho de si una entidad de seguridad tiene una
identidad especificada o si es miembro de una función especificada. La clase PrincipalPermission permite
comprobar los permisos declarativos e imperativos con la entidad de seguridad activa.
Para determinar si el código tiene autorización para el acceso a un recurso o para ejecutar una operación, el
sistema de seguridad en tiempo de ejecución atraviesa la pila de llamadas y compara los permisos concedidos a
cada llamador con el permiso que se exige. Si algún llamador de la pila de llamadas no tiene el permiso exigido, se
iniciará clase SecurityException y se rechazará el acceso.
Solicitar permisos
La finalidad de solicitar permisos es informar al motor en tiempo de ejecución de los permisos que necesita la
aplicación para ejecutarse y garantizar que solo recibe los permisos que realmente necesita. Por ejemplo, si la
aplicación necesita acceso de escritura al disco local, requiere FileIOPermission. Si este permiso no se ha
concedido, se producirán errores en la aplicación al intentar escribir en el disco. Sin embargo, si la aplicación
solicita FileIOPermission y el permiso no se concede, generará la excepción al comienzo y no se cargará.
En un escenario donde la aplicación solo necesita leer datos del disco, puede solicitar que nunca se le concedan
permisos de escritura. En el caso de se produzca un error o un ataque malintencionado, el código no podrá dañar
los datos con los que trabaja. Para obtener más información, consulte Solicitud de permisos.

Seguridad basada en roles y CAS


La implementación de la seguridad basada en roles y de la seguridad de acceso del código (CAS) mejora la
seguridad global de la aplicación. La seguridad basada en roles se puede basar en una cuenta de Windows o en
una identidad personalizada, de forma que la información sobre la entidad de seguridad esté disponible en el
subproceso actual. Además, a menudo se requiere a la aplicaciones que proporcionen acceso a datos o recursos
basándose en credenciales proporcionadas por el usuario. Normalmente, dichas aplicaciones comprueban la
función de un usuario y proporcionan acceso a los recursos basándose en dichos roles.
La seguridad basada en roles permite que un componente identifique los usuarios actuales y sus roles asociados
en tiempo de ejecución. Esta información se asigna a continuación mediante una directiva CAS para determinar el
conjunto de permisos que se conceden en tiempo de ejecución. En un dominio de aplicación especificado, el host
puede cambiar la directiva de seguridad predeterminada basada en roles y establecer una entidad de seguridad
que represente a un usuario y a los roles asociados al usuario.
CLR usa permisos para implementar su mecanismo a fin de aplicar restricciones en el código administrado. Los
permisos de seguridad basada en roles proporcionan un mecanismo para descubrir si un usuario (o el agente que
actúa en su nombre) tiene una identidad concreta o es miembro de una función especificada. Para obtener más
información, consulte Permisosde seguridad .
En función del tipo de aplicación que cree, deberá considerar también la posibilidad de implementar permisos
basados en roles en la base de datos. Para obtener más información sobre la seguridad basada en roles en SQL
Server, vea Seguridad de SQL Server.

Ensamblados
Los ensamblados componen la unidad fundamental de implementación, control de versiones, reutilización, ámbito
de activación y permisos de seguridad en una aplicación de .NET Framework. Un ensamblado proporciona una
colección de tipos y recursos creados para funcionar en conjunto y formar una unidad lógica de funcionalidad. En
CLR, un tipo no existe si no es en el contexto de un ensamblado. Para obtener más información sobre cómo crear e
implementar ensamblados, vea Programación con ensamblados.
Ensamblados con nombre seguro
Un nombre seguro, o firma digital, consta de la identidad del ensamblado, que incluye su nombre de texto sencillo,
el número de versión, la información de referencia cultural (si se proporciona), así como una clave pública y una
firma digital. La firma digital se genera a partir de un archivo de ensamblado que usa la clave privada
correspondiente. El archivo de ensamblado contiene el manifiesto del ensamblado, que contiene los nombres y
códigos hash de todos los archivos que forman el ensamblado.
La asignación de un nombre seguro al ensamblado proporciona a una aplicación o a un componente una
identidad única que puede usar otro software para referirse a ella de manera explícita. La asignación de nombres
seguros protege a los ensamblados de la suplantación por parte de un ensamblado que contenga código
malintencionado. También garantiza la coherencia entre las diferentes versiones de un componente. Debe usar
nombres seguros en los ensamblados que se van a implementar en la caché global de ensamblados (GAC). Para
obtener más información, vea Crear y utilizar ensamblados con nombre seguro.

Confianza parcial en ADO.NET 2.0


En ADO.NET 2.0, se pueden ejecutar los proveedores de datos .NET Framework para SQL Server, OLE DB, ODBC y
para Oracle en entornos de confianza parcial. En versiones anteriores de .NET Framework, solo se admitía el uso
de System.Data.SqlClient en aplicaciones que no fuesen de plena confianza.
Una aplicación de confianza parcial que utilice el proveedor de SQL Server debe tener como mínimo permisos de
ejecución y SqlClientPermission.
Propiedades de atributos de permiso de confianza parcial
En las situaciones de confianza parcial, se puede aplicar SqlClientPermissionAttribute a los miembros a fin de
restringir aún más las capacidades disponibles para el proveedor de datos .NET Framework para SQL Server.
En la siguiente tabla se muestran y se describen las propiedades SqlClientPermissionAttribute disponibles:

P RO P IEDA D DE AT RIB UTO DE P ERM ISO DESC RIP C IÓ N

Action Obtiene o establece una acción de seguridad. Se hereda de


SecurityAttribute.

AllowBlankPassword Habilita o deshabilita el uso de una contraseña en blanco en


una cadena de conexión. Los valores válidos son true (para
habilitar el uso de contraseñas en blanco) y false (para
deshabilitarlo). Se hereda de DBDataPermissionAttribute.

ConnectionString Identifica una cadena de conexión admitida. Se pueden


identificar varias cadenas de conexión. Nota: No incluya un ID
de usuario o una contraseña en la cadena de conexión. En
esta versión no se pueden modificar las restricciones de las
cadenas de conexión mediante la herramienta Configuración
de .NET Framework.

Se hereda de DBDataPermissionAttribute.

KeyRestrictions Identifica qué parámetros de las cadenas de conexión están


permitidos o no lo están. Los parámetros de la cadena de
conexión se identifican en el nombre del * <parámetro*de
formulario>. Se pueden especificar varios parámetros
separados por punto y coma (;). Nota: Si no especifica
KeyRestrictions , pero KeyRestrictionBehavior
establece AllowOnly PreventUsage la propiedad en o , no
se permiten parámetros de cadena de conexión adicionales. Se
hereda de DBDataPermissionAttribute.

KeyRestrictionBehavior Identifica los parámetros de cadenas de conexión como los


únicos parámetros adicionales permitidos ( AllowOnly ) o
bien identifica los parámetros adicionales no permitidos (
PreventUsage ). AllowOnly es el valor predeterminado. Se
hereda de DBDataPermissionAttribute.

TypeID Obtiene un identificador único para el atributo cuando se


implementa en una clase derivada. Se hereda de Attribute.
P RO P IEDA D DE AT RIB UTO DE P ERM ISO DESC RIP C IÓ N

Unrestricted Indica si se declaran permisos protegidos para el origen. Se


hereda de SecurityAttribute.

Sintaxis de ConnectionString
En el siguiente ejemplo se muestra cómo se utiliza el elemento connectionStrings de un archivo de configuración
para permitir únicamente el uso de una determinada cadena de conexión. Consulte Cadenas de conexión para
obtener más información sobre cómo almacenar y recuperar cadenas de conexión de archivos de configuración.

<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;" />
</connectionStrings>

Sintaxis de KeyRestrictions
En el ejemplo siguiente se habilita la Encrypt misma Packet Size cadena de conexión, se habilita el uso de las
opciones de cadena de conexión y, pero se restringe el uso de cualquier otra opción de cadena de conexión.

<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

Sintaxis de KeyRestrictionBehavior con PreventUsage


En el ejemplo siguiente se habilita la misma cadena de conexión y se permiten todos los demás parámetros de
conexión, excepto User Id , Password y Persist Security Info .

<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="User Id=;Password=;Persist Security Info=;"
KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>

Sintaxis de KeyRestrictionBehavior con AllowOnly


En el ejemplo siguiente se habilitan dos cadenas de conexión que contienen los parámetros Initial Catalog ,
Connection Timeout , Encrypt y Packet Size . El resto de los parámetros de cadenas de conexión están
restringidos.
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="Initial Catalog;Connection Timeout=;
Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />

<add name="DatabaseConnection2"
connectionString="Data Source=SqlServer2;Initial
Catalog=Northwind2;Integrated Security=true;"
KeyRestrictions="Initial Catalog;Connection Timeout=;
Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

Habilitar confianza parcial con un conjunto de permisos personalizados


Para habilitar el uso de permisos System.Data.SqlClient para una zona determinada, un administrador del sistema
debe crear un conjunto de permisos personalizados y establecerlo como el conjunto de permisos de dicha zona.
Los conjuntos de permisos predeterminados, como LocalIntranet , no se pueden modificar. Por ejemplo,
System.Data.SqlClient para incluir permisos Zone LocalIntranet para el código que tiene LocalIntranet un
archivo de , un administrador System.Data.SqlClient del sistema podría copiar el conjunto de permisos para ,
cambiarle el nombre a LocalIntranet_Zone "CustomLocalIntranet", agregar los permisos, importar el conjunto de
permisos CustomLocalIntranet mediante Caspol.exe (Herramientade directiva de seguridad de acceso de código) y
establecer el conjunto de permisos de CustomLocalIntranet.
Conjunto de permisos de ejemplo
A continuación se muestra un ejemplo de un conjunto de permisos para el proveedor de datos .NET Framework
para SQL Server en un escenario que no es de plena confianza. Para obtener información sobre la creación de
conjuntos de permisos personalizados, consulte Configuración de conjuntos de permisos mediante Caspol.exe.

<PermissionSet class="System.Security.NamedPermissionSet"
version="1"
Name="CustomLocalIntranet"
Description="Custom permission set given to applications on
the local intranet">

<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0000.0,


Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Integrated Security=true;"
KeyRestrictions="Initial Catalog=;Connection Timeout=;
Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</IPermission>
</PermissionSet>

Comprobar el acceso a código de ADO.NET mediante permisos de


seguridad
En las situaciones de confianza parcial, puede solicitar privilegios de seguridad de acceso del código para
determinados métodos en el código mediante la especificación de SqlClientPermissionAttribute. Si la directiva de
seguridad restringida no permite el privilegio, se inicia una excepción antes de ejecutarse el código. Para obtener
más información sobre la directiva de seguridad, consulte Administración de directivas de seguridad y prácticas
recomendadasde directivas de seguridad .
Ejemplo
En el siguiente ejemplo se muestra cómo escribir código que requiera una determinada cadena de conexión.
Simula la denegación de permisos sin restricciones para System.Data.SqlClient, que podría implementar un
administrador del sistema en una situación real mediante una directiva de seguridad CAS.

IMPORTANT
Al diseñar permisos de seguridad CAS en ADO.NET, el procedimiento correcto es comenzar con el caso más restrictivo (sin
ningún permiso) y agregar a continuación los permisos específicos necesarios para la tarea determinada que el código debe
realizar. El patrón opuesto, que comienza con todos los permisos y deniega a continuación un permiso concreto, no es
seguro puesto que existen muchas formas de expresar la misma cadena de conexión. Por ejemplo, si comienza con todos los
permisos y después intenta denegar el uso de la cadena de conexión "servidor=unServidor", la cadena
"servidor=unServidor.miEmpresa.com" seguirá obteniendo permiso. Al comenzar siempre por no conceder ningún permiso,
se reduce la posibilidad de que haya lagunas en el conjunto de permisos.

En el siguiente código se muestra cómo SqlClient realiza la petición de seguridad, que inicia SecurityException si
los permisos de seguridad CAS adecuados no se encuentran en su lugar. El resultado SecurityException se muestra
en la ventana de la consola.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Security;
using System.Security.Permissions;

namespace PartialTrustTopic {
public class PartialTrustHelper : MarshalByRefObject {
public void TestConnectionOpen(string connectionString) {
// Try to open a connection.
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
}
}
}

class Program {
static void Main(string[] args) {
TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated
Security=true;Initial Catalog=Test");
}

static void TestCAS(string connectString1, string connectString2) {


// Create permission set for sandbox AppDomain.
// This example only allows execution.
PermissionSet permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

// Create sandbox AppDomain with permission set that only allows execution,
// and has no SqlClientPermissions.
AppDomainSetup appDomainSetup = new AppDomainSetup();
appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
AppDomain firstDomain = AppDomain.CreateDomain("NoSqlPermissions", null, appDomainSetup,
permissions);

// Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
Type helperType = typeof(PartialTrustHelper);
PartialTrustHelper firstHelper =
(PartialTrustHelper)firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);

try {
// Attempt to open a connection in the sandbox AppDomain.
// This is expected to fail.
firstHelper.TestConnectionOpen(connectString1);
firstHelper.TestConnectionOpen(connectString1);
Console.WriteLine("Connection opened, unexpected.");
}
catch (System.Security.SecurityException ex) {
Console.WriteLine("Failed, as expected: {0}",
ex.FirstPermissionThatFailed);

// Uncomment the following line to see Exception details.


// Console.WriteLine("BaseException: " + ex.GetBaseException());
}

// Add permission for a specific connection string.


SqlClientPermission sqlPermission = new SqlClientPermission(PermissionState.None);
sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);

permissions.AddPermission(sqlPermission);

AppDomain secondDomain = AppDomain.CreateDomain("OneSqlPermission", null, appDomainSetup,


permissions);
PartialTrustHelper secondHelper =
(PartialTrustHelper)secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);

// Try connection open again, it should succeed now.


try {
secondHelper.TestConnectionOpen(connectString1);
Console.WriteLine("Connection opened, as expected.");
}
catch (System.Security.SecurityException ex) {
Console.WriteLine("Unexpected failure: {0}", ex.Message);
}

// Try a different connection string. This should fail.


try {
secondHelper.TestConnectionOpen(connectString2);
Console.WriteLine("Connection opened, unexpected.");
}
catch (System.Security.SecurityException ex) {
Console.WriteLine("Failed, as expected: {0}", ex.Message);
}
}
}
}

Imports System.Data
Imports System.Data.SqlClient
Imports System.Security
Imports System.Security.Permissions

Namespace PartialTrustTopic
Public Class PartialTrustHelper
Inherits MarshalByRefObject
Public Sub TestConnectionOpen(ByVal connectionString As String)
' Try to open a connection.
Using connection As New SqlConnection(connectionString)
connection.Open()
End Using
End Sub
End Class

Class Program
Public Shared Sub Main(ByVal args As String())
TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated
Security=true;Initial Catalog=Test")
End Sub

Public Shared Sub TestCAS(ByVal connectString1 As String, ByVal connectString2 As String)


' Create permission set for sandbox AppDomain.
' This example only allows execution.
' This example only allows execution.
Dim permissions As New PermissionSet(PermissionState.None)
permissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))

' Create sandbox AppDomain with permission set that only allows execution,
' and has no SqlClientPermissions.
Dim appDomainSetup As New AppDomainSetup()
appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
Dim firstDomain As AppDomain = AppDomain.CreateDomain("NoSqlPermissions", Nothing, appDomainSetup,
permissions)

' Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
Dim helperType As Type = GetType(PartialTrustHelper)
Dim firstHelper As PartialTrustHelper =
DirectCast(firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName),
PartialTrustHelper)

Try
' Attempt to open a connection in the sandbox AppDomain.
' This is expected to fail.
firstHelper.TestConnectionOpen(connectString1)
Console.WriteLine("Connection opened, unexpected.")
Catch ex As System.Security.SecurityException

' Uncomment the following line to see Exception details.


' Console.WriteLine("BaseException: " + ex.GetBaseException());
Console.WriteLine("Failed, as expected: {0}", ex.FirstPermissionThatFailed)
End Try

' Add permission for a specific connection string.


Dim sqlPermission As New SqlClientPermission(PermissionState.None)
sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly)

permissions.AddPermission(sqlPermission)

Dim secondDomain As AppDomain = AppDomain.CreateDomain("OneSqlPermission", Nothing, appDomainSetup,


permissions)
Dim secondHelper As PartialTrustHelper =
DirectCast(secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName),
PartialTrustHelper)

' Try connection open again, it should succeed now.


Try
secondHelper.TestConnectionOpen(connectString1)
Console.WriteLine("Connection opened, as expected.")
Catch ex As System.Security.SecurityException
Console.WriteLine("Unexpected failure: {0}", ex.Message)
End Try

' Try a different connection string. This should fail.


Try
secondHelper.TestConnectionOpen(connectString2)
Console.WriteLine("Connection opened, unexpected.")
Catch ex As System.Security.SecurityException
Console.WriteLine("Failed, as expected: {0}", ex.Message)
End Try
End Sub
End Class
End Namespace

Debe poder ver este resultado en la ventana de la consola:


Failed, as expected: <IPermission class="System.Data.SqlClient.
SqlClientPermission, System.Data, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Initial Catalog=
Northwind;Integrated Security=SSPI" KeyRestrictions=""
KeyRestrictionBehavior="AllowOnly"/>
</IPermission>

Connection opened, as expected.


Failed, as expected: Request failed.

Interoperabilidad de código no administrado


El código que se ejecuta fuera de CLR se denomina código no administrado. Por lo tanto, los mecanismos de
seguridad como CAS no se pueden aplicar en código no administrado. Los componentes COM, las interfaces
ActiveX y las funciones de la API de Windows son ejemplos de código no administrado. Cuando se ejecuta código
no administrado se aplican consideraciones de seguridad especiales, de forma que no se ponga en peligro la
seguridad global de la aplicación. Para más información, consulte Interoperating with Unmanaged Code
(Interoperar con código no administrado)
.NET Framework también es compatible con versiones anteriores de componentes COM existentes mediante el
acceso a través de la interoperabilidad COM. Se pueden incluir componentes COM en una aplicación de .NET
Framework usando las herramientas de la interoperabilidad COM para importar los tipos COM necesarios. Una
vez que se han importado, los tipos COM están listos para su uso. La interoperabilidad COM también permite que
los clientes COM tengan acceso a código administrado mediante la exportación de metadatos de ensamblado a
una biblioteca de tipos y mediante el registro del componente administrado como un componente COM. Para
obtener más información, consulte Interoperabilidad COM avanzada.

Consulte también
Proteger aplicaciones de ADO.NET
Seguridad del código nativo y del código de .NET Framework
Seguridad basada en roles
Información general de ADO.NET
Privacidad y seguridad de datos
08/01/2020 • 5 minutes to read • Edit Online

La protección y la administración de información confidencial en una aplicación de ADO.NET depende de los


productos y las tecnologías utilizados para crearla. ADO.NET no proporciona de forma directa servicios para
proteger ni cifrar datos.

Criptografía y códigos hash


Las clases del espacio de nombres System.Security.Cryptography de .NET Framework se pueden utilizar desde las
aplicaciones de ADO.NET para impedir que terceras partes no autorizadas lean o modifiquen los datos. Algunas
clases son contenedores para Microsoft CryptoAPI no administrado, mientras que otras son implementaciones
administradas. En el tema servicios criptográficos se proporciona información general sobre la criptografía en el
.NET Framework, se describe cómo se implementa la criptografía y cómo se pueden realizar tareas criptográficas
específicas.
Al contrario que la criptografía, que permite cifrar datos y descifrarlos posteriormente, el proceso hash de datos es
unidireccional. La utilización de algoritmos hash en los datos resulta útil para evitar la manipulación mediante la
comprobación de que los datos no han sido alterados: para cadenas de entrada idénticas, los algoritmos hash
siempre generan valores de salida cortos idénticos que se pueden comparar fácilmente. Garantizar la integridad de
los datos con códigos hash describe cómo se pueden generar y comprobar los valores hash.

Cifrar archivos de configuración


La protección del acceso al origen de datos es uno de los objetivos más importantes a la hora de proteger una
aplicación. Una cadena de conexión presenta una vulnerabilidad potencial si no está protegida. Las cadenas de
conexión que se guardan en los archivos de configuración se almacenan en archivos XML estándar para los que
.NET Framework ha definido un conjunto común de elementos. La configuración protegida permite cifrar
información confidencial en un archivo de configuración. Si bien se ha diseñado principalmente para aplicaciones
ASP.NET, la configuración protegida también se puede usar para cifrar secciones del archivo de configuración en
aplicaciones Windows. Para más información, consulte Proteger la información de conexión.

Proteger valores de cadena en memoria


Si un objeto String contiene información confidencial, como una contraseña, un número de tarjeta de crédito o
datos personales, existe el riesgo de que la información se pueda revelar una vez utilizada, porque la aplicación no
puede eliminar los datos de la memoria del equipo.
Un objeto String es inmutable, porque no se puede modificar su valor una vez que se ha creado. Los cambios que
parecen modificar el valor de la cadena crean de hecho una nueva instancia de un objeto String en memoria, que
almacena los datos como texto sin formato. Además, no es posible predecir si las instancias de cadena se
eliminarán de la memoria. La reclamación de memoria con cadenas no es determinista en la recolección de
elementos no utilizados de .NET. Debe evitar el uso de las clases String y StringBuilder si los datos son realmente
confidenciales.
La clase SecureString proporciona métodos para cifrar texto con la API de protección de datos (DPAPI) en memoria.
La cadena se elimina posteriormente de la memoria cuando ya no es necesaria. No existe ningún método
ToString para leer rápidamente el contenido de un objeto SecureString. Puede inicializar una nueva instancia de
SecureString sin ningún valor o pasándole un nuevo puntero a una matriz de objetos Char. A continuación, puede
usar los diferentes métodos de la clase para trabajar con la cadena.
Vea también
Proteger aplicaciones de ADO.NET
Seguridad de SQL Server
Información general sobre ADO.NET
Asignaciones de tipos de datos en ADO.NET
05/02/2020 • 4 minutes to read • Edit Online

.NET Framework se basa en el sistema de tipos común, que define cómo se declaran, usan y administran los tipos
en tiempo de ejecución. Consta de tipos de valor y de tipos de referencia, que derivan todos del tipo base Object.
Al trabajar con un origen de datos, el tipo de datos se deduce del proveedor de datos si no se especifica
explícitamente. Por ejemplo, un objeto DataSet es independiente de cualquier origen de datos específico. Los
datos de DataSet se recuperan desde un origen de datos y los cambios que se realizan en ellos se reflejan en el
origen de datos mediante el uso de DataAdapter . Esto significa que cuando un DataAdapter rellena un DataTable
en un DataSet con valores de un origen de datos, los tipos de datos resultantes de las columnas del DataTable
son .NET Framework tipos, en lugar de tipos específicos del proveedor de datos .NET Framework que se utiliza
para conectarse al origen de datos.
Del mismo modo, cuando un DataReader devuelve un valor de un origen de datos, el valor resultante se
almacena en una variable local que tiene un tipo .NET Framework. En el caso de las operaciones Fill del
DataAdapter y de los métodos Get del DataReader , el tipo de .NET Framework se deduce del valor devuelto del
proveedor de datos .NET Framework.
En lugar de confiar en el tipo de datos deducido, puede utilizar los métodos de descriptor de acceso con tipo de
DataReader cuando conoce el tipo específico del valor que se va a devolver. Los métodos de descriptor de acceso
con tipo proporcionan un mejor rendimiento al devolver un valor como un tipo de .NET Framework específico, lo
que elimina la necesidad de conversión de tipos adicional.

NOTE
Los valores NULL de los tipos de datos del proveedor de datos de .NET Framework se representan mediante
DBNull.Value .

Esta sección
Asignaciones de tipos de datos de SQL Server
Enumera las asignaciones de tipos de datos deducidas y los métodos de descriptor de acceso a datos de
System.Data.SqlClient.
Asignaciones de tipos de datos de OLE DB
Enumera las asignaciones de tipos de datos deducidas y los métodos de descriptor de acceso a datos de
System.Data.OleDb.
Asignaciones de tipos de datos de ODBC
Enumera las asignaciones de tipos de datos deducidas y los métodos de descriptor de acceso a datos de
System.Data.Odbc.
Asignaciones de tipos de datos de Oracle
Enumera las asignaciones de tipos de datos deducidas y los métodos de descriptor de acceso a datos de
System.Data.OracleClient.
Números de punto flotante
Describe los problemas que con frecuencia se encuentran los programadores al trabajar con números de punto
flotante.
Vea también
Tipos de datos de SQL Server y ADO.NET
Configuración de parámetros y tipos de datos de parámetros
Recuperación de información del esquema de la base de datos
Sistema de tipos comunes
Convertir tipos
Información general sobre ADO.NET
Asignaciones de tipos de datos de SQL Server
23/10/2019 • 3 minutes to read • Edit Online

SQL Server y .NET Framework se basan en sistemas de tipos distintos. Por ejemplo, la estructura Decimal de .NET
Framework tiene una escala máxima de 28, mientras que los tipos de datos decimal y numérico de SQL Server
tienen una escala máxima de 38. Para mantener la integridad de los datos al leer y escribir datos, SqlDataReader
expone métodos de descriptores de acceso con tipo específicos de SQL Server que devuelven objetos de
System.Data.SqlTypes así como métodos de descriptores de acceso que devuelven tipos de .NET Framework. Los
tipos de SQL Server y los de .NET Framework se representan también mediante enumeraciones en las clases
DbType y SqlDbType, que puede usar al especificar los tipos de datos SqlParameter.
En la tabla siguiente se muestra el tipo de .NET Framework deducido DbType , SqlDbType las enumeraciones y y
SqlDataReaderlos métodos de descriptor de acceso para.

DESC RIP TO R DE DESC RIP TO R DE


T IP O DE M OTO R A C C ESO C O N A C C ESO C O N
DE B A SE DE T IP O T IP O
DATO S SQ L T IP O . N ET EN UM ERA C IÓ N SQ L DATA REA DER EN UM ERA C IÓ N SQ L DATA REA DER
SERVER F RA M EW O RK SQ L DBT Y P E SQ LT Y P ES DBT Y P E DBT Y P E

bigint Int64 BigInt GetSqlInt64 Int64 GetInt64

binary Byte[] VarBinary GetSqlBinary Binary GetBytes

bit Boolean Bit GetSqlBoolean Boolean GetBoolean

char string Char GetSqlString AnsiStringFixedLe GetString


ngth,
Char[] GetChars
String

fecha 1 DateTime Date 1 GetSqlDateTime Date 1 GetDateTime

(SQL Server 2008


y posteriores)

Datetime DateTime DateTime GetSqlDateTime DateTime GetDateTime

datetime2 DateTime DateTime2 None DateTime2 GetDateTime

(SQL Server 2008


y posteriores)

datetimeoffset DateTimeOffset DateTimeOffset ninguna DateTimeOffset GetDateTimeOffs


et
(SQL Server 2008
y posteriores)

decimal Decimal Decimal GetSqlDecimal Decimal GetDecimal

FILESTREAM Byte[] VarBinary GetSqlBytes Binary GetBytes


attribute
(varbinary(max))
DESC RIP TO R DE DESC RIP TO R DE
T IP O DE M OTO R A C C ESO C O N A C C ESO C O N
DE B A SE DE T IP O T IP O
DATO S SQ L T IP O . N ET EN UM ERA C IÓ N SQ L DATA REA DER EN UM ERA C IÓ N SQ L DATA REA DER
SERVER F RA M EW O RK SQ L DBT Y P E SQ LT Y P ES DBT Y P E DBT Y P E

float Double Float GetSqlDouble Double GetDouble

image Byte[] Binary GetSqlBinary Binary GetBytes

int Int32 Int GetSqlInt32 Int32 GetInt32

money Decimal Money GetSqlMoney Decimal GetDecimal

nchar string NChar GetSqlString StringFixedLength GetString

Char[] GetChars

ntext string NText GetSqlString String GetString

Char[] GetChars

numeric Decimal Decimal GetSqlDecimal Decimal GetDecimal

nvarchar string NVarChar GetSqlString String GetString

Char[] GetChars

real Single Real GetSqlSingle Single GetFloat

rowversion Byte[] Timestamp GetSqlBinary Binary GetBytes

smalldatetime DateTime DateTime GetSqlDateTime DateTime GetDateTime

smallint Int16 SmallInt GetSqlInt16 Int16 GetInt16

smallmoney Decimal SmallMoney GetSqlMoney Decimal GetDecimal

sql_variant Objeto 2 Variant GetSqlValue 2 Object GetValue 2

text string Text GetSqlString String GetString

Char[] GetChars

time TimeSpan Time ninguna Time GetDateTime

(SQL Server 2008


y posteriores)

timestamp Byte[] Timestamp GetSqlBinary Binary GetBytes

tinyint Byte TinyInt GetSqlByte Byte GetByte

uniqueidentifier Guid UniqueIdentifier GetSqlGuid Guid GetGuid

varbinary Byte[] VarBinary GetSqlBinary Binary GetBytes


DESC RIP TO R DE DESC RIP TO R DE
T IP O DE M OTO R A C C ESO C O N A C C ESO C O N
DE B A SE DE T IP O T IP O
DATO S SQ L T IP O . N ET EN UM ERA C IÓ N SQ L DATA REA DER EN UM ERA C IÓ N SQ L DATA REA DER
SERVER F RA M EW O RK SQ L DBT Y P E SQ LT Y P ES DBT Y P E DBT Y P E

varchar string VarChar GetSqlString AnsiString, String GetString

Char[] GetChars

xml xml Xml GetSqlXml Xml ninguna

1 no se puede establecer DbType la propiedad de SqlParameter en SqlDbType.Date .


2 use un descriptor de acceso con tipo específico si conoce el tipo sql_variant subyacente de.

SQL Server documentación


Para obtener más información sobre los tipos de datos de SQL Server, vea tipos de datos (Transact-SQL).

Vea también
Tipos de datos de SQL Server y ADO.NET
Datos binarios y datos de valores grandes de SQL Server
Asignaciones de tipos de datos en ADO.NET
Configuración de parámetros y tipos de datos de parámetros
Información general sobre ADO.NET
Asignaciones de tipos de datos de OLE DB
23/10/2019 • 2 minutes to read • Edit Online

En la tabla siguiente se muestra el tipo de .NET Framework deducido para los tipos de datos del proveedor de
datos .NET Framework paraSystem.Data.OleDbADO y OLE DB (). También OleDbDataReader se enumeran los
métodos de descriptor de acceso con tipo para.

. N ET F RA M EW O RK
DESC RIP TO R DE A C C ESO
T IP O DE A DO T IP O DE O L E DB T IP O . N ET F RA M EW O RK C O N T IP O

adBigInt DBTYPE_I8 Int64 GetInt64()

adBinary DBTYPE_BYTES Byte[] GetBytes()

adBoolean DBTYPE_BOOL Boolean GetBoolean()

adBSTR DBTYPE_BSTR string GetString()

adChapter DBTYPE_HCHAPTER Compatible con GetValue()


DataReader . Consulte
recuperación de datos
mediante DataReader.

adChar DBTYPE_STR string GetString()

adCurrency DBTYPE_CY Decimal GetDecimal()

adDate DBTYPE_DATE DateTime GetDateTime()

adDBDate DBTYPE_DBDATE DateTime GetDateTime()

adDBTime DBTYPE_DBTIME DateTime GetDateTime()

adDBTimeStamp DBTYPE_DBTIMESTAMP DateTime GetDateTime()

adDecimal DBTYPE_DECIMAL Decimal GetDecimal()

adDouble DBTYPE_R8 Double GetDouble()

adError DBTYPE_ERROR ExternalException GetValue()

adFileTime DBTYPE_FILETIME DateTime GetDateTime()

adGUID DBTYPE_GUID Guid GetGuid()

adIDispatch DBTYPE_IDISPATCH * Object GetValue()

adInteger DBTYPE_I4 Int32 GetInt32()


. N ET F RA M EW O RK
DESC RIP TO R DE A C C ESO
T IP O DE A DO T IP O DE O L E DB T IP O . N ET F RA M EW O RK C O N T IP O

adIUnknown DBTYPE_IUNKNOWN * Object GetValue()

adNumeric DBTYPE_NUMERIC Decimal GetDecimal()

adPropVariant DBTYPE_PROPVARIANT Object GetValue()

adSingle DBTYPE_R4 Single GetFloat()

adSmallInt DBTYPE_I2 Int16 GetInt16()

adTinyInt DBTYPE_I1 Byte GetByte()

adUnsignedBigInt DBTYPE_UI8 UInt64 GetValue()

adUnsignedInt DBTYPE_UI4 UInt32 GetValue()

adUnsignedSmallInt DBTYPE_UI2 UInt16 GetValue()

adUnsignedTinyInt DBTYPE_UI1 Byte GetByte()

adVariant DBTYPE_VARIANT Object GetValue()

adWChar DBTYPE_WSTR string GetString()

adUserDefined DBTYPE_UDT no admitido

adVarNumeric DBTYPE_VARNUMERIC no admitido

*En el caso de DBTYPE_IUNKNOWN los DBTYPE_IDISPATCH tipos de OLE DB y, la referencia de objeto es una
representación de cálculo de referencias del puntero.

Vea también
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
Asignaciones de tipos de datos de ODBC
23/10/2019 • 2 minutes to read • Edit Online

En la tabla siguiente se muestra el tipo de .NET Framework deducido para los tipos de datos del proveedor de
datosSystem.Data.Odbcde .NET Framework para ODBC (). También OdbcDataReader se enumeran los métodos de
descriptor de acceso con tipo para.

. N ET F RA M EW O RK DESC RIP TO R DE
T IP O DE O DB C T IP O . N ET F RA M EW O RK A C C ESO C O N T IP O

SQL_BIGINT Int64 GetInt64()

SQL_BINARY Byte[] GetBytes()

SQL_BIT Boolean GetBoolean()

SQL_CHAR string GetString()

Char[] GetChars()

SQL_DECIMAL Decimal GetDecimal()

SQL_DOUBLE Double GetDouble()

SQL_GUID Guid GetGuid()

SQL_INTEGER Int32 GetInt32()

SQL_LONG_VARCHAR string GetString()

Char[] GetChars()

SQL_LONGVARBINARY Byte[] GetBytes()

SQL_NUMERIC Decimal GetDecimal()

SQL_REAL Single GetFloat()

SQL_SMALLINT Int16 GetInt16()

SQL_TINYINT Byte GetByte()

SQL_TYPE_TIMES DateTime GetDateTime()

SQL_TYPE_TIMESTAMP DateTime GetDateTime()

SQL_VARBINARY Byte[] GetBytes()


. N ET F RA M EW O RK DESC RIP TO R DE
T IP O DE O DB C T IP O . N ET F RA M EW O RK A C C ESO C O N T IP O

SQL_WCHAR string GetString()

Char[] GetChars()

SQL_WLONGVARCHAR string GetString()

Char[] GetChars()

SQL_WVARCHAR string GetString()

Char[] GetChars()

Vea también
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
Asignaciones de tipos de datos de Oracle
23/10/2019 • 6 minutes to read • Edit Online

En la siguiente tabla se enumeran los tipos de datos de Oracle y sus asignaciones al OracleDataReader.

T IP O DE DATO S
T IP O DE DATO S DE . N ET O RA C L EC L IEN T DEVUELTO
F RA M EW O RK DEVUELTO P O R POR
O RA C L EDATA REA DER. GET VA O RA C L EDATA REA DER. GETO R
T IP O DE DATO S DE O RA C L E L UE A C L EVA L UE C O M EN TA RIO S

SOBRE Byte[] OracleBFile

BLOB Byte[] OracleLob

CHAR String OracleString

CLOB String OracleLob

DATE DateTime OracleDateTime

FLOAT Decimal OracleNumber Este tipo de datos es un alias


para el tipo de datos
Number y está diseñado
para que OracleDataReader
devuelva un valor System.
decimal o OracleNumber en
lugar de un valor de punto
flotante. El uso del tipo de
datos de .NET Framework
puede ocasionar un
desbordamiento.

INTEGER Decimal OracleNumber Este tipo de datos es un alias


para el tipo de datos
Number (38) y está
diseñado para que
OracleDataReader devuelva
un valor System. decimal o
OracleNumber en lugar de
un valor entero. El uso del
tipo de datos de .NET
Framework puede ocasionar
un desbordamiento.

INTERVALO AÑO A MES Int32 OracleMonthSpan

INTERVALO DE DÍA A TimeSpan OracleTimeSpan


SEGUNDO

LONG String OracleString

LONG RAW Byte[] OracleBinary


T IP O DE DATO S
T IP O DE DATO S DE . N ET O RA C L EC L IEN T DEVUELTO
F RA M EW O RK DEVUELTO P O R POR
O RA C L EDATA REA DER. GET VA O RA C L EDATA REA DER. GETO R
T IP O DE DATO S DE O RA C L E L UE A C L EVA L UE C O M EN TA RIO S

NCHAR String OracleString

NCLOB String OracleLob

NÚMEROS Decimal OracleNumber El uso del tipo de datos de


.NET Framework puede
ocasionar un
desbordamiento.

NVARCHAR2 String OracleString

RAW Byte[] OracleBinary

REF CURSOR El tipo de datos ref cursor


de Oracle no es compatible
OracleDataReader con el
objeto.

ROWID String OracleString

INDICACIONES DateTime OracleDateTime

MARCA DE TIEMPO CON DateTime OracleDateTime


ZONA HORARIA LOCAL

MARCA DE TIEMPO CON DateTime OracleDateTime


ZONA HORARIA

ENTERO SIN SIGNO Número OracleNumber Este tipo de datos es un alias


para el tipo de datos
Number (38) y está
diseñado para que
OracleDataReader devuelva
un valor System. decimal o
OracleNumber en lugar de
un valor entero sin signo. El
uso del tipo de datos de
.NET Framework puede
ocasionar un
desbordamiento.

VARCHAR2 String OracleString

En la tabla siguiente se enumeran los tipos de datos de Oracle y los tipos de datos .NET Framework
OracleType(System. Data. DbType y) que se van a usar al enlazarlos como parámetros.

EN UM ERA C IÓ N
EN UM ERA C IÓ N DBT Y P E O RA C L ET Y P E PA RA
PA RA EN L A Z A R C O M O UN EN L A Z A R C O M O UN
T IP O DE DATO S DE O RA C L E PA RÁ M ET RO PA RÁ M ET RO C O M EN TA RIO S
EN UM ERA C IÓ N
EN UM ERA C IÓ N DBT Y P E O RA C L ET Y P E PA RA
PA RA EN L A Z A R C O M O UN EN L A Z A R C O M O UN
T IP O DE DATO S DE O RA C L E PA RÁ M ET RO PA RÁ M ET RO C O M EN TA RIO S

SOBRE BFile Oracle solo permite enlazar


un BFILE como parámetro
BFILE . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor noBFILE ,
como Byte [] o
OracleBinary.

BLOB Blob Oracle solo permite enlazar


un BLOB como parámetro
de BLOB . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor que no
esBLOB , como Byte [] o
OracleBinary.

CHAR AnsiStringFixedLength Char

CLOB Clob Oracle solo permite enlazar


un CLOB como parámetro
CLOB . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor que no
esCLOB , como System.
String o OracleString.

DATE DateTime DateTime

FLOAT Single, Double, decimal Float, Double, número Sizedetermina los datos
System. Data. DBType y
OracleType.

INTEGER SByte, Int16, Int32, SByte, Int16, Int32, Sizedetermina los datos
Int64, Decimal Number System. Data. DBType y
OracleType.

INTERVALO AÑO A MES Int32 Inter valYearToMonth OracleType solo está


disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

INTERVALO DE DÍA A Objeto Inter valDayToSecond OracleType solo está


SEGUNDO disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

LONG AnsiString LongVarChar


EN UM ERA C IÓ N
EN UM ERA C IÓ N DBT Y P E O RA C L ET Y P E PA RA
PA RA EN L A Z A R C O M O UN EN L A Z A R C O M O UN
T IP O DE DATO S DE O RA C L E PA RÁ M ET RO PA RÁ M ET RO C O M EN TA RIO S

LONG RAW Binar y LongRaw

NCHAR StringFixedLength NChar

NCLOB NClob Oracle solo permite enlazar


un NCLOB como parámetro
NCLOB . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor que no
seaNCLOB , como System.
String o OracleString.

NÚMEROS VarNumeric Número

NVARCHAR2 String NVarChar

RAW Binar y Socket

REF CURSOR Cursor Para obtener más


información, vea cursores
REF cursor de Oracle.

ROWID AnsiString Rowid

INDICACIONES DateTime Marca de tiempo OracleType solo está


disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

MARCA DE TIEMPO CON DateTime TimestampLocal OracleType solo está


ZONA HORARIA LOCAL disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

MARCA DE TIEMPO CON DateTime TimestampWithTz OracleType solo está


ZONA HORARIA disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

ENTERO SIN SIGNO Byte, UInt16, UInt32, Byte, UInt16, Uint32, Sizedetermina los datos
UInt64, Decimal número System. Data. DBType y
OracleType.

VARCHAR2 AnsiString VarChar

Los Valuevaloresde ParameterDirection , Output y ReturnValue que usa la propiedad del


objetoson.NETFrameworktiposdedatos,amenosqueelvalordeentradaseauntipodedatosdeOracle(paraOracleParamet
er ejemplo, OracleNumber o OracleString). Esto no se aplica a los tipos de datos ref cursor , BFILE o LOB .

Vea también
Oracle y ADO.NET
Información general sobre ADO.NET
Números de punto flotante
23/10/2019 • 3 minutes to read • Edit Online

En este tema se describen algunos de los problemas que los desarrolladores suelen encontrar al trabajar con
números de punto flotante en ADO.NET. Estos problemas se deben a la manera en que los equipos almacenan
números de punto flotante y no son específicos de un proveedor determinado System.Data.SqlClient como
System.Data.OracleCliento.
En general, los números de punto flotante no tienen una representación binaria exacta. En realidad, el equipo
almacena una aproximación del número. En diferentes momentos se pueden utilizar diferentes números de dígitos
binarios para representar el número. Cuando un número de punto flotante se convierte de una representación a
otra, los dígitos menos significativos de dicho número pueden variar ligeramente. Por lo general el cambio se
produce cuando el número se convierte de un tipo a otro. La variación se produce si la conversión se realiza en una
base de datos, entre tipos que representan valores de base de datos o entre tipos. Debido a estos cambios, los
números que lógicamente deberían ser iguales pueden presentar cambios en sus dígitos menos significativos que
hagan que muestren valores diferentes. La cantidad de dígitos de precisión en el número puede ser mayor o
menor de la esperada. Cuando el formato del número cambia a cadena, puede que no muestre el valor esperado.
Para reducir estos efectos al mínimo, debe usar la coincidencia más próxima entre tipos de números que haya
disponible. Por ejemplo, si está trabajando con SQL Server, el valor numérico exacto puede cambiar si convierte un
valor de Transact-SQL de tipo real en un valor de tipo float. En el .NET Framework, la conversión Single de Double a
también puede producir resultados inesperados. En ambos casos, una estrategia adecuada consiste en establecer
que todos los valores de la aplicación usen el mismo tipo numérico. También puede utilizar un tipo de decimal de
precisión fija o bien convertir los números de punto flotante a un tipo decimal de precisión fija antes de trabajar
con ellos.
Para solucionar problemas relacionados con la comparación de igualdad, puede codificar la aplicación de forma
que se pasen por alto las diferentes en los dígitos menos significativos. Por ejemplo, en lugar de comparar dos
números para comprobar si son iguales, puede restar un número del otro. Si la diferencia se sitúa en un margen
aceptable de redondeo, la aplicación puede considerar los números como si fuesen iguales.

Vea también
Por qué los números de punto flotante pierden precisión
Información general sobre ADO.NET
Recuperar y modificar datos en ADO.NET
05/02/2020 • 4 minutes to read • Edit Online

La principal función de cualquier aplicación de base de datos es conectarse a un origen de datos y recuperar los
datos que contiene. Los .NET Framework proveedores de datos de ADO.NET sirven como puente entre una
aplicación y un origen de datos, lo que le permite ejecutar comandos, así como recuperar datos mediante un
DataReader o un DataAdapter . Una función clave de cualquier aplicación de base de datos es la capacidad de
actualizar los datos almacenados en la misma. En ADO.NET, la actualización de datos implica el uso de los
objetos DataAdapter y DataSet, y de los objetos de comando . también puede implicar el uso de
transacciones.

Esta sección
Conexión a un origen de datos
Describe cómo se establece una conexión con un origen de datos y cómo trabajar con eventos de conexión.
Cadenas de conexión
Contiene temas en los que se describen varios aspectos del uso de cadenas de conexión, entre los que se
incluyen las palabras clave de cadena de conexión, información sobre seguridad y el almacenamiento y
recuperación de cadenas de conexión.
Agrupación de conexiones
Describe la agrupación de conexiones para los proveedores de datos .NET Framework.
Comandos y parámetros
Contiene temas en los que se describe cómo crear comandos y generadores de comandos, configurar
parámetros y cómo ejecutar comandos para recuperar y modificar datos.
Objetos DataAdapter y DataReader
Contiene temas en los que se describen DataReaders, DataAdapters, los parámetros, el control de eventos
DataAdapter y la ejecución de operaciones por lotes.
Transacciones y simultaneidad
Contiene temas en los que se describe cómo realizar transacciones locales y transacciones distribuidas, y cómo
trabajar con la simultaneidad optimista.
Recuperación de valores autonuméricos y de identidad
Proporciona un ejemplo de asignación de los valores generados para una columna de identidad en una tabla
de SQL Server o para un campo Autonumber de una tabla de Microsoft Access, a una columna de una fila
insertada en una tabla. Describe la combinación de valores de identidad en una DataTable .
Recuperación de datos binarios
Describe cómo recuperar datos binarios o estructuras de datos de gran tamaño mediante CommandBehavior .
SequentialAccess para modificar el comportamiento predeterminado de un DataReader .

Modificación de datos con procedimientos almacenados


Describe cómo utilizar parámetros de entrada y parámetros de salida de procedimientos almacenados para
insertar una fila en una base de datos y devolver un nuevo valor de identidad.
Recuperación de información del esquema de la base de datos
Describe cómo obtener de un origen de dstos las bases de datos o catálogos disponibles, las tablas y vistas de
una base de datos, las restricciones que existen para las tablas y otra información de esquema.
Objetos DbProviderFactory
Describe el modelo de generador de proveedor y demuestra cómo usar las clases base del espacio de nombres
System.Data.Common .

Traza de datos en ADO.NET


Describe cómo ADO.NET proporciona funcionalidad integrada de traza de datos.
Performance Counters
Describe los contadores de rendimiento disponibles para SqlClient y OracleClient .
Programación asincrónica
Describe la compatibilidad de ADO.NET con la programación asincrónica.
Compatibilidad de streaming de SqlClient
Describe cómo escribir aplicaciones que transmiten datos de SQL Server sin que se carguen por completo en la
memoria.

Vea también
Asignaciones de tipos de datos en ADO.NET
Objetos DataSet, DataTable y DataView
Proteger aplicaciones de ADO.NET
SQL Server y ADO.NET
Información general sobre ADO.NET
Conectarse a un origen de datos en ADO.NET
10/02/2020 • 2 minutes to read • Edit Online

En ADO.NET, se usa un objeto de conexión para conectarse a un origen de datos específico proporcionando la
información de autenticación necesaria en una cadena de conexión. El objeto de conexión que se utiliza depende
del tipo de origen de datos.
Cada proveedor de datos .NET Framework incluye un objeto DbConnection: el proveedor de datos .NET
Framework para OLE DB incluye un objeto OleDbConnection, el proveedor de datos .NET Framework para SQL
Server incluye un objeto SqlConnection, el proveedor de datos .NET Framework para ODBC incluye un objeto
OdbcConnection y el proveedor de datos .NET Framework para Oracle incluye un objeto OracleConnection.

En esta sección
Establecimiento de la conexión
Describe cómo utilizar un objeto de conexión para establecer una conexión a un origen de datos.
Eventos de conexión
Describe cómo usar un evento InfoMessage para recuperar mensajes informativos de un origen de datos.

Consulte también
Cadenas de conexión
Agrupación de conexiones
Comandos y parámetros
Objetos DataAdapter y DataReader
Transacciones y simultaneidad
Información general sobre ADO.NET
Establecer la conexión
21/03/2020 • 10 minutes to read • Edit Online

Para conectarse a Microsoft SQL Server, use el objeto SqlConnection del proveedor de datos .NET Framework para
SQL Server. Para conectarse a un origen de datos OLE DB, use el objeto OleDbConnection del proveedor de datos
.NET Framework para OLE DB. Para conectarse a un origen de datos ODBC, utilice el objeto OdbcConnection del
proveedor de datos .NET Framework para ODBC. Para conectarse a un origen de datos Oracle, utilice el objeto
OracleConnection del proveedor de datos .NET Framework para ODBC. Para almacenar y recuperar cadenas de
conexión de forma segura, consulte Protección dela información de conexión .

Cierre de conexiones
Recomendamos cerrar siempre la conexión cuando termine de utilizarla, para que la conexión pueda regresar al
grupo. El bloque Using de Visual Basic o C# elimina automáticamente la conexión cuando el código sale del
bloque, incluso en el caso de una excepción no controlada. Consulte Using Statement y Using Statement para
obtener más información.
También puede utilizar los métodos Close o Dispose del objeto de conexión correspondiente al proveedor que
esté utilizando. Es posible que las conexiones que no se cierran explícitamente no se puedan agregar ni puedan
regresar al grupo. Por ejemplo, una conexión que se ha salido del ámbito pero que no se ha cerrado explícitamente
solo se devolverá al grupo de conexión si se ha alcanzado el tamaño máximo del grupo y la conexión aún es válida.
Para obtener más información, vea Agrupación de conexiones OLE DB, ODBC y Oracle.

NOTE
No llame Close Dispose ni en un Connection , un DataReader Finalize o cualquier otro objeto administrado en el
método de la clase. En un finalizador, libere solo los recursos no administrados que pertenezcan directamente a su clase. Si la
clase no dispone de recursos no administrados, no incluya un método Finalize en la definición de clase. Para obtener más
información, consulte Recolección de elementos no utilizados.

NOTE
Los eventos de inicio y cierre de sesión no se provocarán en el servidor cuando se busque una conexión desde el grupo de
conexiones o se devuelva a éste, puesto que la conexión no está cerrada realmente cuando se devuelve al grupo de
conexiones. Para más información, consulte el artículo sobre la agrupación de conexiones de SQL Server (ADO.NET).

Conectarse a SQL Server


El proveedor de datos .NET Framework para SQL Server admite un formato de cadena de conexión que es similar
al de OLE DB (ADO). Para consultar los nombres y valores válidos de formato de cadena, vea la propiedad
ConnectionString del objeto SqlConnection. También puede usar la clase SqlConnectionStringBuilder para crear
cadenas de conexión sintácticamente válidas en tiempo de ejecución. Para obtener más información, vea
Generadores de cadenas de conexión.
El siguiente código de ejemplo demuestra cómo crear y abrir una conexión a una base de datos SQL Server.
' Assumes connectionString is a valid connection string.
Using connection As New SqlConnection(connectionString)
connection.Open()
' Do work here.
End Using

// Assumes connectionString is a valid connection string.


using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Do work here.
}

Seguridad integrada y ASP.NET


La seguridad integrada de SQL Server (también conocida como conexiones de confianza) ayuda a proteger las
conexiones a SQL Server dado que no expone el identificador y la contraseña de un usuario en la cadena de
conexión y es el método recomendado para autenticar una conexión. La seguridad integrada utiliza la identidad de
seguridad actual, o símbolo (token), del proceso en ejecución, que en aplicaciones de escritorio, es normalmente la
identidad del usuario que actualmente ha iniciado la sesión.
La identidad de seguridad para aplicaciones ASP.NET se puede establecer en una de varias opciones diferentes.
Para comprender mejor la identidad de seguridad que utiliza una aplicación ASP.NET al conectarse a SQL Server,
vea ASP.NET Suplantación, autenticación de ASP.NETy Cómo: obtener acceso a SQL Server mediantela seguridad
integrada de Windows .

Conexión a un origen de datos OLE DB


El proveedor de datos de .NET Framework para OLE DB proporciona conectividad a orígenes de datos expuestos
mediante OLE DB (a través de SQLOLEDB, el proveedor OLE DB para SQL Server), mediante el objeto
OleDbConnection .
En el proveedor de datos .NET Framework para OLE DB, el formato de cadena de conexión es idéntico al utilizado
en ADO, con las siguientes excepciones:
Se requiere la palabra clave Provider.
No se admiten las palabras clave URL, Proveedor remoto y Ser vidor remoto.
Para obtener más información acerca de las cadenas de conexión OLE DB, vea el tema ConnectionString. También
puede usar OleDbConnectionStringBuilder para crear cadenas de conexión en tiempo de ejecución.

NOTE
El objeto OleDbConnection no admite la configuración ni la recuperación de propiedades dinámicas específicas de un
proveedor OLE DB. Solo se admiten las propiedades que se pueden proporcionar en la cadena de conexión para el proveedor
OLE DB.

El siguiente código de ejemplo demuestra cómo crear y abrir una conexión a un origen de datos OLE DB.

' Assumes connectionString is a valid connection string.


Using connection As New OleDbConnection(connectionString)
connection.Open()
' Do work here.
End Using
// Assumes connectionString is a valid connection string.
using (OleDbConnection connection =
new OleDbConnection(connectionString))
{
connection.Open();
// Do work here.
}

No utilice archivos de vínculo de datos universal


Es posible proporcionar información de conexión para un OleDbConnection en un archivo de vínculo de datos
universal (UDL); sin embargo, debe evitar hacerlo. Los archivos UDL no están cifrados y exponen la información de
cadena de conexión en texto sin cifrar. Un archivo UDL no se puede proteger mediante .NET Framework, ya que se
trata de un recurso basado en un archivo externo a la aplicación.

Conexión a un origen de datos ODBC


El proveedor de datos de .NET Framework para ODBC proporciona conectividad a orígenes de datos expuestos
mediante ODBC mediante el objeto OdbcConnection.
En el proveedor de datos .NET Framework para ODBC, el formato de cadena de conexión está diseñado para que
coincida lo más posible con el de ODBC. También puede proporcionar un nombre de origen de datos (DSN) ODBC.
Para obtener más información sobre OdbcConnection OdbcConnection , consulte el archivo .
El siguiente código de ejemplo demuestra cómo crear y abrir una conexión a un origen de datos ODBC.

' Assumes connectionString is a valid connection string.


Using connection As New OdbcConnection(connectionString)
connection.Open()
' Do work here.
End Using

// Assumes connectionString is a valid connection string.


using (OdbcConnection connection =
new OdbcConnection(connectionString))
{
connection.Open();
// Do work here.
}

Conexión a un origen de datos Oracle


El proveedor de datos de .NET Framework para Oracle proporciona conectividad a orígenes de datos de Oracle
mediante el objeto OracleConnection.
En el proveedor de datos .NET Framework para Oracle, el formato de cadena de conexión está diseñado para que
coincida lo más posible con el del proveedor OLE DB para Oracle (MSDAORA). Para obtener más detalles sobre
OracleConnection OracleConnection , consulte el archivo .
El siguiente código de ejemplo demuestra cómo crear y abrir una conexión a un origen de datos Oracle.
' Assumes connectionString is a valid connection string.
Using connection As New OracleConnection(connectionString)
connection.Open()
' Do work here.
End Using

// Assumes connectionString is a valid connection string.


using (OracleConnection connection =
new OracleConnection(connectionString))
{
connection.Open();
// Do work here.
}
OracleConnection nwindConn = new OracleConnection("Data Source=MyOracleServer;Integrated Security=yes;");
nwindConn.Open();

Consulte también
Conectarse a un origen de datos
Cadenas de conexión
Agrupación de conexiones de OLE DB, ODBC y Oracle
Información general de ADO.NET
Eventos de Connection
21/03/2020 • 6 minutes to read • Edit Online

Todos los proveedores de datos de .NET Framework tienen objetos Connection con dos eventos que puede usar
para recuperar mensajes informativos de un origen de datos o para determinar si ha cambiado el estado de una
conexión. En la tabla siguiente se describen los eventos del objeto Connection.

EVEN TO DESC RIP C IÓ N

InfoMessage Se produce cuando se devuelve un mensaje informativo desde


un origen de datos. Los mensajes informativos son aquellos
procedentes de orígenes de datos que no inician una
excepción.

StateChange Se produce cuando cambia el estado de la conexión.

Trabajar con el evento InfoMessage


Con el evento InfoMessage del objeto SqlConnection puede recuperar advertencias o mensajes informativos de un
origen de datos de SQL Server. Si se devuelven errores desde el origen de datos con un nivel de seguridad entre
11 y 16, se inicia una excepción. Sin embargo, el evento InfoMessage se puede utilizar para obtener mensajes del
origen de datos que no estén asociados a un error. En el caso de Microsoft SQL Server, cualquier error que tenga la
gravedad 10, como máximo, se considera de tipo informativo y se captura mediante el evento InfoMessage. Para
obtener más información, consulte el artículo Gravedad de errores del motor de base de datos.
El InfoMessage evento recibe SqlInfoMessageEventArgs un objeto que contiene, en su propiedad Errors, una
colección de los mensajes del origen de datos. Puede consultar los objetos Error de esta colección para el número
de error y el texto del mensaje, así como el origen del error. El proveedor de datos .NET Framework para SQL
Server incluye asimismo datos acerca de la base de datos, el procedimiento almacenado y el número de línea
donde se originó el mensaje.
Ejemplo
En el ejemplo de código siguiente se muestra cómo se puede agregar un controlador de eventos para el evento
InfoMessage.

' Assumes that connection represents a SqlConnection object.


AddHandler connection.InfoMessage, _
New SqlInfoMessageEventHandler(AddressOf OnInfoMessage)

Private Shared Sub OnInfoMessage(sender As Object, _


args As SqlInfoMessageEventArgs)
Dim err As SqlError
For Each err In args.Errors
Console.WriteLine("The {0} has received a severity {1}, _
state {2} error number {3}\n" & _
"on line {4} of procedure {5} on server {6}:\n{7}", _
err.Source, err.Class, err.State, err.Number, err.LineNumber, _
err.Procedure, err.Server, err.Message)
Next
End Sub
// Assumes that connection represents a SqlConnection object.
connection.InfoMessage +=
new SqlInfoMessageEventHandler(OnInfoMessage);

protected static void OnInfoMessage(


object sender, SqlInfoMessageEventArgs args)
{
foreach (SqlError err in args.Errors)
{
Console.WriteLine(
"The {0} has received a severity {1}, state {2} error number {3}\n" +
"on line {4} of procedure {5} on server {6}:\n{7}",
err.Source, err.Class, err.State, err.Number, err.LineNumber,
err.Procedure, err.Server, err.Message);
}
}

Controlar errores como InfoMessages


Normalmente, el evento InfoMessage solo se activa para mensajes informativos y de advertencia enviados desde
el servidor. Sin embargo, cuando se produce un error real, se detiene la ejecución del método ExecuteNonQuer y
o ExecuteReader que inició la operación de servidor y se produce una excepción.
Si desea seguir procesando el resto de las instrucciones de un comando, independientemente de los errores
producidos en el servidor, establezca la propiedad FireInfoMessageEventOnUserErrors de SqlConnection como
true . De esta forma, la conexión activa el evento InfoMessage para errores, en lugar de iniciar una excepción e
interrumpir el procesamiento. La aplicación cliente puede controlar el evento y reaccionar ante las situaciones de
error.

NOTE
Los errores con un nivel de gravedad de 17, como mínimo, que hacen que el servidor interrumpa el procesamiento de
comandos, deben controlarse como excepciones. En este caso, se inicia una excepción, independientemente del modo en que
se controle el error en el evento InfoMessage.

Trabajar con el evento StateChange


El evento StateChange se produce cuando cambia el estado de una conexión. El evento StateChange recibe
StateChangeEventArgs que le permiten determinar el cambio en el estado de Connection mediante el uso de la
OriginalState y CurrentState propiedades. El OriginalState propiedad ConnectionState es una enumeración
que indica el estado de la conexión antes de que cambiara. CurrentState es ConnectionState una enumeración
que indica el estado de la conexión después de cambiar.
En el ejemplo de código siguiente se utiliza el StateChange eventos para escribir un mensaje en la consola
cuando cambia el estado de la conexión.
' Assumes connection represents a SqlConnection object.
AddHandler connection.StateChange, _
New StateChangeEventHandler(AddressOf OnStateChange)

Protected Shared Sub OnStateChange( _


sender As Object, args As StateChangeEventArgs)

Console.WriteLine( _
"The current Connection state has changed from {0} to {1}.", _
args.OriginalState, args.CurrentState)
End Sub

// Assumes connection represents a SqlConnection object.


connection.StateChange += new StateChangeEventHandler(OnStateChange);

protected static void OnStateChange(object sender,


StateChangeEventArgs args)
{
Console.WriteLine(
"The current Connection state has changed from {0} to {1}.",
args.OriginalState, args.CurrentState);
}

Consulte también
Conectarse a un origen de datos
Información general de ADO.NET
Cadenas de conexión de ADO.NET
05/02/2020 • 5 minutes to read • Edit Online

Una cadena de conexión contiene información de inicialización que se transfiere como un parámetro desde un
proveedor de datos a un origen de datos. El proveedor de datos recibe la cadena de conexión como el valor de la
propiedad DbConnection.ConnectionString. El proveedor analiza la cadena de conexión y garantiza que la sintaxis
es correcta y que se admiten las palabras clave. A continuación, el método DbConnection.Open() pasa los
parámetros de conexión analizados al origen de datos. El origen de datos realiza una validación adicional y
establece una conexión.

Sintaxis de cadena de conexión


Una cadena de conexión es una lista delimitada por signos de punto y coma de pares de parámetros de clave-
valor:

keyword1=value; keyword2=value;

Las palabras clave no distinguen mayúsculas de minúsculas. Sin embargo, los valores pueden distinguir entre
mayúsculas y minúsculas, en función del origen de datos. Las palabras clave y los valores pueden contener
caracteres de espacio en blanco. Los espacios en blanco iniciales y finales se omiten en las palabras clave y los
valores sin comillas.
Si un valor contiene el punto y coma, caracteres de control Unicodeo espacios en blanco iniciales o finales, debe
incluirse entre comillas simples o dobles. Por ejemplo:

Keyword=" whitespace ";


Keyword='special;character';

Es posible que el carácter envolvente no se encuentre dentro del valor que contiene. Por lo tanto, un valor que
contenga comillas simples solo se puede incluir entre comillas dobles y viceversa:

Keyword='double"quotation;mark';
Keyword="single'quotation;mark";

También puede escapar el carácter envolvente con dos de ellos juntos:

Keyword="double""quotation";
Keyword='single''quotation';

Las comillas, así como el signo igual, no requieren caracteres de escape, por lo que las siguientes cadenas de
conexión son válidas:

Keyword=no "escaping" 'required';


Keyword=a=b=c

Puesto que cada valor se lee hasta el punto y coma siguiente o el final de la cadena, el valor del último ejemplo es
a=b=c y el punto y coma final es opcional.
Todas las cadenas de conexión comparten la misma sintaxis básica que se ha descrito anteriormente. Sin
embargo, el conjunto de palabras clave reconocidas depende del proveedor y ha evolucionado a lo largo de los
años desde API anteriores, como ODBC. El proveedor de datos de .NET Framework para SQL Server ( SqlClient )
admite muchas palabras clave de API anteriores, pero suele ser más flexible y acepta sinónimos para muchas de
las palabras clave de cadena de conexión comunes.
Los errores tipográficos pueden producir errores. Por ejemplo, Integrated Security=true es válido, pero
IntegratedSecurity=true produce un error.

Las cadenas de conexión construidas manualmente en tiempo de ejecución desde la entrada de usuario no
validada son vulnerables a ataques de inyección de cadenas y ponen en peligro la seguridad en el origen de
datos. Para solucionar estos problemas, ADO.NET 2,0 presentó generadores de cadenas de conexión para cada
proveedor de datos .NET Framework . Estos generadores de cadenas de conexión exponen parámetros como
propiedades fuertemente tipadas y permiten validar la cadena de conexión antes de enviarla al origen de datos.

Esta sección
Generadores de cadenas de conexión
Muestra cómo usar las clases ConnectionStringBuilder para construir cadenas de conexión válidas en tiempo de
ejecución.
Cadenas de conexión y archivos de configuración
Muestra cómo almacenar y recuperar cadenas de conexión en archivos de configuración.
\ de Sintaxis de cadena de conexión
Proteger la información de conexión
Muestra técnicas de protección de la información utilizada para conectarse a un origen de datos.

Vea también
Conexión a un origen de datos
Información general sobre ADO.NET
Generadores de cadenas de conexión
21/03/2020 • 7 minutes to read • Edit Online

En versiones anteriores de ADO.NET, no se producía la comprobación en tiempo de compilación de cadenas


ArgumentExceptionde conexión con valores de cadena concatenados, por lo que en tiempo de ejecución, una
palabra clave incorrecta generaba un archivo . Cada uno de los proveedores de datos de .NET Framework
admitía una sintaxis diferente para las palabras clave de cadena de conexión, lo que dificultaba la construcción
de cadenas de conexión válidas si se realizaba manualmente. Para solucionar este problema, ADO.NET 2.0
introdujo nuevos generadores de cadenas de conexión para cada proveedor de datos de .NET Framework. Cada
uno de los proveedores de datos incluye una clase creadora de cadenas de conexión fuertemente tipadas que
hereda de DbConnectionStringBuilder. En la tabla siguiente se enumeran los proveedores de datos de .NET
Framework y sus clases de generador de cadenas de conexión asociadas.

P RO VEEDO R C L A SE C O N N EC T IO N ST RIN GB UIL DER

System.Data.SqlClient System.Data.SqlClient.SqlConnectionStringBuilder

System.Data.OleDb System.Data.OleDb.OleDbConnectionStringBuilder

System.Data.Odbc System.Data.Odbc.OdbcConnectionStringBuilder

System.Data.OracleClient System.Data.OracleClient.OracleConnectionStringBuilder

Ataques de inyección de cadenas de conexión


Cuando se utiliza la concatenación dinámica de cadenas para generar cadenas de conexión basadas en datos
introducidos por el usuario, se pueden producir ataques de inyección de cadenas de conexión. Si no se valida la
cadena y no se crean secuencias de escape para los caracteres o el texto malintencionado, los atacantes pueden
tener acceso a datos confidenciales y a otros recursos del servidor. Por ejemplo, un atacante puede realizar un
ataque si proporciona un punto y coma y anexa un valor adicional. La cadena de conexión se analiza utilizando
un algoritmo del tipo "el último gana", y la entrada hostil se sustituye por un valor legítimo.
Las clases compiladoras de cadenas de conexión se han diseñado para eliminar la adivinación y desarrollar
protección ante errores de sintaxis y vulnerabilidades de seguridad. Proporcionan métodos y propiedades que
corresponden a los pares clave-valor conocidos permitidos por cada proveedor de datos. Cada clase mantiene
una colección fija de sinónimos y puede convertir un sinónimo al correspondiente nombre de clave conocido. En
los pares clave-valor se realizan comprobaciones y los pares no válidos inician una excepción. Además, los
valores inyectados se controlan de forma segura.
En el ejemplo siguiente se muestra cómo SqlConnectionStringBuilder controla un valor adicional insertado en la
configuración Initial Catalog .

Dim builder As New System.Data.SqlClient.SqlConnectionStringBuilder


builder("Data Source") = "(local)"
builder("Integrated Security") = True
builder("Initial Catalog") = "AdventureWorks;NewValue=Bad"
Console.WriteLine(builder.ConnectionString)
System.Data.SqlClient.SqlConnectionStringBuilder builder =
new System.Data.SqlClient.SqlConnectionStringBuilder();
builder["Data Source"] = "(local)";
builder["integrated Security"] = true;
builder["Initial Catalog"] = "AdventureWorks;NewValue=Bad";
Console.WriteLine(builder.ConnectionString);

El resultado muestra que SqlConnectionStringBuilder controla esta situación correctamente, ya que establece el
escape del valor adicional entre comillas dobles en lugar de anexarlo a la cadena de conexión como un nuevo
par clave-valor.

data source=(local);Integrated Security=True;


initial catalog="AdventureWorks;NewValue=Bad"

Crear cadenas de conexión a partir de archivos de configuración


Si determinados elementos de una cadena de conexión se conocen de antemano, se pueden almacenar en un
archivo de configuración y recuperar en tiempo de ejecución para construir una cadena de conexión completa.
Por ejemplo, se puede conocer por adelantado el nombre de la base de datos, pero no el del servidor. También
es posible que desee que un usuario indique un nombre y una contraseña en tiempo de ejecución sin que pueda
inyectar otros valores en ella.
Uno de los constructores sobrecargados de un compilador de cadenas de conexión toma String como
argumento, lo que permite proporcionar una cadena de conexión parcial que se puede completar después con
los datos introducidos por el usuario. La cadena de conexión parcial se puede almacenar en un archivo de
configuración y recuperarse en tiempo de ejecución.

NOTE
El espacio de nombres System.Configuration permite el acceso mediante programación a archivos de configuración que
usan WebConfigurationManager en aplicaciones web y ConfigurationManager en aplicaciones Windows. Para obtener
más información sobre cómo trabajar con cadenas de conexión y archivos de configuración, vea Cadenas de conexión y
archivosde configuración .

Ejemplo
En este ejemplo se muestra la recuperación de una cadena de conexión incluida en un archivo de configuración
y cómo se completa mediante el establecimiento de las propiedades DataSource, UserID y Password de
SqlConnectionStringBuilder. El archivo de configuración se define de la siguiente forma.

<connectionStrings>
<clear/>
<add name="partialConnectString"
connectionString="Initial Catalog=Northwind;"
providerName="System.Data.SqlClient" />
</connectionStrings>

NOTE
Para ejecutar el código, debe establecer una referencia al archivo System.Configuration.dll del proyecto.
private static void BuildConnectionString(string dataSource,
string userName, string userPassword)
{
// Retrieve the partial connection string named databaseConnection
// from the application's app.config or web.config file.
ConnectionStringSettings settings =
ConfigurationManager.ConnectionStrings["partialConnectString"];

if (null != settings)
{
// Retrieve the partial connection string.
string connectString = settings.ConnectionString;
Console.WriteLine("Original: {0}", connectString);

// Create a new SqlConnectionStringBuilder based on the


// partial connection string retrieved from the config file.
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder(connectString);

// Supply the additional values.


builder.DataSource = dataSource;
builder.UserID = userName;
builder.Password = userPassword;
Console.WriteLine("Modified: {0}", builder.ConnectionString);
}
}

Private Sub BuildConnectionString(ByVal dataSource As String, _


ByVal userName As String, ByVal userPassword As String)

' Retrieve the partial connection string named databaseConnection


' from the application's app.config or web.config file.
Dim settings As ConnectionStringSettings = _
ConfigurationManager.ConnectionStrings("partialConnectString")

If Not settings Is Nothing Then


' Retrieve the partial connection string.
Dim connectString As String = settings.ConnectionString
Console.WriteLine("Original: {0}", connectString)

' Create a new SqlConnectionStringBuilder based on the


' partial connection string retrieved from the config file.
Dim builder As New SqlConnectionStringBuilder(connectString)

' Supply the additional values.


builder.DataSource = dataSource
builder.UserID = userName
builder.Password = userPassword

Console.WriteLine("Modified: {0}", builder.ConnectionString)


End If
End Sub

Consulte también
Cadenas de conexión
Privacidad y seguridad de datos
Información general de ADO.NET
Cadenas de conexión y archivos de configuración
27/03/2020 • 22 minutes to read • Edit Online

La incrustación de cadenas de conexión en el código de la aplicación puede producir vulnerabilidades en la


seguridad y problemas de mantenimiento. Las cadenas de conexión sin cifrar compiladas en el código fuente de
una aplicación se pueden ver con la herramienta Ildasm.exe (Desensamblador de IL). Además, si la cadena de
conexión cambia en algún momento, será necesario compilar de nuevo la aplicación. Por estas razones, se
recomienda almacenar las cadenas de conexión en un archivo de configuración de la aplicación.

Trabajar con archivos de configuración de la aplicación


Los archivos de configuración de la aplicación contienen valores específicos de una aplicación determinada. Por
ejemplo, una aplicación ASP.NET puede tener uno o varios archivos web.config y una aplicación Windows puede
tener un archivo app.config opcional. Los archivos de configuración comparten elementos comunes, aunque su
nombre y ubicación varían en función del host de la aplicación.
Sección connectionStrings
Las cadenas de conexión se pueden almacenar como pares clave-valor en la sección connectionStrings del
elemento configuration en el archivo de configuración de una aplicación. Los elementos secundarios incluyen
add , clear y remove .
El siguiente fragmento del archivo de configuración muestra el esquema y la sintaxis para almacenar una cadena
de conexión. El atributo name es un nombre que se proporciona para identificar de forma única una cadena de
conexión, de forma que se pueda recuperar en tiempo de ejecución. providerName es el nombre invariable del
proveedor de datos de .NET Framework registrado en el archivo machine.config.

<?xml version='1.0' encoding='utf-8'?>


<configuration>
<connectionStrings>
<clear />
<add name="Name"
providerName="System.Data.ProviderName"
connectionString="Valid Connection String;" />
</connectionStrings>
</configuration>

NOTE
Puede guardar parte de la cadena de conexión en un archivo de configuración y usar la clase DbConnectionStringBuilder
para completarla en tiempo de ejecución. Esto resulta útil en escenarios en los que no se conocen los elementos de la
cadena de conexión por anticipado o cuando no desea guardar información confidencial en un archivo de configuración.
Para obtener más información, vea Generadores de cadenas de conexión.

Uso de archivos de configuración externos


Los archivos de configuración externos son archivos independientes que contienen un fragmento de un archivo
de configuración compuesto de una sola sección. El archivo de configuración principal hace referencia al archivo
de configuración externo. El almacenamiento de la sección connectionStrings en un archivo físicamente
independiente resulta útil en situaciones en las que es posible que se editen las cadenas de conexión después de
implementar la aplicación. Por ejemplo, si se modifican los archivos de configuración, ASP.NET reinicia de forma
predeterminada el dominio de la aplicación, lo que provoca la pérdida de la información de estado. Sin embargo,
la modificación de un archivo de configuración externo no provoca el reinicio de la aplicación. Los archivos de
configuración externos no se limitan a ASP.NET; también se pueden utilizar en aplicaciones Windows. Además, la
seguridad y permisos de acceso a los archivos se pueden usar para restringir el acceso a los archivos de
configuración externos. El trabajo con archivos de configuración externos en tiempo de ejecución es transparente
y no requiere código especial.
Para almacenar las cadenas de conexión en un archivo de configuración externo, cree un archivo independiente
que contenga únicamente la sección connectionStrings . No incluya elementos, secciones ni atributos
adicionales. En este ejemplo se muestra la sintaxis de un archivo de configuración externo.

<connectionStrings>
<add name="Name"
providerName="System.Data.ProviderName"
connectionString="Valid Connection String;" />
</connectionStrings>

En el archivo de configuración principal de la aplicación, use el atributo configSource para especificar el nombre
completo y la ubicación del archivo externo. En este ejemplo se hace referencia a un archivo de configuración
externo denominado connections.config .

<?xml version='1.0' encoding='utf-8'?>


<configuration>
<connectionStrings configSource="connections.config"/>
</configuration>

Recuperar cadenas de conexión en tiempo de ejecución


.NET Framework 2.0 incorpora nuevas clases en el espacio de nombres System.Configuration para simplificar la
recuperación de las cadenas de conexión de los archivos de configuración en tiempo de ejecución. La cadena de
conexión se puede recuperar mediante programación con el nombre de la cadena o el nombre de proveedor.

NOTE
El archivo machine.config también contiene una sección connectionStrings , donde se encuentran las cadenas de
conexión que usa Visual Studio. Al recuperar cadenas de conexión por nombre de proveedor desde el archivo app.config
en una aplicación de Windows, las cadenas de conexión de machine.config se cargan primero y, a continuación, las
entradas de app.config . Agregar clear inmediatamente después de la connectionStrings elemento quita todas las
referencias heredadas de la estructura de datos en memoria, de modo que sólo se tienen en cuenta las cadenas de conexión
definidas en el archivo app.config local.

Trabajar con clases de configuración


A partir de .NET Framework 2.0, se usa el elemento ConfigurationManager al trabajar con archivos de
configuración en el equipo local, reemplazando al elemento en desuso ConfigurationSettings.
WebConfigurationManager se usa para trabajar con archivos de configuración de ASP.NET. Esta característica se
ha diseñado para trabajar con archivos de configuración en un servidor web y permite el acceso mediante
programación a secciones del archivo de configuración como system.web .

NOTE
El acceso a los archivos de configuración en tiempo de ejecución requiere la concesión de permisos al llamador; los permisos
necesarios dependen del tipo de aplicación, del archivo de configuración y de la ubicación. Para obtener más información,
vea Utilizar las clases Configuration y WebConfigurationManager para las aplicaciones ASP.NET, o bien
ConfigurationManager para las aplicaciones Windows.
Puede usar ConnectionStringSettingsCollection para recuperar cadenas de conexión de archivos de configuración
de aplicación. Esta clase contiene una colección de objetos ConnectionStringSettings, cada uno de los cuales
representa una única entrada en la sección connectionStrings . Sus propiedades se asignan a los atributos de
cadenas de conexión, lo que permite recuperar una cadena de conexión mediante la especificación de su nombre
o del nombre del proveedor.

P RO P IEDA D DESC RIP C IÓ N

Name Nombre de la cadena de conexión. Se asigna al atributo


name .

ProviderName Nombre completo del proveedor. Se asigna al atributo


providerName .

ConnectionString La cadena de conexión. Se asigna al atributo


connectionString .

Ejemplo: mostrar todas las cadenas de conexión


En este ejemplo se ConnectionStringSettingsCollection recorre ConnectionStringSettings.Namenitalas y se
muestran las propiedades , ConnectionStringSettings.ProviderName, y
ConnectionStringSettings.ConnectionString en la ventana de la consola.

NOTE
System.Configuration.dll no se incluye en todos los tipos de proyectos y es posible que deba establecer una referencia a
este elemento para usar las clases de configuración. El nombre y la ubicación de un archivo de configuración de aplicación
determinado varían en función del tipo de aplicación y del proceso de hospedaje.

using System.Configuration;

class Program
{
static void Main()
{
GetConnectionStrings();
Console.ReadLine();
}

static void GetConnectionStrings()


{
ConnectionStringSettingsCollection settings =
ConfigurationManager.ConnectionStrings;

if (settings != null)
{
foreach(ConnectionStringSettings cs in settings)
{
Console.WriteLine(cs.Name);
Console.WriteLine(cs.ProviderName);
Console.WriteLine(cs.ConnectionString);
}
}
}
}
Imports System.Configuration

Class Program
Shared Sub Main()
GetConnectionStrings()
Console.ReadLine()
End Sub

Private Shared Sub GetConnectionStrings()

Dim settings As ConnectionStringSettingsCollection = _


ConfigurationManager.ConnectionStrings

If Not settings Is Nothing Then


For Each cs As ConnectionStringSettings In settings
Console.WriteLine(cs.Name)
Console.WriteLine(cs.ProviderName)
Console.WriteLine(cs.ConnectionString)
Next
End If
End Sub
End Class

Ejemplo: recuperar una cadena de conexión por su nombre


El siguiente ejemplo muestra cómo recuperar una cadena de conexión de un archivo de configuración mediante
la especificación del nombre. El código crea un objeto ConnectionStringSettings, de forma que el parámetro de
entrada proporcionado coincida con el nombre de ConnectionStrings. Si no se encuentra una coincidencia de
nombre, la función devuelve null ( Nothing en Visual Basic).

// Retrieves a connection string by name.


// Returns null if the name is not found.
static string GetConnectionStringByName(string name)
{
// Assume failure.
string returnValue = null;

// Look for the name in the connectionStrings section.


ConnectionStringSettings settings =
ConfigurationManager.ConnectionStrings[name];

// If found, return the connection string.


if (settings != null)
returnValue = settings.ConnectionString;

return returnValue;
}
' Retrieves a connection string by name.
' Returns Nothing if the name is not found.
Private Shared Function GetConnectionStringByName( _
ByVal name As String) As String

' Assume failure


Dim returnValue As String = Nothing

' Look for the name in the connectionStrings section.


Dim settings As ConnectionStringSettings = _
ConfigurationManager.ConnectionStrings(name)

' If found, return the connection string.


If Not settings Is Nothing Then
returnValue = settings.ConnectionString
End If

Return returnValue
End Function

Ejemplo: recuperar una cadena de conexión por el nombre de proveedor


En este ejemplo se muestra cómo recuperar una cadena de conexión mediante la especificación del nombre
invariable de proveedor con el formato Sistema.Datos.NombreDelProveedor. El código recorre en iteración
ConnectionStringSettingsCollection y devuelve la cadena de conexión del primer valor de ProviderName
encontrado. Si no se encuentra el nombre del proveedor, la función devuelve null ( Nothing en Visual Basic).

// Retrieve a connection string by specifying the providerName.


// Assumes one connection string per provider in the config file.
static string GetConnectionStringByProvider(string providerName)
{
// Return null on failure.
string returnValue = null;

// Get the collection of connection strings.


ConnectionStringSettingsCollection settings =
ConfigurationManager.ConnectionStrings;

// Walk through the collection and return the first


// connection string matching the providerName.
if (settings != null)
{
foreach (ConnectionStringSettings cs in settings)
{
if (cs.ProviderName == providerName)
returnValue = cs.ConnectionString;
break;
}
}
return returnValue;
}
' Retrieve a connection string by specifying the providerName.
' Assumes one connection string per provider in the config file.
Private Shared Function GetConnectionStringByProvider( _
ByVal providerName As String) As String

'Return Nothing on failure.


Dim returnValue As String = Nothing

' Get the collection of connection strings.


Dim settings As ConnectionStringSettingsCollection = _
ConfigurationManager.ConnectionStrings

' Walk through the collection and return the first


' connection string matching the providerName.
If Not settings Is Nothing Then
For Each cs As ConnectionStringSettings In settings
If cs.ProviderName = providerName Then
returnValue = cs.ConnectionString
Exit For
End If
Next
End If

Return returnValue
End Function

Cifrar secciones del archivo de configuración mediante una


configuración protegida
En ASP.NET 2.0 se incorporó una característica nueva denominada configuración protegida, que permite cifrar la
información confidencial en un archivo de configuración. Si bien se ha diseñado principalmente para ASP.NET, la
configuración protegida también se puede usar para cifrar secciones del archivo de configuración en aplicaciones
Windows. Para obtener una descripción detallada de las funciones de configuración protegida, vea Cifrar
información de configuración mediante una configuración protegida.
En el fragmento de archivo de configuración siguiente se muestra la sección connectionStrings después de
haberse cifrado. En la sección configProtectionProvider se especifica el proveedor de configuración protegida
que se usa para cifrar y descifrar las cadenas de conexión. En la sección Encr yptedData se incluye el texto
cifrado.

<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAH2... </CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>

Cuando se recupera la cadena de conexión cifrada en tiempo de ejecución, .NET Framework usa el proveedor
especificado para descifrar CipherValue y que así esté disponible para la aplicación. No es necesario escribir
ningún código adicional para administrar el proceso de descifrado.
Proveedores de configuración protegida
Los proveedores de configuración protegida se registran en la sección configProtectedData del archivo
machine.config en el equipo local, como se muestra en el fragmento siguiente, donde se pueden ver los dos
proveedores de configuración protegida que proporciona .NET Framework. Los valores que se muestran se han
truncado para facilitar la lectura.
<configProtectedData defaultProvider="RsaProtectedConfigurationProvider">
<providers>
<add name="RsaProtectedConfigurationProvider"
type="System.Configuration.RsaProtectedConfigurationProvider" />
<add name="DataProtectionConfigurationProvider"
type="System.Configuration.DpapiProtectedConfigurationProvider" />
</providers>
</configProtectedData>

Puede configurar otros proveedores de configuración protegida si los agrega al archivo machine.config .
Asimismo, puede crear su propio proveedor de configuración protegida heredando de la clase base abstracta
ProtectedConfigurationProvider. En la tabla siguiente se describen los dos archivos de configuración incluidos en
.NET Framework.

P RO VEEDO R DESC RIP C IÓ N

RsaProtectedConfigurationProvider Usa el algoritmo de cifrado RSA para cifrar y descifrar datos.


Los algoritmos RSA se pueden usar para el cifrado de clave
pública y para firmas digitales. También se conoce como
cifrado de "clave pública" o asimétrico, ya que usa dos claves
diferentes. Puede usar la Herramienta de registro de IIS en
ASP.NET (aspnet_regiis.exe) para cifrar secciones de un archivo
Web.config y administrar las claves de cifrado. ASP.NET
descifra el archivo de configuración cuando lo procesa. La
identidad de la aplicación ASP.NET debe tener acceso de
lectura a la clave de cifrado utilizada para cifrar y descifrar las
secciones cifradas.

DpapiProtectedConfigurationProvider Usa la API de protección de datos (DPAPI) de Windows para


cifrar y descifrar las secciones de configuración. Usa los
servicios criptográficos integrados de Windows y se puede
configurar para la protección específica de equipo o para la
protección específica de cuenta de usuario. La protección
específica de equipo resulta útil cuando varias aplicaciones del
mismo servidor deben compartir información. La protección
específica de cuenta de usuario se puede utilizar para los
servicios que se ejecutan con una identidad de usuario
concreta, como un entorno de hospedaje compartido. Cada
aplicación se ejecuta con una identidad independiente que
limita el acceso a recursos como los archivos y las bases de
datos.

Ambos proveedores proporcionan cifrado de datos de alta seguridad. No obstante, si prevé usar el mismo archivo
de configuración de cifrado en varios servidores como, por ejemplo, una granja de servidores web, solo
RsaProtectedConfigurationProvider permite exportar las claves de cifrado usadas para cifrar los datos e
importarlas a otro servidor. Para obtener más información, vea Importar y exportar contenedores de claves RSA
con configuración protegida.
Uso de clases de configuración
El espacio de nombres System.Configuration proporciona clases para trabajar con valores de configuración
mediante programación. La clase ConfigurationManager proporciona acceso a los archivos de configuración de
equipo, aplicación y usuario. Si va a crear una aplicación WebConfigurationManager ASP.NET, puede utilizar la
clase, que proporciona la misma funcionalidad y, al mismo tiempo, le permite acceder a la configuración que es
exclusiva de ASP.NET aplicaciones, como las que se encuentran en ** <system.web>**.
NOTE
El espacio de nombres System.Security.Cryptography contiene clases que proporcionan opciones adicionales para cifrar y
descifrar datos. Use estas clases si requiere servicios criptográficos que no están disponibles cuando se usa la configuración
protegida. Algunas de estas clases son contenedores de Microsoft CryptoAPI no administrado, mientras que otras son
simplemente implementaciones administradas. Para más información, vea Servicios criptográficos.

Ejemplo de App.config
En este ejemplo se muestra cómo alternar el cifrado de la sección connectionStrings de un archivo app.config
para una aplicación Windows. En este ejemplo, el procedimiento recibe el nombre de la aplicación como
argumento, por ejemplo, "MyApplication.exe". Después, el archivo app.config se cifra y se copia en la carpeta que
contiene el ejecutable con el nombre "MyApplication.exe.config".

NOTE
La cadena de conexión solo se puede descifrar en el equipo donde se ha cifrado.

El código usa el método OpenExeConfiguration para abrir el archivo app.config y editarlo; el método GetSection
devuelve la sección connectionStrings . A continuación, el código comprueba la propiedad IsProtected y llama a
ProtectSection para cifrar la sección si no está cifrada. Para descifrar la sección se llama al método
UnprotectSection. El método Save completa la operación y guarda los cambios.

NOTE
Para ejecutar el código, debe establecer una referencia al archivo System.Configuration.dll del proyecto.
static void ToggleConfigEncryption(string exeConfigName)
{
// Takes the executable file name without the
// .config extension.
try
{
// Open the configuration file and retrieve
// the connectionStrings section.
Configuration config = ConfigurationManager.
OpenExeConfiguration(exeConfigName);

ConnectionStringsSection section =
config.GetSection("connectionStrings")
as ConnectionStringsSection;

if (section.SectionInformation.IsProtected)
{
// Remove encryption.
section.SectionInformation.UnprotectSection();
}
else
{
// Encrypt the section.
section.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
}
// Save the current configuration.
config.Save();

Console.WriteLine("Protected={0}",
section.SectionInformation.IsProtected);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Shared Sub ToggleConfigEncryption(ByVal exeConfigName As String)
' Takes the executable file name without the
' .config extension.
Try
' Open the configuration file and retrieve
' the connectionStrings section.
Dim config As Configuration = ConfigurationManager. _
OpenExeConfiguration(exeConfigName)

Dim section As ConnectionStringsSection = DirectCast( _


config.GetSection("connectionStrings"), _
ConnectionStringsSection)

If section.SectionInformation.IsProtected Then
' Remove encryption.
section.SectionInformation.UnprotectSection()
Else
' Encrypt the section.
section.SectionInformation.ProtectSection( _
"DataProtectionConfigurationProvider")
End If

' Save the current configuration.


config.Save()

Console.WriteLine("Protected={0}", _
section.SectionInformation.IsProtected)

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub

Ejemplo de Web.config
Este ejemplo usa el método OpenWebConfiguration de WebConfigurationManager . Observe que en este caso
puede indicar la ruta de acceso relativa al archivo Web.config mediante una tilde. El código requiere una
referencia a la clase System.Web.Configuration .

static void ToggleWebEncrypt()


{
// Open the Web.config file.
Configuration config = WebConfigurationManager.
OpenWebConfiguration("~");

// Get the connectionStrings section.


ConnectionStringsSection section =
config.GetSection("connectionStrings")
as ConnectionStringsSection;

// Toggle encryption.
if (section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
}
else
{
section.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
}

// Save changes to the Web.config file.


config.Save();
}
Shared Sub ToggleWebEncrypt()
' Open the Web.config file.
Dim config As Configuration = WebConfigurationManager. _
OpenWebConfiguration("~")

' Get the connectionStrings section.


Dim section As ConnectionStringsSection = DirectCast( _
config.GetSection("connectionStrings"), _
ConnectionStringsSection)

' Toggle encryption.


If section.SectionInformation.IsProtected Then
section.SectionInformation.UnprotectSection()
Else
section.SectionInformation.ProtectSection( _
"DataProtectionConfigurationProvider")
End If

' Save changes to the Web.config file.


config.Save()
End Sub

Para obtener más información acerca de cómo proteger ASP.NET aplicaciones, consulte Protección de sitios web
de ASP.NET.

Vea también
Generadores de cadenas de conexión
Proteger la información de conexión
Utilizar las clases Configuration
Configurar aplicaciones
Administrar sitios web ASP.NET
Información general sobre ADO.NET
Sintaxis de cadenas de conexión
21/03/2020 • 17 minutes to read • Edit Online

Cada proveedor de datos .NET Framework tiene un objeto Connection que hereda de la clase DbConnection, así
como una propiedad ConnectionString específica del proveedor. La sintaxis de la cadena de conexión específica de
cada proveedor se indica en su propiedad ConnectionString . En la tabla siguiente se muestran los cuatro
proveedores de datos que se incluyen en .NET Framework.

P RO VEEDO R DE DATO S DE . N ET F RA M EW O RK ( . N ET
F RA M EW O RK DATA P RO VIDER) DESC RIP C IÓ N

System.Data.SqlClient Proporciona acceso de datos para Microsoft SQL Server. Para


obtener más información acerca de la sintaxis de la cadena de
conexión, vea ConnectionString.

System.Data.OleDb Proporciona acceso de datos para orígenes de datos que se


exponen mediante OLE DB. Para obtener más información
acerca de la sintaxis de la cadena de conexión, vea
ConnectionString.

System.Data.Odbc Proporciona acceso de datos para orígenes de datos que se


exponen mediante ODBC. Para obtener más información
acerca de la sintaxis de la cadena de conexión, vea
ConnectionString.

System.Data.OracleClient Proporciona acceso de datos para Oracle versión 8.1.7 o


superior. Para obtener más información acerca de la sintaxis de
la cadena de conexión, vea ConnectionString.

Generadores de cadenas de conexión


ADO.NET 2.0 presentó los siguientes compiladores de cadenas de conexión para los proveedores de datos .NET
Framework.
SqlConnectionStringBuilder
OleDbConnectionStringBuilder
OdbcConnectionStringBuilder
OracleConnectionStringBuilder
Los compiladores de cadenas de conexión permiten crear cadenas de conexión sintácticamente válidas en tiempo
de ejecución. Por tanto, el usuario no tiene que concatenar manualmente los valores de las cadenas de conexión
del código. Para obtener más información, vea Generadores de cadenas de conexión.

Autenticación de Windows
Se recomienda usar la autenticación de Windows (a veces denominada seguridad integrada) para conectarse a
orígenes de datos que lo admiten. La sintaxis utilizada en la cadena de conexión varía dependiendo del proveedor.
En la siguiente tabla se muestra la sintaxis de autenticación de Windows utilizada con los proveedores de datos
.NET Framework.
P RO VEEDO R SIN TA XIS

SqlClient Integrated Security=true;

-- or --

Integrated Security=SSPI;

OleDb Integrated Security=SSPI;

Odbc Trusted_Connection=yes;

OracleClient Integrated Security=yes;

NOTE
Integrated Security=true produce una excepción cuando se usa con el proveedor OleDb .

Cadenas de conexión SqlClient


La sintaxis de una cadena de conexión de SqlConnection se documenta en la propiedad
SqlConnection.ConnectionString. Puede usar la propiedad ConnectionString para obtener o establecer una cadena
de conexión para una base de datos de SQL Server. Si necesita conectarse a una versión anterior de SQL Server,
debe usar el proveedor de datos .NET Framework para OleDb (System.Data.OleDb). La mayoría de las palabras
clave de cadenas de conexión se corresponden también con las propiedades de SqlConnectionStringBuilder.

IMPORTANT
La configuración predeterminada Persist Security Info de false la palabra clave es . Si se establece en true o yes ,
permite obtener información de seguridad confidencial de la conexión, incluidos el identificador de usuario y la contraseña,
una vez abierta la conexión. Manténgase Persist Security Info false establecido para asegurarse de que un origen
que no es de confianza no tiene acceso a la información de cadena de conexión confidencial.

Autenticación de Windows con SqlClient


Cada una de las siguientes formas de sintaxis utiliza la autenticación de Windows para conectarse a la base de
datos AdventureWorks en un servidor local.

"Persist Security Info=False;Integrated Security=true;


Initial Catalog=AdventureWorks;Server=MSSQL1"
"Persist Security Info=False;Integrated Security=SSPI;
database=AdventureWorks;server=(local)"
"Persist Security Info=False;Trusted_Connection=True;
database=AdventureWorks;server=(local)"

Autenticación de SQL Server con SqlClient


Es preferible utilizar la autenticación de Windows para conectarse a SQL Server. No obstante, si se requiere
autenticación de SQL Server, deberá utilizar la siguiente sintaxis para especificar un nombre de usuario y una
contraseña. En este ejemplo, los asteriscos representan un nombre de usuario y una contraseña válidos.

"Persist Security Info=False;User ID=*****;Password=*****;Initial Catalog=AdventureWorks;Server=MySqlServer"


Cuando se conecte a Azure SQL Database o a Azure user@servername SQL Data Warehouse servername y
proporcione un inicio Server= de sesión en el formato, asegúrese de que el valor del inicio de sesión coincide con
el valor proporcionado para .

NOTE
La autenticación de Windows tiene prioridad sobre los inicios de sesión de SQL Server. Si especifica Integrated Security=true
junto con un nombre de usuario y una contraseña, se ignorarán el nombre de usuario y la contraseña, y se usará la
autenticación de Windows.

Conéctese a una instancia con nombre de SQL Server


Para conectarse a una instancia con nombre de SQL ServerSQL Server, utilice la sintaxis de nombre de servidor o
nombre de instancia.

"Data Source=MySqlServer\MSSQL1;"

A la hora de crear una cadena de conexión, también puede establecer la propiedad DataSource del
SqlConnectionStringBuilder en el nombre de instancia. La propiedad DataSource de un objeto SqlConnection es
de solo lectura.
Cambios en Type System Version
La Type System Version palabra SqlConnection.ConnectionString clave en a especifica la representación del lado
cliente de los tipos de SQL ServerSQL Server . Para obtener más información sobre la palabra clave
SqlConnection.ConnectionString, vea Type System Version .

Concatenar y adjuntar a instancias de usuario de SQL Server Express


Las instancias de usuario son una característica de SQL Server Express. Permiten que un usuario que ejecuta una
cuenta de Windows local y con privilegios mínimos adjunte y ejecute una base de datos de SQL Server sin
necesidad de tener privilegios administrativos. Una instancia de usuario se ejecuta con las credenciales de
Windows del usuario, no como un servicio.
Para obtener más información sobre cómo trabajar con instancias de usuario, vea Instanciasde usuario de SQL
Server Express .

Usar TrustServerCertificate
La TrustServerCertificate palabra clave solo es válida cuando se conecta a una instancia de SQL ServerSQL
Server con un certificado válido. Cuando TrustServerCertificate se establece en true , la capa de transporte
utilizará SSL para cifrar el canal y evitar recorrer la cadena de certificados para validar la confianza.

"TrustServerCertificate=true;"

NOTE
Si TrustServerCertificate se establece en true y se activa el cifrado, se utilizará el nivel de cifrado especificado en el
servidor aunque Encrypt esté establecido en false en la cadena de conexión. De lo contrario, se producirá un error en la
conexión.

Habilitar el cifrado
Para habilitar el cifrado cuando no se ha aprovisionado un certificado en el servidor, las opciones Forzar cifrado
de protocolo y Certificado de servidor de confianza deben establecerse en el Administrador de configuración de
SQL ServerSQL Server . En ese caso, el cifrado utilizará un certificado de servidor autofirmado sin validación si no
se ha proporcionado ningún certificado comprobable en el servidor.
La configuración de las aplicaciones no puede reducir el nivel de seguridad configurado en SQL Server, sino que en
todo caso puede reforzarlo. Una aplicación puede solicitar TrustServerCertificate el Encrypt cifrado true
estableciendo las palabras clave y, garantizando que el cifrado tiene lugar incluso cuando no se ha aprovisionado
un certificado de servidor y no se ha configurado Forzar cifrado de protocolo para el cliente. Sin embargo, si en
la configuración del cliente no está habilitado TrustServerCertificate , seguirá siendo necesario un certificado de
servidor.
En la siguiente tabla se describen todos los casos.

C L IEN T E C A DEN A DE C A DEN A DE


C L IEN T E C O N F IGURA DO C O N C O N EXIÓ N / AT RIB UTO C O N EXIÓ N / AT RIB UTO
C O N F IGURA DO C O N C O N F IA R EN C IF RA R/ UT IL IZ A R C O N F IA R EN
F O RZ A R C IF RA DO DE C ERT IF IC A DO DE C IF RA DO PA RA LO S C ERT IF IC A DO DE
P ROTO C O LO SERVIDO R DATO S SERVIDO R RESULTA DO

Sin N/D No (valor Omitido No se produce el


predeterminado) cifrado.

Sin N/D Sí No (valor El cifrado solamente


predeterminado) se produce si hay un
certificado de servidor
comprobable; de lo
contrario, se produce
un error en el intento
de conexión.

Sin N/D Sí Sí El cifrado se produce


siempre, pero puede
que se utilice un
certificado de servidor
autofirmado.

Sí Sin Omitido Omitido El cifrado solo se


produce si hay un
certificado de servidor
verificable; de lo
contrario, se produce
un error en el intento
de conexión.

Sí Sí No (valor Omitido El cifrado se produce


predeterminado) siempre, pero puede
que se utilice un
certificado de servidor
autofirmado.

Sí Sí Sí No (valor El cifrado solo se


predeterminado) produce si hay un
certificado de servidor
verificable; de lo
contrario, se produce
un error en el intento
de conexión.
C L IEN T E C A DEN A DE C A DEN A DE
C L IEN T E C O N F IGURA DO C O N C O N EXIÓ N / AT RIB UTO C O N EXIÓ N / AT RIB UTO
C O N F IGURA DO C O N C O N F IA R EN C IF RA R/ UT IL IZ A R C O N F IA R EN
F O RZ A R C IF RA DO DE C ERT IF IC A DO DE C IF RA DO PA RA LO S C ERT IF IC A DO DE
P ROTO C O LO SERVIDO R DATO S SERVIDO R RESULTA DO

Sí Sí Sí Sí El cifrado se produce
siempre, pero puede
que se utilice un
certificado de servidor
autofirmado.

Para obtener más información, vea Usar el cifrado sin validación.

Cadenas de conexión OleDb


La propiedad ConnectionString de un objeto OleDbConnection permite obtener o establecer una cadena de
conexión para un origen de datos OLE DB, como Microsoft Access. Asimismo puede crear una cadena de conexión
de OleDb en tiempo de ejecución mediante la clase OleDbConnectionStringBuilder.
Sintaxis de cadena de conexión OleDb
Para una cadena de conexión OleDbConnection, debe proporcionar un nombre de proveedor. La siguiente cadena
de conexión conecta a una base de datos Microsoft Access mediante el proveedor Jet. Tenga en cuenta que las
palabras clave User ID y Password son opcionales si la base de datos no está protegida (opción predeterminada).

Provider=Microsoft.Jet.OLEDB.4.0; Data Source=d:\Northwind.mdb;User ID=Admin;Password=;

Si la base de datos Jet está protegida mediante la seguridad de nivel de usuario, debe proporcionar la ubicación
del archivo de información de grupo de trabajo (.mdw). Este archivo se usa para validar las credenciales
presentadas en la cadena de conexión.

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:\Northwind.mdb;Jet OLEDB:System


Database=d:\NorthwindSystem.mdw;User ID=*****;Password=*****;

IMPORTANT
Es posible proporcionar información de conexión para un OleDbConnection en un archivo de vínculo de datos universal
(UDL); sin embargo, debe evitar hacerlo. Los archivos UDL no están cifrados y exponen la información de cadena de conexión
en texto sin cifrar. Un archivo UDL no se puede proteger mediante .NET Framework, ya que se trata de un recurso basado en
un archivo externo a la aplicación. Los archivos UDL no son compatibles con SqlClient .

Utilizar DataDirectory para conectarse a Access/Jet


DataDirectory no es exclusivo de SqlClient . También se puede utilizar con los proveedores de datos de .NET
System.Data.OleDb y System.Data.Odbc. La siguiente cadena OleDbConnection de ejemplo muestra la sintaxis
necesaria para conectarse a la base de datos Northwind.mdb situada en la carpeta app_data de la aplicación. La
base de datos del sistema (System.mdw) también está almacenada en esa ubicación.

"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=|DataDirectory|\Northwind.mdb;
Jet OLEDB:System Database=|DataDirectory|\System.mdw;"
IMPORTANT
No es necesario especificar la ubicación de la base de datos del sistema en la cadena de conexión si la base de datos
Access/Jet no está protegida. La seguridad está desactivada de forma predeterminada y todos los usuarios se conectan como
el usuario Admin integrado, con una contraseña en blanco. Aun cuando la seguridad a nivel de usuario esté correctamente
implementada, una base de datos Jet sigue siendo vulnerable a los ataques. Por eso no se recomienda almacenar
información delicada en una base de datos Access/Jet a causa de la fragilidad inherente a su esquema de seguridad basado
en archivos.

Conectarse a Excel
Para conectarse a un libro de Excel se utiliza el proveedor Microsoft Jet. En la siguiente cadena de conexión, la
palabra clave Extended Properties establece propiedades que son específicas de Excel. "HDR=Yes;" indica que la
primera fila contiene nombres de columna, no datos, e "IMEX=1;" indica al controlador que siempre lea las
columnas de datos "entremezclados" como texto.

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\MyExcel.xls;Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1""

Tenga en cuenta que el carácter de comilla doble necesario en Extended Properties también debe ir incluido entre
comillas dobles.
Sintaxis de cadena de conexión del proveedor de formas de datos
Utilice las palabras clave Provider y Data Provider cuando use el proveedor de formas de datos de Microsoft. En
el siguiente ejemplo se utiliza el proveedor de formas para conectarse a una instancia local de SQL Server.

"Provider=MSDataShape;Data Provider=SQLOLEDB;Data Source=(local);Initial Catalog=pubs;Integrated


Security=SSPI;"

Cadenas de conexión Odbc


La propiedad ConnectionString de una OdbcConnection permite obtener o establecer una cadena de conexión
para un origen de datos OLE DB. Las cadenas de conexión Odbc también son compatibles con
OdbcConnectionStringBuilder.
En la siguiente cadena de conexión se utiliza el Controlador de texto de Microsoft.

Driver={Microsoft Text Driver (*.txt; *.csv)};DBQ=d:\bin

Utilizar DataDirectory para conectarse a Visual FoxPro


El siguiente ejemplo de cadena de conexión OdbcConnection muestra cómo se utiliza DataDirectory para
conectarse a un archivo de Microsoft Visual FoxPro.

"Driver={Microsoft Visual FoxPro Driver};


SourceDB=|DataDirectory|\MyData.DBC;SourceType=DBC;"

Cadenas de conexión Oracle


La propiedad ConnectionString de una OracleConnection permite obtener o establecer una cadena de conexión
para un origen de datos OLE DB. Las cadenas de conexión Oracle también son compatibles con
OracleConnectionStringBuilder.
Data Source=Oracle9i;User ID=*****;Password=*****;

Para obtener más información acerca de la sintaxis de cadena de conexión ODBC, vea ConnectionString.

Consulte también
Cadenas de conexión
Conectarse a un origen de datos
Información general de ADO.NET
Proteger la información de conexión
21/03/2020 • 7 minutes to read • Edit Online

La protección del acceso al origen de datos es uno de los objetivos más importantes a la hora de proteger una
aplicación. Las cadenas de conexión presentan una posible vulnerabilidad si no se protegen. El almacenamiento
de la información de conexión en texto sin formato o en la memoria ponen en riesgo el sistema completo. Las
cadenas de conexión incrustadas en el código fuente se pueden leer mediante Ildasm.exe (IL Disassembler) para
ver el lenguaje intermedio de Microsoft (MSIL) en un ensamblado compilado.
Pueden surgir vulnerabilidades de seguridad que afecten a las cadenas de conexión en función del tipo de
autenticación usado, de la forma en que las cadenas de conexión se almacenan en memoria y en disco, y de las
técnicas usadas para construirlas en tiempo de ejecución.

Usar autenticación de Windows


Para contribuir a limitar el acceso al origen de datos, debe proteger la información de la conexión como, por
ejemplo, el id. de usuario, la contraseña y el nombre de origen de datos. Para evitar exponer la información del
usuario, se recomienda usar la autenticación de Windows (a veces denominada seguridad integrada) siempre
que sea posible. La autenticación de Windows se especifica en una cadena de conexión mediante las palabras
clave Integrated Security o Trusted_Connection , lo que elimina la necesidad de usar un identificador de usuario
y una contraseña. Cuando se usa autenticación de Windows, este sistema operativo autentica a los usuarios y el
acceso a los recursos del servidor y de la base de datos se determina mediante la concesión de permisos a
usuarios y grupos de Windows.
Cuando no sea posible usar la autenticación de Windows, es necesario extremar las precauciones, ya que las
credenciales de usuario están expuestas en la cadena de conexión. En una aplicación ASP.NET, puede configurar
una cuenta de Windows como una identidad fija que se usa para conectarse a las bases de datos y a otros
recursos de red. Habilitar la suplantación en el elemento de identidad en el archivo web.config y especificar un
nombre de usuario y una contraseña.

<identity impersonate="true"
userName="MyDomain\UserAccount"
password="*****" />

La cuenta de identidad fija debe ser una cuenta con pocos privilegios a la que sólo se le concedan los permisos
necesarios en la base de datos. Además, debe cifrar el archivo de configuración para que el nombre de usuario y
la contraseña no se expongan en texto no cifrado.

No usar archivos de vínculo de datos universal (UDL)


Evite almacenar cadenas de conexión de OleDbConnection en un archivo de vínculo de datos universal (UDL).
Los archivos UDL se almacenan en texto no cifrado y no se pueden cifrar. Un archivo UDL no se puede proteger
ni cifrar mediante .NET Framework, ya que se trata de un recurso basado en un archivo externo a la aplicación.

Evitar ataques de inyección con compiladores de cadenas de conexión


Se pueden producir ataques de inyección de cadenas de conexión cuando se usa la concatenación dinámica de
cadenas para generar cadenas de conexión basadas en la entrada del usuario. Si no se valida la entrada del
usuario y no se crean secuencias de escape para los caracteres o el texto malintencionado, los atacantes pueden
tener acceso a datos confidenciales y a otros recursos del servidor. Para resolver este problema, ADO.NET 2.0
incorporó nuevas clases de compilador de cadenas de conexión para validar la sintaxis de las cadenas y
garantizar que no se inserten parámetros adicionales. Para obtener más información, vea Generadores de
cadenas de conexión.

Usar Persist Security Info=False


El valor predeterminado de Persist Security Info es false, y se recomienda mantener este valor
predeterminado en todas las cadenas de conexión. Si Persist Security Info se establece en true o yes ,
permitirá obtener información confidencial de seguridad de la conexión, incluidos el identificador del usuario y la
contraseña, una vez que esté abierta. Si Persist Security Info se establece en false o no , la información de
seguridad se descarta tras usarla para abrir la conexión. Esto permite garantizar que los orígenes que no sean de
confianza no tengan acceso a la información confidencial de seguridad.

Cifrar archivos de configuración


Las cadenas de conexión también se pueden almacenar en archivos de configuración, lo que elimina la necesidad
de incrustarlas en el código de la aplicación. Los archivos de configuración son archivos XML estándar para los
que .NET Framework ha definido un conjunto común de elementos. Las cadenas de conexión de los ** <**
archivos de configuración normalmente se almacenan dentro del elemento connectionStrings>en el archivo
app.config de una aplicación de Windows o el archivo web.config de una aplicación ASP.NET. Para obtener más
información sobre los conceptos básicos de almacenamiento, recuperación y cifrado de cadenas de conexión
desde archivos de configuración, consulte Cadenas de conexión y archivosde configuración .

Consulte también
Proteger aplicaciones de ADO.NET
Cifrado de la información de configuración mediante la configuración protegida
Seguridad en .NET
Información general de ADO.NET
Agrupación de conexiones
23/10/2019 • 2 minutes to read • Edit Online

La conexión a un origen de datos puede ser un proceso largo. Para minimizar el costo de la apertura de
conexiones, ADO.NET usa una técnica de optimización denominada agrupaciónde conexiones, lo que minimiza el
costo de abrir y cerrar conexiones repetidas veces. Los proveedores de datos .NET Framework tratan de forma
diferente la agrupación de conexiones.

En esta sección
Agrupación de conexiones de SQL Server (ADO.NET)
Proporciona información general sobre la agrupación de conexiones y describe cómo funciona la agrupación de
conexiones en SQL Server.
Agrupación de conexiones de OLE DB, ODBC y Oracle
Describe la agrupación de conexiones en los proveedores de datos .NET Framework para OLE DB, ODBC y Oracle.

Vea también
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
Agrupación de conexiones de SQL Server
(ADO.NET)
21/03/2020 • 20 minutes to read • Edit Online

La conexión a un servidor de bases de datos suele constar de varios pasos que requieren mucho tiempo. Se debe
establecer un canal físico, como un socket o una canalización con nombre, debe tener lugar el protocolo de enlace
con el servidor, se debe analizar la información de la cadena de conexión, el servidor debe autenticar la conexión,
se deben ejecutar comprobaciones para la inscripción en la transacción actual, etc.
En la práctica, la mayoría de las aplicaciones solamente utilizan unas cuantas configuraciones diferentes para las
conexiones. Esto significa que durante la ejecución de la aplicación, muchas conexiones idénticas se abrirán y
cerrarán de forma repetida. Para minimizar el costo de abrir conexiones, ADO.NET utiliza una técnica de
optimización denominada agrupaciónde conexiones.
La agrupación de conexiones reduce el número de veces que es necesario abrir nuevas conexiones. El agrupador
mantiene la propiedad de la conexión física. Para administrar las conexiones, mantiene un conjunto de conexiones
activas para cada configuración de conexión dada. Cada vez que un usuario llama a Open en una conexión, el
agrupador comprueba si hay una conexión disponible en el grupo. Si hay disponible una conexión agrupada, la
devuelve a la persona que llama en lugar de abrir una nueva. Cuando la aplicación llama a Close en la conexión,
el agrupador la devuelve al conjunto agrupado de conexiones activas en lugar de cerrarla. Una vez que la conexión
vuelve al grupo, ya está preparada para volverse a utilizar en la siguiente llamada a Open .
Solo se pueden agrupar conexiones con la misma configuración. ADO.NET mantiene varios grupos al mismo
tiempo, uno para cada configuración. Las conexiones se dividen en grupos por cadena de conexión, y por
identidad de Windows si se utiliza seguridad integrada. Las conexiones también se agrupan en función de si están
incluidas en una transacción. Cuando se usa ChangePassword, la instancia de SqlCredential afecta al grupo de
conexiones. Distintas instancias de SqlCredential usarán diferentes grupos de conexiones, incluso si el identificador
de usuario y la contraseña son iguales.
La agrupación de conexiones puede mejorar de forma considerable el rendimiento y la escalabilidad de la
aplicación. De forma predeterminada, la agrupación de conexiones está habilitada en ADO.NET. A menos que la
deshabilite explícitamente, el agrupador optimiza las conexiones a medida que se abren y cierran en la aplicación.
También puede proporcionar varios modificadores de cadena de conexión para controlar el comportamiento de
agrupación de conexiones. Para obtener más información, vea "Control de la agrupación de conexiones con
palabras clave de cadena de conexión" más adelante en este tema.

NOTE
Cuando se habilita la agrupación de conexiones, y si se produce un error de tiempo de expiración u otro error de inicio de
sesión, se producirá una excepción y los intentos de conexión posteriores producirán errores durante los cinco segundos
siguientes, el "período de bloqueo". Si la aplicación intenta conectarse dentro del período de bloqueo, se volverá a producir la
primera excepción. Los errores que se produzcan después de que finalice un período de bloqueo darán lugar a nuevos
períodos de bloqueo que serán el doble de largos que el período de bloqueo anterior, hasta un máximo de un minuto.

Creación y asignación del grupo


Cuando se abre una conexión por primera vez, se crea un grupo de conexión basado en un algoritmo de
coincidencia exacta que asocia el grupo con la cadena de conexión de la conexión. Cada grupo de conexión se
asocia con una cadena de conexión distinta. Si se abre una nueva conexión y la cadena de conexión no coincide
exactamente con un grupo existente, se crea un nuevo grupo. Las conexiones se agrupan por proceso, por dominio
de aplicación, por cadena de conexión y, cuando se utiliza seguridad integrada, por identidad de Windows. Las
cadenas de conexión también deben ser una coincidencia exacta; las palabras clave indicadas en un orden
diferente para la misma conexión se agruparán por separado.
En el siguiente ejemplo con C#, se crean tres nuevos objetos SqlConnection, pero solo se necesitan dos grupos de
conexión para administrarlos. Observe que las cadenas de conexión primera y segunda difieren en el valor
asignado a Initial Catalog .

using (SqlConnection connection = new SqlConnection(


"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// Pool A is created.
}

using (SqlConnection connection = new SqlConnection(


"Integrated Security=SSPI;Initial Catalog=pubs"))
{
connection.Open();
// Pool B is created because the connection strings differ.
}

using (SqlConnection connection = new SqlConnection(


"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// The connection string matches pool A.
}

Si no se especifica MinPoolSize en la cadena de conexión o se especifica como cero, las conexiones del grupo se
cerrarán tras un período de inactividad. No obstante, si el MinPoolSize especificado es mayor que cero, el grupo
de conexión no se destruye hasta que se descarga el AppDomain y finaliza el proceso. El mantenimiento de grupos
inactivos o vacíos supone una sobrecarga mínima para el sistema.

NOTE
El grupo se borra automáticamente cuando se produce un error irrecuperable, como una conmutación por error.

Agregar conexiones
Para cada cadena de conexión única se crea un grupo de conexión. Cuando se crea un grupo, se crean y agregan al
grupo varios objetos de conexión y se satisface así el requisito de tamaño mínimo del grupo. Las conexiones se
agregan al grupo cuando es necesario, hasta el tamaño máximo del grupo especificado (100 es el valor
predeterminado), y se liberan de nuevo en el grupo cuando se cierran o eliminan.
Cuando se solicita un objeto SqlConnection, se obtiene del grupo si se encuentra disponible una conexión que se
pueda utilizar. Una conexión de este tipo debe estar sin utilizar, tener un contexto de transacción coincidente o no
estar asociada con ningún contexto de transacción y tener un vínculo válido al servidor.
El agrupador de conexiones satisface las solicitudes de conexión al reasignar las conexiones conforme se liberan
de nuevo en el grupo. Si se ha alcanzado el tamaño máximo del grupo y no hay disponible ninguna conexión que
se pueda utilizar, la solicitud se pone en la cola. A continuación, el concentrador intenta reclamar las conexiones
hasta que se agota el tiempo de espera (el valor predeterminado es 15 segundos). Si no puede satisfacer la
solicitud antes de que se agote el tiempo de espera de la conexión, se inicia una excepción.
Cau t i on

Se recomienda encarecidamente cerrar siempre la conexión cuando se termine de utilizar para que regrese al
grupo. Puede hacerlo mediante los Close Dispose métodos Connection o del objeto, o using abriendo todas las
Using conexiones dentro de una instrucción en C- o una instrucción en Visual Basic. Es posible que las conexiones
que no se cierran explícitamente no se puedan agregar ni puedan regresar al grupo. Para obtener más
información, vea Using Statement o How to: Dispose of a System Resource for Visual Basic.

NOTE
No llame a Close o a Dispose en un objeto Connection , un objeto DataReader o cualquier otro objeto administrado
en el método Finalize de la clase. En un finalizador, libere solo los recursos no administrados que pertenezcan
directamente a su clase. Si la clase no dispone de recursos no administrados, no incluya un método Finalize en la
definición de clase. Para obtener más información, consulte Recolección de elementos no utilizados.

Para obtener más información acerca de los eventos asociados con las conexiones de apertura y cierre, vea Clase
de evento de inicio de sesión de auditoría y Clase de evento de cierre de sesión de auditoría en la documentación
de SQL Server.

Cómo quitar conexiones


El agrupador de conexiones quita una conexión del grupo después de haber estado inactiva unos 4-8 minutos o si
detecta que se ha roto la conexión con el servidor. Tenga en cuenta que una conexión rota solo puede detectarse
después de intentar comunicarse con el servidor. Si se encuentra que una conexión ya no está conectada al
servidor, se marca como no válida. Las conexiones no válidas se quitan del grupo de conexión solo cuando se
cierran o reclaman.
Si existe una conexión en un servidor que ha desaparecido, se puede extraer del grupo aunque el agrupador de
conexiones no haya detectado la conexión rota y la haya marcado como no válida. El motivo es que la sobrecarga
de comprobar que la conexión es aún válida eliminaría los beneficios de tener un concentrador y ocasionaría que
se produjera otro viaje de ida y vuelta (round trip) al servidor. Cuando esto ocurre, el primer intento para usar la
conexión detectará que ésta se ha roto y se iniciará una excepción.

Borrado del grupo


ADO.NET 2.0 introdujo dos nuevos métodos para borrar el pool: ClearAllPools y ClearPool. ClearAllPools borra
los grupos de conexión de un proveedor dado, y ClearPool borra el grupo de conexión que está asociado a una
conexión concreta. Si en el momento de la llamada se están usando conexiones, se marcan de forma adecuada.
Cuando se cierran, se descartan en lugar de devolverse al grupo.

Compatibilidad con transacciones


Las conexiones se extraen del grupo y se asignan en función del contexto de transacción. A menos que se
especifique Enlist=false en la cadena de conexión, el grupo de conexión garantiza que la conexión está dada de
alta en el contexto de Current. Cuando se cierra una conexión y se devuelve al grupo con una transacción
System.Transactions dada de alta, se reserva de forma que la siguiente solicitud de ese grupo de conexiones con
la misma transacción System.Transactions devolverá la misma conexión, si está disponible. Si se emite dicha
solicitud y no hay conexiones agrupadas disponibles, se extrae una conexión de la parte sin transacción del grupo
y se le da de alta. Si no hay conexiones disponibles en cualquier área del grupo, se crea y da de alta una nueva
conexión.
Cuando se cierra una conexión, se libera de nuevo en el grupo y en la subdivisión adecuada en función de su
contexto de transacción. Por lo tanto, puede cerrar la conexión sin generar un error, incluso aunque aún haya
pendiente una transacción distribuida. Esto permite confirmar o anular la transacción distribuida más adelante.

Control de la agrupación de conexiones con palabras clave de cadena


de conexión
La propiedad ConnectionString del objeto SqlConnection admite pares clave-valor de cadena de conexión que se
pueden utilizar para ajustar el comportamiento de la lógica de agrupación de conexiones. Para más información,
consulte ConnectionString.

Fragmentación de grupos
La fragmentación de grupos es un problema común en muchas aplicaciones web en las que la aplicación puede
crear gran cantidad de grupos que no se liberan hasta que finaliza el proceso. El resultado es un gran número de
conexiones abiertas que consumen memoria, lo que da lugar a un bajo rendimiento.
Fragmentación de grupos debido a la seguridad integrada
Las conexiones se agrupan de acuerdo con la cadena de conexión y la identidad del usuario. Por lo tanto, si utiliza
autenticación básica o autenticación de Windows en el sitio web y un inicio de sesión de seguridad integrada,
obtendrá un grupo por usuario. Aunque de esta manera se mejora el rendimiento de las posteriores solicitudes de
base de datos de un solo usuario, ese usuario no podrá aprovechar las conexiones realizadas por otros usuarios.
Además, como resultado habrá una conexión como mínimo por usuario al servidor de bases de datos. Se trata de
un efecto secundario de una determinada arquitectura de aplicaciones web que los desarrolladores deben sopesar
frente a los requisitos de seguridad y auditoría.
Fragmentación de grupos debido a muchas bases de datos
Muchos proveedores de acceso a Internet hospedan varios sitios web en un único servidor. Puede que utilicen una
sola base de datos para confirmar un inicio de sesión de autenticación de formularios y luego abran una conexión
a una base de datos específica para ese usuario o grupo de usuarios. La conexión a la base de datos de
autenticación es agrupada y utilizada por todo el mundo. Sin embargo, hay un grupo independiente de conexiones
con cada base de datos, lo que implica un aumento del número de conexiones con el servidor.
Este es también un efecto secundario del diseño de la aplicación. Existe, sin embargo, una forma relativamente
sencilla de evitarlo sin comprometer la seguridad cuando se establece conexión con SQL Server. En lugar de
realizar una conexión a una base de datos diferente por cada usuario o grupo, realice una conexión a la misma
base de datos en el servidor y, a continuación, ejecute la instrucción USE de Transact-SQL para cambiar a la base
de datos deseada. En el siguiente fragmento de código se muestra la creación de una conexión inicial con la base
de datos master y el cambio a la base de datos deseada especificada en la variable de cadena databaseName .

' Assumes that command is a valid SqlCommand object and that


' connectionString connects to master.
command.Text = "USE DatabaseName"
Using connection As New SqlConnection(connectionString)
connection.Open()
command.ExecuteNonQuery()
End Using

// Assumes that command is a SqlCommand object and that


// connectionString connects to master.
command.Text = "USE DatabaseName";
using (SqlConnection connection = new SqlConnection(
connectionString))
{
connection.Open();
command.ExecuteNonQuery();
}

Roles de aplicación y agrupación de conexiones


Una vez activada una función de aplicación de SQL Server al llamar al procedimiento almacenado de sistema
sp_setapprole , no se puede restablecer el contexto de seguridad de la conexión. Sin embargo, cuando se habilita
la agrupación, la conexión se devuelve al grupo y se produce un error al utilizar de nuevo la conexión agrupada.
Para obtener más información, vea el artículo de Knowledge Base, "Errores de rol deaplicación SQL con
agrupaciónde recursos OLE DB ."
Alternativas a los roles de aplicación
Se recomienda aprovechar las ventajas de los mecanismos de seguridad que se pueden usar en lugar de roles de
aplicación. Para obtener más información, vea Crear roles de aplicación en SQL Server.

Consulte también
Agrupar conexiones
SQL Server y ADO.NET
Contadores de rendimiento
Información general de ADO.NET
Agrupación de conexiones de OLE DB, ODBC y
Oracle
08/01/2020 • 11 minutes to read • Edit Online

La agrupación de conexiones puede mejorar de forma considerable el rendimiento y la escalabilidad de la


aplicación. En esta sección se describe la agrupación de conexiones para los proveedores de datos de .NET
Framework para OLE DB, ODBC y Oracle.

OleDb
El proveedor de datos .NET Framework para OLE DB agrupa automáticamente las conexiones mediante la
agrupación de sesiones OLE DB. Se pueden utilizar argumentos de cadena de conexión para habilitar o
deshabilitar servicios OLE DB, incluida la agrupación. Por ejemplo, la siguiente cadena de conexión deshabilita la
agrupación de sesiones OLE DB y la inscripción automática de transacciones.

Provider=SQLOLEDB;OLE DB Services=-4;Data Source=localhost;Integrated Security=SSPI;

Se recomienda cerrar siempre o eliminar una conexión cuando termine de utilizarla, para que la conexión pueda
regresar al grupo. Es posible que las conexiones que no se cierran explícitamente no puedan regresar al grupo. Por
ejemplo, una conexión que se ha salido del ámbito pero que no se ha cerrado explícitamente solo se devolverá al
grupo de conexión si se ha alcanzado el tamaño máximo del grupo y la conexión aún es válida.
Para obtener más información sobre OLE DB la agrupación de sesiones o de recursos, y cómo deshabilitar la
agrupación invalidando los valores predeterminados del servicio de proveedor de OLE DB, consulte la Guía del
programador de OLE DB.

ODBC
La agrupación de conexiones para el proveedor de datos .NET Framework para ODBC se administra a través del
Administrador de controladores ODBC que se utiliza en la conexión, y que no está influido por dicho proveedor.
Para habilitar o deshabilitar la agrupación de conexiones, abra el Administrador de orígenes de datos ODBC
en la carpeta Herramientas administrativas del panel de control. La pestaña agrupación de conexiones permite
especificar parámetros de agrupación de conexiones para cada controlador ODBC instalado. Los cambios en la
agrupación de conexiones para un controlador ODBC específico afectan a todas las aplicaciones que utilizan ese
controlador ODBC.

OracleClient
El proveedor de datos .NET Framework para Oracle ofrece agrupación automática de conexiones para la aplicación
cliente de ADO.NET. También puede suministrar varios modificadores de cadena de conexión para controlar el
comportamiento de agrupación de conexiones (vea "Control de la agrupación de conexiones con palabras clave
de cadena de conexión", más adelante en este tema).
Crear y asignar grupos
Cuando se abre una conexión, se crea un grupo de conexión basado en un algoritmo de coincidencia exacta que
asocia el grupo con la cadena de conexión de la conexión. Cada grupo de conexión se asocia con una cadena de
conexión distinta. Si se abre una nueva conexión y la cadena de conexión no coincide exactamente con un grupo
existente, se crea un nuevo grupo.
Una vez creados, los grupos de conexión no se destruyen hasta que finaliza el proceso activo. Mantener grupos
inactivos o vacíos consume muy pocos recursos del sistema.
Adición de conexiones
Para cada cadena de conexión única se crea un grupo de conexión. Cuando se crea un grupo, se crean y agregan al
grupo varios objetos de conexión y se satisface así el requisito de tamaño mínimo del grupo. Las conexiones se
agregan al grupo cuando es necesario, hasta el tamaño máximo del grupo.
Cuando se solicita un objeto OracleConnection, se obtiene del grupo si se encuentra disponible una conexión que
se pueda utilizar. Una conexión de este tipo debe estar sin utilizar en ese momento, tener un contexto de
transacción coincidente o no estar asociada con ningún contexto de transacción, y tener un vínculo válido al
servidor.
Si se ha alcanzado el tamaño máximo del grupo y no hay disponible ninguna conexión que se pueda utilizar, la
solicitud se pone en la cola. El concentrador de conexión satisface estas solicitudes al reasignar las conexiones
conforme se liberan de nuevo en el grupo, lo cual ocurre y se liberan de nuevo en el grupo cuando se cierran o
eliminan.
Eliminación de conexiones
El agrupador de conexiones quita una conexión del grupo después de que haya estado inactiva durante un
período de tiempo prolongado o si el concentrador detecta que se ha roto la conexión con el servidor. Esto solo se
puede detectar después de intentar comunicarse con el servidor. Si se encuentra que una conexión ya no está
conectada al servidor, se marca como no válida. El concentrador de conexión analiza periódicamente los grupos en
busca de objetos que se han liberado en el grupo y marcado como no válidos. Luego, estas conexiones se quitan
de forma permanente.
Si existe una conexión a un servidor que ha desaparecido, se puede extraer del grupo si el concentrador de
conexión no ha detectado la conexión rota y la ha marcado como no válida. Cuando esto se produce, se genera
una excepción. No obstante, aun así deberá cerrar la conexión para liberarla de nuevo en el grupo.
No llame a Close o a Dispose en un objeto Connection , un objeto DataReader o cualquier otro objeto
administrado en el método Finalize de la clase. En un finalizador, libere solo los recursos no administrados que
pertenezcan directamente a su clase. Si la clase no dispone de recursos no administrados, no incluya un método
Finalize en la definición de clase. Para obtener más información, consulte recolección de elementos no
utilizados.
Compatibilidad con transacciones
Las conexiones se extraen del grupo y se asignan en función del contexto de transacción. Es necesario que el
subproceso solicitante y la conexión asignada coincidan. Por lo tanto, cada grupo de conexiones se subdivide en
conexiones sin contexto de transacción asociado y en N subdivisiones, cada una de las cuales contiene conexiones
con un contexto de transacción determinado.
Cuando se cierra una conexión, se libera de nuevo en el grupo y en la subdivisión adecuada en función de su
contexto de transacción. Por lo tanto, puede cerrar la conexión sin generar un error, incluso aunque aún haya
pendiente una transacción distribuida. Esto le permite confirmar o anular la transacción distribuida más adelante.
Controlar la agrupación de conexiones con palabras clave de cadena de conexión
La propiedad ConnectionString del objeto OracleConnection admite pares clave-valor de cadena de conexión que
se pueden utilizar para ajustar el comportamiento de la lógica de agrupación de conexiones.
En la siguiente tabla se describen los valores ConnectionString que puede utilizar para ajustar el comportamiento
de agrupación de conexiones.
NAME P REDET ERM IN A DO DESC RIP C IÓ N

Connection Lifetime 0 Cuando una conexión se devuelve al


grupo, su hora de creación se compara
con la hora actual y, si ese marco
temporal (en segundos) supera el valor
especificado por
Connection Lifetime , la conexión se
destruye. Esto resulta de utilidad en
configuraciones agrupadas para forzar
el equilibrio de carga entre un servidor
en ejecución y uno que acaba de
conectarse.

Un valor de cero (0) hará que las


conexiones agrupadas tengan el tiempo
de espera máximo.

Enlist 'true' Cuando es true , el concentrador


inscribe automáticamente la conexión
en el contexto de transacción actual del
subproceso de creación, si existe un
contexto de transacción.

Max Pool Size 100 El número máximo de conexiones


permitido en el grupo.

Min Pool Size 0 El número mínimo de conexiones


mantenido en el grupo.

Pooling 'true' Cuando es true , la conexión se extrae


del grupo adecuado o, si es necesario,
se crea y agrega al grupo correcto.

Vea también
Agrupación de conexiones
Performance Counters
Información general sobre ADO.NET
Comandos y parámetros
23/10/2019 • 3 minutes to read • Edit Online

Una vez establecida una conexión a un origen de datos, puede ejecutar comandos y devolver resultados desde el
mismo mediante un objeto DbCommand. Para crear un comando, puede utilizar uno de los constructores de
comando del proveedor de datos .NET Framework con el que esté trabajando. Los constructores pueden aceptar
argumentos opcionales, como una instrucción SQL para ejecutar en el origen de datos, un objeto DbConnection o
un objeto DbTransaction. También puede configurar dichos objetos como propiedades del comando. También
puede crear un comando para una determinada conexión mediante el método CreateCommand de un objeto
DbConnection . La instrucción SQL que ejecuta el comando se puede configurar mediante la propiedad
CommandText.
Cada proveedor de datos .NET Framework incluido en .NET Framework cuenta con un objeto Command : El
proveedor de datos .NET Framework para OLE DB incluye un objeto OleDbCommand, el proveedor de datos .NET
Framework para SQL Server incluye un objeto SqlCommand, el proveedor de datos .NET Framework para ODBC
incluye un objeto OdbcCommand y el proveedor de datos .NET Framework para Oracle incluye un objeto
OracleCommand.

En esta sección
Ejecución de un comando
Describe el objeto Command de ADO.NET, así como la forma de utilizarlo para ejecutar consultas y comandos con
respecto a un origen de datos.
Configuración de parámetros y tipos de datos de parámetros
Describe el trabajo con parámetros Command , incluidos dirección, tipo de datos y sintaxis de parámetros.
Generación de comandos con objetos CommandBuilder
Describe cómo utilizar generadores de comandos para generar automáticamente comandos INSERT, UPDATE y
DELETE para un DataAdapter que tiene un comando SELECT de tabla única.
Obtención de un valor único de una base de datos
Describe cómo utilizar el método ExecuteScalar de un objeto Command para devolver un único valor desde una
consulta de base de datos.
Uso de comandos para modificar datos
Describe cómo se utiliza un proveedor de datos para ejecutar procedimientos almacenados o instrucciones de
lenguaje de definición de datos (DDL).

Vea también
Objetos DataAdapter y DataReader
Objetos DataSet, DataTable y DataView
Conexión a un origen de datos
Información general sobre ADO.NET
Ejecutar un comando
23/10/2019 • 4 minutes to read • Edit Online

Cada proveedor de datos .NET Framework incluido en .NET Framework dispone de su propio objeto command que
hereda de DbCommand. El proveedor de datos .NET Framework para OLE DB incluye un objeto OleDbCommand,
el proveedor de datos .NET Framework para SQL Server incluye un objeto SqlCommand, el proveedor de datos
.NET Framework para ODBC incluye un objeto OdbcCommand y el proveedor de datos .NET Framework para
Oracle incluye un objeto OracleCommand. Cada uno de estos objetos expone métodos para ejecutar comandos
que se basan en el tipo de comando y el valor devuelto deseado, tal como se describe en la tabla siguiente.

GET - H EL P VA LO R DEVUELTO

ExecuteReader Devuelve un objeto DataReader .

ExecuteScalar Devuelve un solo valor escalar.

ExecuteNonQuery Ejecuta un comando que no devuelve ninguna fila.

ExecuteXMLReader Devuelve un valor XmlReader. Solo está disponible para un


objeto SqlCommand .

Cada objeto command fuertemente tipado admite también una enumeración CommandType que especifica cómo
se interpreta una cadena de comando, tal como se describe en la tabla siguiente.

C O M M A N DT Y P E DESC RIP C IÓ N

Text Comando de SQL que define las instrucciones que se van a


ejecutar en el origen de datos.

StoredProcedure Nombre del procedimiento almacenado. Puede usar la


propiedad Parameters de un comando para tener acceso a
los parámetros de entrada y de salida y a los valores
devueltos, independientemente del método Execute al que
se llame. Al usar ExecuteReader , no es posible el acceso a
los valores devueltos y los parámetros de salida hasta que se
cierra DataReader .

TableDirect Nombre de una tabla.

Ejemplo
En el ejemplo de código siguiente se muestra cómo se crea un objeto SqlCommand para ejecutar un
procedimiento almacenado mediante el establecimiento de sus propiedades. Para especificar el parámetro de
entrada del procedimiento almacenado se usa un objeto SqlParameter. El comando se ejecuta con el método
ExecuteReader y el resultado de SqlDataReader se muestra en la ventana de consola.
static void GetSalesByCategory(string connectionString,
string categoryName)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create the command and set its properties.
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "SalesByCategory";
command.CommandType = CommandType.StoredProcedure;

// Add the input parameter and set its properties.


SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;

// Add the parameter to the Parameters collection.


command.Parameters.Add(parameter);

// Open the connection and execute the reader.


connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}: {1:C}", reader[0], reader[1]);
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
}
Shared Sub GetSalesByCategory(ByVal connectionString As String, _
ByVal categoryName As String)

Using connection As New SqlConnection(connectionString)

' Create the command and set its properties.


Dim command As SqlCommand = New SqlCommand()
command.Connection = connection
command.CommandText = "SalesByCategory"
command.CommandType = CommandType.StoredProcedure

' Add the input parameter and set its properties.


Dim parameter As New SqlParameter()
parameter.ParameterName = "@CategoryName"
parameter.SqlDbType = SqlDbType.NVarChar
parameter.Direction = ParameterDirection.Input
parameter.Value = categoryName

' Add the parameter to the Parameters collection.


command.Parameters.Add(parameter)

' Open the connection and execute the reader.


connection.Open()
Using reader As SqlDataReader = command.ExecuteReader()

If reader.HasRows Then
Do While reader.Read()
Console.WriteLine("{0}: {1:C}", _
reader(0), reader(1))
Loop
Else
Console.WriteLine("No rows returned.")
End If
End Using
End Using
End Sub

Solución de problemas de comandos


El proveedor de datos .NET Framework para SQL Server agrega contadores de rendimiento que permiten detectar
problemas intermitentes relacionados con errores en la ejecución de comandos. Para obtener más información,
vea contadores de rendimiento.

Vea también
Comandos y parámetros
Objetos DataAdapter y DataReader
Información general sobre ADO.NET
Configurar parámetros y tipos de datos de
parámetro
23/10/2019 • 19 minutes to read • Edit Online

Los objetos de comando usan parámetros para pasar valores a instrucciones SQL o procedimientos almacenados
que permiten realizar operaciones de comprobación de tipos y validación. A diferencia del texto de comando, la
entrada de parámetros se trata como un valor literal, y no como código ejecutable. De esta forma, se protege
contra ataques por "inyección de código SQL", en los que un atacante inserta un comando que pone en peligro la
seguridad del servidor en una instrucción SQL.
Los comandos parametrizados también pueden mejorar el rendimiento de ejecución de la consulta, ya que ayudan
al servidor de bases de datos a que haga coincidir precisamente el comando entrante con un plan de consulta
almacenado en caché adecuado. Para obtener más información, consulte Plan de almacenamiento en caché en
ejecución y volver a utilizar y parámetros y reutilización de Plan de ejecución. Además de las ventajas en la
seguridad y el rendimiento, los comandos con parámetros proporcionan un método práctico para organizar los
valores que se pasan a un origen de datos.
Para crear un objeto DbParameter , se puede usar su constructor o bien se puede agregar a DbParameterCollection
mediante una llamada al método Add de la colección DbParameterCollection . El método Add acepta como
entrada argumentos del constructor o cualquier objeto de parámetro ya existente, en función del proveedor de
datos.

Proporcionar la propiedad ParameterDirection


Cuando se agregan parámetros distintos de los parámetros de entrada, se debe proporcionar una propiedad
ParameterDirection . En la tabla siguiente se muestran los valores de ParameterDirection que se pueden usar con
la enumeración ParameterDirection .

N O M B RE DE M IEM B RO DESC RIP C IÓ N

Input Se trata de un parámetro de entrada. Este es el valor


predeterminado.

InputOutput El parámetro se puede comportar tanto de entrada como de


salida.

Output Se trata de un parámetro de salida.

ReturnValue El parámetro representa un valor devuelto de una operación


como, por ejemplo, un procedimiento almacenado, una
función integrada o una función definida por el usuario.

Trabajar con los marcadores de posición


La sintaxis de los marcadores de posición de parámetros depende del origen de datos. Los proveedores de datos
.NET Framework administran la asignación de nombres y la especificación de parámetros y marcadores de
posición de parámetros de diferente forma. Esta sintaxis se personaliza para un origen de datos específico, como
se describe en la tabla siguiente.
P RO VEEDO R DE DATO S SIN TA XIS DE N O M EN C L AT URA DE PA RÁ M ET RO S

System.Data.SqlClient Usa parámetros con nombre, con el formato @


nombreDeParámetro.

System.Data.OleDb Usa marcadores de parámetro de posición, indicados por un


signo de interrogación ( ? ).

System.Data.Odbc Usa marcadores de parámetro de posición, indicados por un


signo de interrogación ( ? ).

System.Data.OracleClient Usa parámetros con nombre, con el formato :


nombreDeParámetro (o nombreDeParámetro).

Especificar tipos de datos de parámetro


El tipo de datos de un parámetro es específico del proveedor de datos de .NET Framework. Especificar el tipo
convierte el valor de la Parameter para el tipo de proveedor de datos de .NET Framework antes de pasar el valor
al origen de datos. Si lo desea, puede especificar el tipo de un objeto Parameter de forma genérica estableciendo
la propiedad DbType del objeto Parameter en un DbTypedeterminado.
El tipo de proveedor de datos de .NET Framework de un Parameter objeto se deriva del tipo de .NET Framework
de la Value de la Parameter objeto, o desde el DbType de la Parameter objeto. En la siguiente tabla se muestra el
tipo deducido de Parameter en función del objeto que se ha pasado como valor Parameter o del DbType
especificado.

T IP O DE . N ET
F RA M EW O RK DBT Y P E SQ L DBT Y P E O L EDBT Y P E O DB C T Y P E O RA C L ET Y P E

Boolean Booleano Bit Booleano Bit Byte

Byte Byte TinyInt UnsignedTinyInt TinyInt Byte

byte[] Binary VarBinary. Esta VarBinary Binary Raw


conversión
implícita generará
un error si la
matriz de bytes
es mayor que el
tamaño máximo
de un tipo
VarBinary, que es
de 8000 bytes.
Para las matrices
de bytes más de
8.000 bytes,
establezca
explícitamente la
SqlDbType.

Char No se admite la Char Char Byte


deducción de un
tipo SqlDbType a
partir de char.

DateTime DateTime DateTime DBTimeStamp DateTime DateTime


T IP O DE . N ET
F RA M EW O RK DBT Y P E SQ L DBT Y P E O L EDBT Y P E O DB C T Y P E O RA C L ET Y P E

DateTimeOffset DateTimeOffset DateTimeOffset DateTime


en SQL Server
2008. La
deducción de un
elemento
SqlDbType a
partir de
DateTimeOffset
no se admite en
versiones de SQL
Server anteriores
a SQL Server
2008.

Decimal Decimal Decimal Decimal Numérica número

Double Doble Float Doble Doble Doble

Single Single Real Single Real Float

Guid GUID UniqueIdentifier Guid UniqueIdentifier Raw

Int16 Int16 SmallInt SmallInt SmallInt Int16

Int32 Int32 Valor int. Valor int. Valor int. Int32

Int64 Int64 BigInt BigInt BigInt Número

Object Object Variante Variante No se admite la Blob


deducción de un
tipo OdbcType a
partir de Object.

String String NVarChar. Esta VarWChar NVarChar NVarChar


conversión
implícita generará
un error en el
caso de que la
cadena tenga un
tamaño superior
al tamaño
máximo de un
tipo NVarChar,
que es de 4.000
caracteres. En
cadenas con más
de 4.000
caracteres,
establezca de
forma explícita el
tipo SqlDbType.
T IP O DE . N ET
F RA M EW O RK DBT Y P E SQ L DBT Y P E O L EDBT Y P E O DB C T Y P E O RA C L ET Y P E

TimeSpan Tiempo Time en SQL DBTime Tiempo DateTime


Server 2008. La
deducción de un
elemento
SqlDbType a
partir de
TimeSpan no se
admite en
versiones de SQL
Server anteriores
a SQL Server
2008.

UInt16 UInt16 No se admite la UnsignedSmallInt Valor int. UInt16


deducción de un
tipo SqlDbType a
partir de UInt16.

UInt32 UInt32 No se admite la UnsignedInt BigInt UInt32


deducción de un
tipo SqlDbType a
partir de UInt32.

UInt64 UInt64 No se admite la UnsignedBigInt Numérica número


deducción de un
tipo SqlDbType a
partir de UInt64.

AnsiString VarChar VarChar VarChar VarChar

AnsiStringFixedLe Char Char Char Char


ngth

Moneda Money Moneda No es posible número


deducir el valor
de OdbcType a
partir de
Currency .

Fecha Date en SQL DBDate Fecha DateTime


Server 2008. La
deducción de un
elemento
SqlDbType a
partir de Date no
se admite en
versiones de SQL
Server anteriores
a SQL Server
2008.

SByte No se admite la TinyInt No se admite la SByte


deducción de un deducción de un
elemento tipo OdbcType a
SqlDbType a partir de SByte.
partir de SByte.
T IP O DE . N ET
F RA M EW O RK DBT Y P E SQ L DBT Y P E O L EDBT Y P E O DB C T Y P E O RA C L ET Y P E

StringFixedLengt NChar WChar NChar NChar


h

Tiempo Time en SQL DBTime Tiempo DateTime


Server 2008. La
deducción de un
elemento
SqlDbType a
partir de Time no
se admite en
versiones de SQL
Server anteriores
a SQL Server
2008.

VarNumeric No se admite la VarNumeric No se admite la número


deducción de un deducción de un
elemento tipo OdbcType a
SqlDbType a partir de
partir de VarNumeric.
VarNumeric.

tipo definido por Object o String SqlDbType.Udt si OleDbType.VarW OdbcType.NVarC no admitido


el usuario (un según el SqlUserDefinedTy Char (si el valor har
objeto con proveedor peAttribute está es NULL); de lo
SqlUserDefinedA (SqlClient presente; de lo contrario,
ggregateAttribut siempre devuelve contrario, Variant. OleDbType.Varian
e Object, ODBC t.
siempre devuelve
String y el
proveedor de
datos
administrados
OleDb puede ver
ambos)

NOTE
Las conversiones de valores de tipo decimal en otros tipos de valor son conversiones de restricción que redondean el valor
decimal al valor entero más próximo a cero. Si el resultado de la conversión no puede representarse en el tipo de destino, se
produce OverflowException .

NOTE
Cuando se envía un valor de parámetro null al servidor, debe especificar DBNull, no null ( Nothing en Visual Basic). El
valor nulo en el sistema es un objeto vacío que no tiene ningún valor. Para representar los valores nulos, se usaDBNull . Para
obtener más información sobre valores nulos de base de datos, consulte Handling Null Values.

Derivar información de parámetros


Los parámetros también se pueden derivar de un procedimiento almacenado mediante la clase DbCommandBuilder .
Las clases SqlCommandBuilder y OleDbCommandBuilder proporcionan un método estático, DeriveParameters , que
rellena automáticamente la colección de parámetros de un objeto de comando que usa información de
parámetros procedente de un procedimiento almacenado. Tenga en cuenta que DeriveParameters sobrescribirá
toda la información de parámetros existente en el comando.

NOTE
La derivación de información de parámetros afecta al rendimiento, ya que precisa un viaje adicional de ida y vuelta (round
trip) al origen de datos para recuperar la información. Si la información de los parámetros se conoce en tiempo de diseño, se
puede mejorar el rendimiento de la aplicación si se establecen los parámetros con los valores correspondientes de forma
explícita.

Para obtener más información, consulte generar comandos con objetos CommandBuilder.

Usar parámetros con SqlCommand y un procedimiento almacenado


Los procedimientos almacenados ofrecen numerosas ventajas en el caso de aplicaciones que procesan datos.
Mediante el uso de procedimientos almacenados, las operaciones de bases de datos se pueden encapsular en un
solo comando, optimizar para lograr el mejor rendimiento, y mejorar con seguridad adicional. Aunque se puede
llamar a un procedimiento almacenado pasando el nombre de procedimiento almacenado, seguido de
argumentos de parámetro como una instrucción SQL, utilizando el Parameters colección de ADO.NET
DbCommand objeto le permite definir más explícitamente el procedimiento almacenado los parámetros así como
para tener acceso a los parámetros de salida y valores devueltos.

NOTE
Las instrucciones con parámetros se ejecutan en el servidor utilizando sp_executesql, ; esto permite volver a utilizar el
plan de consultas. Los cursores o las variables locales del lote de sp_executesql no son visibles para el lote que llama a
sp_executesql . Los cambios en el contexto de base de datos solo se mantienen hasta el final de la instrucción
sp_executesql . Para obtener más información, consulte sp_executesql (Transact-SQL).

Cuando se usan parámetros con SqlCommand para ejecutar un procedimiento almacenado de SQL Server, los
nombres de los parámetros agregados a la colección Parameters deben coincidir con los nombres de los
marcadores de parámetro del procedimiento almacenado. El proveedor de datos de .NET Framework para SQL
Server no es compatible con el marcador de posición de signo de interrogación (?) para pasar parámetros a una
instrucción SQL o un procedimiento almacenado. Este proveedor trata los parámetros del procedimiento
almacenado como parámetros con nombre y busca marcadores de parámetros coincidentes. Por ejemplo, el
procedimiento almacenado CustOrderHist se define usando un parámetro denominado @CustomerID . Cuando el
código ejecuta el procedimiento almacenado, también debe usar un parámetro denominado @CustomerID .

CREATE PROCEDURE dbo.CustOrderHist @CustomerID varchar(5)

Ejemplo
En este ejemplo se muestra cómo llamar a un procedimiento almacenado de SQL Server en la base de datos de
ejemplo Northwind . El nombre del procedimiento almacenado es dbo.SalesByCategory e incluye un parámetro de
entrada denominado @CategoryName con el tipo de datos nvarchar(15) . El código crea una nueva clase
SqlConnection dentro de un bloque en uso, de forma que la conexión se cierre cuando finalice el procedimiento.
Se crean los objetos SqlCommand y SqlParameter , y se establecen sus propiedades. SqlDataReader ejecuta
SqlCommand y devuelve el conjunto de resultados del procedimiento almacenado, mostrándolos en la ventana de
consola.
NOTE
En lugar de crear objetos SqlCommand y SqlParameter y, a continuación, establecer propiedades en instrucciones
independientes, puede usar uno de los constructores sobrecargados para establecer varias propiedades en una única
instrucción.

static void GetSalesByCategory(string connectionString,


string categoryName)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create the command and set its properties.
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "SalesByCategory";
command.CommandType = CommandType.StoredProcedure;

// Add the input parameter and set its properties.


SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;

// Add the parameter to the Parameters collection.


command.Parameters.Add(parameter);

// Open the connection and execute the reader.


connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}: {1:C}", reader[0], reader[1]);
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
}
Shared Sub GetSalesByCategory(ByVal connectionString As String, _
ByVal categoryName As String)

Using connection As New SqlConnection(connectionString)

' Create the command and set its properties.


Dim command As SqlCommand = New SqlCommand()
command.Connection = connection
command.CommandText = "SalesByCategory"
command.CommandType = CommandType.StoredProcedure

' Add the input parameter and set its properties.


Dim parameter As New SqlParameter()
parameter.ParameterName = "@CategoryName"
parameter.SqlDbType = SqlDbType.NVarChar
parameter.Direction = ParameterDirection.Input
parameter.Value = categoryName

' Add the parameter to the Parameters collection.


command.Parameters.Add(parameter)

' Open the connection and execute the reader.


connection.Open()
Using reader As SqlDataReader = command.ExecuteReader()

If reader.HasRows Then
Do While reader.Read()
Console.WriteLine("{0}: {1:C}", _
reader(0), reader(1))
Loop
Else
Console.WriteLine("No rows returned.")
End If
End Using
End Using
End Sub

Usar parámetros con OleDbCommand o con OdbcCommand


Cuando se usan parámetros con OleDbCommand o con OdbcCommand, el orden de los parámetros agregados a
la colección Parameters debe coincidir con el de los parámetros definidos en el procedimiento almacenado. El
proveedor de datos de .NET Framework para OLE DB y el proveedor de datos de .NET Framework para ODBC
consideran los parámetros de un procedimiento almacenado como marcadores de posición y aplican los valores
de parámetros en orden. Además, los parámetros de valores devueltos deben ser los primeros que se agreguen a
la colección Parameters .
El proveedor de datos de .NET Framework para OLE DB y el proveedor de datos de .NET Framework para ODBC no
admiten parámetros con nombre para pasar parámetros a una instrucción SQL o un procedimiento almacenado.
En este caso, se debe utilizar el marcador de posición de signo interrogación de cierre (?), como se muestra en el
ejemplo siguiente.

SELECT * FROM Customers WHERE CustomerID = ?

Por eso, el orden en que se agregan los objetos Parameter a la colección Parameters debe coincidir exactamente
con la posición del marcador de posición de interrogación de cierre correspondiente al parámetro.
Ejemplo de OleDb
Dim command As OleDbCommand = New OleDbCommand( _
"SampleProc", connection)
command.CommandType = CommandType.StoredProcedure

Dim parameter As OleDbParameter = command.Parameters.Add( _


"RETURN_VALUE", OleDbType.Integer)
parameter.Direction = ParameterDirection.ReturnValue

parameter = command.Parameters.Add( _
"@InputParm", OleDbType.VarChar, 12)
parameter.Value = "Sample Value"

parameter = command.Parameters.Add( _
"@OutputParm", OleDbType.VarChar, 28)
parameter.Direction = ParameterDirection.Output

OleDbCommand command = new OleDbCommand("SampleProc", connection);


command.CommandType = CommandType.StoredProcedure;

OleDbParameter parameter = command.Parameters.Add(


"RETURN_VALUE", OleDbType.Integer);
parameter.Direction = ParameterDirection.ReturnValue;

parameter = command.Parameters.Add(
"@InputParm", OleDbType.VarChar, 12);
parameter.Value = "Sample Value";

parameter = command.Parameters.Add(
"@OutputParm", OleDbType.VarChar, 28);
parameter.Direction = ParameterDirection.Output;

Ejemplo de Odbc
Dim command As OdbcCommand = New OdbcCommand( _
"{ ? = CALL SampleProc(?, ?) }", connection)
command.CommandType = CommandType.StoredProcedure

Dim parameter As OdbcParameter = command.Parameters.Add("RETURN_VALUE", OdbcType.Int)


parameter.Direction = ParameterDirection.ReturnValue

parameter = command.Parameters.Add( _
"@InputParm", OdbcType.VarChar, 12)
parameter.Value = "Sample Value"

parameter = command.Parameters.Add( _
"@OutputParm", OdbcType.VarChar, 28)
parameter.Direction = ParameterDirection.Output
OdbcCommand command = new OdbcCommand( _
"{ ? = CALL SampleProc(?, ?) }", connection);
command.CommandType = CommandType.StoredProcedure;

OdbcParameter parameter = command.Parameters.Add( _


"RETURN_VALUE", OdbcType.Int);
parameter.Direction = ParameterDirection.ReturnValue;

parameter = command.Parameters.Add( _
"@InputParm", OdbcType.VarChar, 12);
parameter.Value = "Sample Value";

parameter = command.Parameters.Add( _
"@OutputParm", OdbcType.VarChar, 28);
parameter.Direction = ParameterDirection.Output;

Vea también
Comandos y parámetros
Parámetros de DataAdapter
Asignaciones de tipos de datos en ADO.NET
Información general sobre ADO.NET
Generar comandos con objetos CommandBuilder
21/03/2020 • 13 minutes to read • Edit Online

Cuando la propiedad SelectCommand se especifica de forma dinámica en tiempo de ejecución, por ejemplo a través
de una herramienta de consulta que acepta un comando de texto del usuario, existe la posibilidad de que no se
pueda especificar adecuadamente en tiempo de diseño el comando InsertCommand , UpdateCommand o
DeleteCommand correspondiente. Si el objeto DataTable se asigna a una única tabla de base de datos o se genera a
partir de ella, puede utilizar el objeto DbCommandBuilder para generar automáticamente las propiedades
DeleteCommand , InsertCommand y UpdateCommand de DbDataAdapter.

El requisito mínimo para que la generación automática de comandos funcione correctamente consiste en
establecer la propiedad SelectCommand . El esquema de tabla que recupera la propiedad SelectCommand determina
la sintaxis de las instrucciones INSERT, UPDATE y DELETE generadas automáticamente.
DbCommandBuilder debe ejecutar SelectCommand con el objeto de devolver los metadatos necesarios para
construir los comandos SQL INSERT, UPDATE y DELETE. Por eso es necesario realizar un viaje adicional al origen de
datos, con el consiguiente efecto adverso en el rendimiento. Para mejorar el rendimiento, debe especificar los
comandos de forma explícita, en lugar de utilizar DbCommandBuilder.
SelectCommand también debe devolver como mínimo una clave principal o una columna única. Si no hay ninguna,
se genera una excepción InvalidOperation y no se genera ningún comando.
Cuando se asocia con un objeto DataAdapter , DbCommandBuilder genera automáticamente las propiedades
InsertCommand , UpdateCommand y DeleteCommand del objeto DataAdapter si son referencias nulas. Si ya existe algún
objeto Command para una propiedad, se utilizará el objeto Command existente.
Las vistas de bases de datos creadas al unir una o varias tablas no se consideran una tabla única de base de datos.
En este caso no puede utilizar DbCommandBuilder para generar comandos automáticamente y deberá
especificarlos de manera explícita. Para obtener información sobre cómo establecer DataSet explícitamente
comandos para resolver actualizaciones de una copia posterior en el origen de datos, vea Actualizar orígenes de
datos con DataAdapters.
Es posible que desee asignar parámetros de salida a la fila actualizada de un DataSet . Una tarea habitual consiste
en recuperar, a partir del origen de datos, el valor de un campo de identidad de generación automática o una
marca de tiempo. DbCommandBuilder no asigna de forma predeterminada los parámetros de salida a las
columnas de una fila actualizada. En este caso, debe especificar el comando de forma explícita. Para obtener un
ejemplo de asignación de un campo de identidad generado automáticamente a una columna de una fila insertada,
vea Recuperar valores de identidad o numeración automática.

Reglas para comandos generados automáticamente


En la tabla siguiente se muestran las reglas de la generación automática de comandos.

GET - H EL P REGL A

InsertCommand Inserta una fila en el origen de datos para todas las filas de la
tabla con una RowState con el valor Added. Inserta valores
para todas las columnas actualizables, pero no para
determinadas columnas como identidades, expresiones o
marcas de tiempo.
GET - H EL P REGL A

UpdateCommand Actualiza filas en el origen de datos para todas las filas de la


tabla con una propiedad RowState con el valor Modified.
Actualiza los valores de todas las columnas, con excepción de
las que no son actualizables, como identidades o expresiones.
Actualiza todas las filas en las que los valores de columna en
el origen de datos coinciden con los valores de la columna de
clave principal de la fila, siempre que las restantes columnas
del origen de datos coincidan con los valores originales de la
fila. Para obtener más información, vea la sección "Modelo de
simultaneidad optimista para actualizaciones y eliminaciones"
de este mismo tema.

DeleteCommand Elimina filas en el origen de datos para todas las filas de la


tabla con una propiedad RowState con el valor Deleted.
Elimina todas las filas en las que los valores de columna
coinciden con los valores de la columna de clave principal de
la fila, siempre que las restantes columnas del origen de datos
coincidan con los valores originales de la fila. Para obtener
más información, vea la sección "Modelo de simultaneidad
optimista para actualizaciones y eliminaciones" de este mismo
tema.

Modelo de simultaneidad optimista para actualizaciones y


eliminaciones
La lógica para generar comandos automáticamente para las instrucciones UPDATE y DELETE se basa en la
simultaneidad optimista, es decir, los registros no están bloqueados para su edición y pueden ser modificados por
otros usuarios o procesos en cualquier momento. Dado que existe la posibilidad de que un registro haya sido
modificado después de que haya sido devuelto por la instrucción SELECT y antes de que se emita la instrucción
UPDATE o DELETE, la instrucción UPDATE o DELETE generada automáticamente incluye una cláusula WHERE que
especifica que la fila solo se actualiza cuando contiene todos los valores originales y no ha sido eliminada del
origen de datos. Esto evita que se sobrescriban los datos nuevos. Si una actualización generada automáticamente
intenta actualizar una fila que ha sido eliminada o que no contiene los valores originales del DataSet, el comando
no tienen ningún efecto en los registros y se inicia una excepción DBConcurrencyException.
Si desea que la instrucción UPDATE o DELETE se ejecute sin tener en cuenta los valores originales, debe establecer
de forma explícita la propiedad UpdateCommand del DataAdapter sin utilizar la generación automática de
comandos.

Limitaciones de la lógica de generación automática de comandos


La generación automática de comandos tiene las siguientes limitaciones.
Solo tablas no relacionadas
La lógica de generación automática de comandos crea instrucciones INSERT, UPDATE o DELETE para tablas
independientes sin tener en cuenta las relaciones que éstas puedan tener con otras tablas en el origen de datos.
Por eso, se puede producir un error al llamar a Update para realizar cambios en una columna que participa de
una restricción de clave externa en la base de datos. Para evitar esa excepción, no utilice DbCommandBuilder al
actualizar las columnas que participan en una restricción de clave externa. En este caso debe especificar de forma
explícita las instrucciones que se van a utilizar para llevar a cabo la operación.
Nombres de tabla y columna
La lógica de generación automática de comandos puede ocasionar un error cuando los nombres de las tablas o de
las columnas incluyen algún carácter especial, como espacios, puntos, comillas y otros caracteres no
alfanuméricos, incluso si están delimitados por corchetes. En función del proveedor, el establecimiento de los
parámetros QuotePrefix y QuoteSuffix puede permitir que la lógica de generación procese espacios, pero los
caracteres especiales no pueden convertirse en caracteres de escape. Se admiten nombres de tabla completos en
forma de catalog.schema.table.

Utilizar CommandBuilder para generar automáticamente una


instrucción SQL
Para generar instrucciones SQL automáticamente para un DataAdapter , defina en primer lugar la propiedad
SelectCommand del DataAdapter y, a continuación, cree un objeto CommandBuilder y especifique como argumento
el DataAdapter para el que CommandBuilder generará automáticamente las instrucciones SQL.

' Assumes that connection is a valid SqlConnection object


' inside of a Using block.
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT * FROM dbo.Customers", connection)
Dim builder As SqlCommandBuilder = New SqlCommandBuilder(adapter)
builder.QuotePrefix = "["
builder.QuoteSuffix = "]"

// Assumes that connection is a valid SqlConnection object


// inside of a using block.
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT * FROM dbo.Customers", connection);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
builder.QuotePrefix = "[";
builder.QuoteSuffix = "]";

Modificar SelectCommand
Es posible que se inicie una excepción si modifica valor CommandText de SelectCommand después de generar
automáticamente los comandos INSERT, UPDATE o DELETE. Si el valor SelectCommand.CommandText modificado
contiene información del esquema que sea incoherente con el valor SelectCommand.CommandText utilizado en el
momento de la generación automática de los comandos de inserción, actualización o eliminación, las futuras
llamadas al método DataAdapter.Update pueden tratar de obtener acceso a columnas que ya no existen en la tabla
actual a la que hace referencia SelectCommand , con lo que se iniciará una excepción.
Puede actualizar la información del esquema que utiliza CommandBuilder para generar automáticamente los
comandos; para ello, basta con llamar al método RefreshSchema de CommandBuilder .
Si desea conocer el comando generado automáticamente, puede obtener una referencia a los comandos
generados automáticamente mediante los métodos GetInsertCommand , GetUpdateCommand y GetDeleteCommand del
objeto CommandBuilder y la comprobación de la propiedad CommandText del comando asociado.
En el ejemplo de código siguiente se escribe en la consola el comando de actualización generado
automáticamente.

Console.WriteLine(builder.GetUpdateCommand().CommandText)

Console.WriteLine(builder.GetUpdateCommand().CommandText);

En el siguiente ejemplo se vuelve a crear la tabla Customers en el conjunto de datos custDS . El RefreshSchema
se llama al método para actualizar los comandos generados automáticamente con esta nueva información de
columna.

' Assumes an open SqlConnection and SqlDataAdapter inside of a Using block.


adapter.SelectCommand.CommandText = _
"SELECT CustomerID, ContactName FROM dbo.Customers"
builder.RefreshSchema()

custDS.Tables.Remove(custDS.Tables("Customers"))
adapter.Fill(custDS, "Customers")

// Assumes an open SqlConnection and SqlDataAdapter inside of a using block.


adapter.SelectCommand.CommandText =
"SELECT CustomerID, ContactName FROM dbo.Customers";
builder.RefreshSchema();

custDS.Tables.Remove(custDS.Tables["Customers"]);
adapter.Fill(custDS, "Customers");

Consulte también
Comandos y parámetros
Ejecución de un comando
DbConnection, DbCommand y DbException
Información general de ADO.NET
Obtener un valor único de una base de datos
23/10/2019 • 2 minutes to read • Edit Online

En ocasiones se debe devolver información de bases de datos consistente en un único valor, en lugar de una tabla o
un flujo de datos. Por ejemplo, puede que desee devolver el resultado de una función de agregado como Count (*),
SUM (Price) o AVG (quantity). El objeto Command proporciona la capacidad de devolver valores únicos mediante
el método ExecuteScalar . El método ExecuteScalar devuelve, como un valor escalar, el valor de la primera
columna de la primera fila del conjunto de resultados.
El ejemplo de código siguiente inserta un valor nuevo en la base de datos utilizando SqlCommand. El método
ExecuteScalar se utiliza para devolver el valor de columna de identidad para el registro insertado.

static public int AddProductCategory(string newName, string connString)


{
Int32 newProdID = 0;
string sql =
"INSERT INTO Production.ProductCategory (Name) VALUES (@Name); "
+ "SELECT CAST(scope_identity() AS int)";
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("@Name", SqlDbType.VarChar);
cmd.Parameters["@name"].Value = newName;
try
{
conn.Open();
newProdID = (Int32)cmd.ExecuteScalar();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return (int)newProdID;
}

Public Function AddProductCategory( _


ByVal newName As String, ByVal connString As String) As Integer
Dim newProdID As Int32 = 0
Dim sql As String = _
"INSERT INTO Production.ProductCategory (Name) VALUES (@Name); " _
& "SELECT CAST(scope_identity() AS int);"

Using conn As New SqlConnection(connString)


Dim cmd As New SqlCommand(sql, conn)
cmd.Parameters.Add("@Name", SqlDbType.VarChar)
cmd.Parameters("@Name").Value = newName
Try
conn.Open()
newProdID = Convert.ToInt32(cmd.ExecuteScalar())
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using

Return newProdID
End Function
Vea también
Comandos y parámetros
Ejecución de un comando
DbConnection, DbCommand y DbException
Información general sobre ADO.NET
Usar comandos para modificar datos
23/10/2019 • 2 minutes to read • Edit Online

Mediante un proveedor de datos .NET Framework puede ejecutar procedimientos almacenados o instrucciones de
lenguaje de definición de datos, como CREATE TABLE y ALTER COLUMN, para manipular los esquemas de una base
de datos o catálogo. Estos comandos no devuelven filas como lo haría una consulta, por lo que el objeto de
comando proporciona un ExecuteNonQuer y para procesarlos.
Además de usar ExecuteNonQuer y para modificar el esquema, también puede utilizar este método para
procesar instrucciones SQL que modifican datos pero que no devuelven filas, como INSERT, Update y DELETE.
Aunque el método ExecuteNonQuer y no devuelve las filas, los parámetros de entrada y salida y los valores
devueltos se pueden pasar y devolver a través de la colección Parameters del objeto Command .

En esta sección
Actualización de datos de un origen de datos
Describe la forma de ejecutar comandos o procedimientos almacenados que modifican datos en una base de
datos.
Realización de operaciones de catálogo
Describe la forma de ejecutar comandos que modifican esquemas de la base de datos.

Vea también
Recuperar y modificar datos en ADO.NET
Comandos y parámetros
Información general sobre ADO.NET
Actualizar datos de un origen de datos
21/03/2020 • 3 minutes to read • Edit Online

Las instrucciones SQL que modifican datos (por ejemplo INSERT, UPDATE o DELETE) no devuelven ninguna fila. De
la misma forma, muchos procedimientos almacenados realizan alguna acción pero no devuelven filas. Para
ejecutar comandos que no devuelven filas, cree un objeto Command con el comando SQL adecuado y una
conexión , incluidos los parámetros necesarios. Ejecute el comando con el método ExecuteNonQuer y del
objeto Command.
El Método ExecuteNonQuer y devuelve un entero que representa el número de filas afectadas por la instrucción
o el procedimiento almacenado que se ejecutó. Si se ejecutan varias instrucciones, el valor devuelto es la suma de
los registros afectados por todas las instrucciones ejecutadas.

Ejemplo
En el ejemplo de código siguiente se ejecuta una instrucción INSERT para insertar un registro en una base de datos
mediante ExecuteNonQuer y .

' Assumes connection is a valid SqlConnection.


connection.Open()

Dim queryString As String = "INSERT INTO Customers " & _


"(CustomerID, CompanyName) Values('NWIND', 'Northwind Traders')"

Dim command As SqlCommand = New SqlCommand(queryString, connection)


Dim recordsAffected As Int32 = command.ExecuteNonQuery()

// Assumes connection is a valid SqlConnection.


connection.Open();

string queryString = "INSERT INTO Customers " +


"(CustomerID, CompanyName) Values('NWIND', 'Northwind Traders')";

SqlCommand command = new SqlCommand(queryString, connection);


Int32 recordsAffected = command.ExecuteNonQuery();

En el ejemplo de código siguiente se ejecuta el procedimiento almacenado creado por el código de ejemplo en
Realizar operacionesde catálogo . El procedimiento almacenado no devuelve ninguna fila, por lo que se usa el
método ExecuteNonQuer y, pero el procedimiento almacenado recibe un parámetro de entrada y devuelve un
parámetro de salida y un valor devuelto.
Para OleDbCommand el objeto, el parámetro ReturnValue debe agregarse primero a la colección Parameters.
' Assumes connection is a valid SqlConnection.
Dim command As SqlCommand = _
New SqlCommand("InsertCategory" , connection)
command.CommandType = CommandType.StoredProcedure

Dim parameter As SqlParameter = _


command.Parameters.Add("@RowCount", SqlDbType.Int)
parameter.Direction = ParameterDirection.ReturnValue

parameter = command.Parameters.Add( _
"@CategoryName", SqlDbType.NChar, 15)

parameter = command.Parameters.Add("@Identity", SqlDbType.Int)


parameter.Direction = ParameterDirection.Output

command.Parameters("@CategoryName").Value = "New Category"


command.ExecuteNonQuery()

Dim categoryID As Int32 = CInt(command.Parameters("@Identity").Value)


Dim rowCount As Int32 = CInt(command.Parameters("@RowCount").Value)

// Assumes connection is a valid SqlConnection.


SqlCommand command = new SqlCommand("InsertCategory" , connection);
command.CommandType = CommandType.StoredProcedure;

SqlParameter parameter = command.Parameters.Add(


"@RowCount", SqlDbType.Int);
parameter.Direction = ParameterDirection.ReturnValue;

parameter = command.Parameters.Add(
"@CategoryName", SqlDbType.NChar, 15);

parameter = command.Parameters.Add("@Identity", SqlDbType.Int);


parameter.Direction = ParameterDirection.Output;

command.Parameters["@CategoryName"].Value = "New Category";


command.ExecuteNonQuery();

Int32 categoryID = (Int32) command.Parameters["@Identity"].Value;


Int32 rowCount = (Int32) command.Parameters["@RowCount"].Value;

Consulte también
Uso de comandos para modificar datos
Actualizar orígenes de datos con objetos DataAdapter
Comandos y parámetros
Información general de ADO.NET
Realizar operaciones de catálogo
21/03/2020 • 2 minutes to read • Edit Online

Para ejecutar un comando para modificar una base de datos o un catálogo, como la instrucción CREATE TABLE o
CREATE PROCEDURE, cree un objeto Command utilizando las instrucciones SQL adecuadas y un objeto
Connection. Ejecute el comando con el método ExecuteNonQuer y del objeto Command.
En el ejemplo de código siguiente se crea un procedimiento almacenado en una base de datos de Microsoft SQL
Server.

' Assumes connection is a valid SqlConnection.


Dim queryString As String = "CREATE PROCEDURE InsertCategory " & _
"@CategoryName nchar(15), " & _
"@Identity int OUT " & _
"AS " & _
"INSERT INTO Categories (CategoryName) VALUES(@CategoryName) " & _
"SET @Identity = @@Identity " & _
"RETURN @@ROWCOUNT"

Dim command As SqlCommand = New SqlCommand(queryString, connection)


command.ExecuteNonQuery()

// Assumes connection is a valid SqlConnection.


string queryString = "CREATE PROCEDURE InsertCategory " +
"@CategoryName nchar(15), " +
"@Identity int OUT " +
"AS " +
"INSERT INTO Categories (CategoryName) VALUES(@CategoryName) " +
"SET @Identity = @@Identity " +
"RETURN @@ROWCOUNT";

SqlCommand command = new SqlCommand(queryString, connection);


command.ExecuteNonQuery();

Consulte también
Uso de comandos para modificar datos
Comandos y parámetros
Información general de ADO.NET
Objetos DataAdapter y DataReader
23/10/2019 • 4 minutes to read • Edit Online

Puede usar ADO.NET DataReader para recuperar una secuencia de datos de solo lectura y de solo avance desde
una base de datos. Los resultados se devuelven cuando se ejecuta la consulta y se almacenan en el búfer de red
en el cliente hasta que se solicitan mediante el método Read del DataReader . El uso de DataReader puede
aumentar el rendimiento de la aplicación al recuperar los datos tan pronto como estén disponibles y, de forma
predeterminada, almacenar solo una fila a la vez en la memoria, lo que reduce la sobrecarga del sistema.
Un DataAdapter se utiliza para recuperar datos de un origen de datos y llenar tablas con un DataSet.
DataAdapter también resuelve los cambios realizados en DataSet de vuelta al origen de datos. Mediante el
objeto DataAdapter del proveedor de datos .NET Framework, Connection se conecta a un origen de datos y
utiliza objetos Command para recuperar datos del origen de datos y resolver los cambios a dicho origen.
Cada proveedor de datos .NET Framework incluido con .NET Framework tiene un objeto DbDataReader y un
objeto DbDataAdapter, el proveedor de datos .NET Framework para OLE DB incluye un objeto OleDbDataReader
y un objeto OleDbDataAdapter, el proveedor de datos .NET Framework para SQL Server incluye un objeto
SqlDataReader y un objeto SqlDataAdapter, el proveedor de datos .NET Framework para ODBC incluye un objeto
OdbcDataReader y un objeto OdbcDataAdapter, y el proveedor de datos .NET Framework para Oracle incluyes
un objeto OracleDataReader y un objeto OracleDataAdapter.

En esta sección
Recuperación de datos utilizando un objeto DataReader
Describe el objeto datareader ADO.net y cómo usarlo para devolver una secuencia de resultados de un origen
de datos.
Rellenar un conjunto de datos desde un objeto DataAdapter
Describe cómo llenar un DataSet de tablas, columnas y filas mediante un DataAdapter .
Parámetros de DataAdapter
Describe cómo utilizar parámetros con las propiedades de comando de DataAdapter , lo que incluye cómo
asignar el contenido de una columna de DataSet a un parámetro de comando.
Agregar restricciones existentes a un conjunto de datos
Describe cómo agregar restricciones existentes a un DataSet .
Correspondencias de DataTable y DataColumn en un objeto DataAdapter
Describe cómo configurar DataTableMappings y ColumnMappings para DataAdapter .
Paginación de un resultado de consulta
Proporciona un ejemplo de cómo ver los resultados de una consulta como páginas de datos.
Actualizar orígenes de datos con objetos DataAdapter
Describe cómo se utiliza DataAdapter para resolver modificaciones en DataSet en la base de datos.
Control de eventos de DataAdapter
Describe los eventos de DataAdapter y cómo utilizarlos.
Realización de operaciones por lotes utilizando objetos DataAdapter
Describe cómo mejorar el rendimiento de la aplicación mediante la reducción del número de viajes de ida y
vuelta (round trip) al servidor SQL Server al aplicar las actualizaciones desde el DataSet .
Vea también
Conexión a un origen de datos
Comandos y parámetros
Transacciones y simultaneidad
Objetos DataSet, DataTable y DataView
Información general sobre ADO.NET
Recuperar datos mediante un DataReader
21/03/2020 • 13 minutes to read • Edit Online

Para recuperar datos mediante un DataReader , cree una instancia de la Command objeto y, a continuación, cree
un DataReader mediante una llamada a Command.ExecuteReader para recuperar filas de un origen de datos.
DataReader proporciona un flujo de datos sin búfer que permite que la lógica de procedimientos procese
eficazmente los resultados de un origen de datos secuencialmente. DataReader es una buena opción cuando se
recuperan grandes cantidades de datos porque los datos no se almacenan en caché en la memoria.
En el ejemplo siguiente se muestra reader el uso de command un DataReader , donde representa un DataReader
válido y representa un objeto Command válido.

reader = command.ExecuteReader();

reader = command.ExecuteReader()

Utilice el método DataReader.Read para obtener una fila de los resultados de la consulta. Puede tener acceso a
cada columna de la fila devuelta pasando el nombre o el número ordinal de la columna al DataReader . Sin
embargo, para obtener el mejor rendimiento, DataReader proporciona una serie de métodos que permiten
tener acceso a los valores de columna en sus tipos de datos nativos (GetDateTime , GetDouble , GetGuid ,
GetInt32 , etc.). Para obtener una lista de métodos de descriptor OleDbDataReader de SqlDataReaderacceso con
tipo para DataReaders específicos del proveedor de datos , vea y . El uso de los métodos de descriptor de acceso
con tipo cuando conoce el tipo de datos subyacente reduce la cantidad de conversión de tipos necesaria al
recuperar el valor de columna.
En el ejemplo siguiente se recorre en iteración un DataReader objeto y devuelve dos columnas de cada fila.

static void HasRows(SqlConnection connection)


{
using (connection)
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM Categories;",
connection);
connection.Open();

SqlDataReader reader = command.ExecuteReader();

if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
reader.GetString(1));
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
Private Sub HasRows(ByVal connection As SqlConnection)
Using connection
Dim command As SqlCommand = New SqlCommand( _
"SELECT CategoryID, CategoryName FROM Categories;", _
connection)
connection.Open()

Dim reader As SqlDataReader = command.ExecuteReader()

If reader.HasRows Then
Do While reader.Read()
Console.WriteLine(reader.GetInt32(0) _
& vbTab & reader.GetString(1))
Loop
Else
Console.WriteLine("No rows found.")
End If

reader.Close()
End Using
End Sub

Cerrar el DataReader
Llame siempre a la Close método cuando haya terminado de usar el DataReader objeto.
Si el comando contiene parámetros de salida o valores devueltos, esos valores no están disponibles hasta que
se cierra DataReader.
Mientras un DataReader está abierto, la conexión está en uso exclusivamente por ese DataReader . No puede
ejecutar ningún comando para Connection , incluida la creación de otro DataReader , hasta que se cierre el
DataReader original.

NOTE
No llame a Close o Dispose en una conexión , un DataReader o cualquier otro objeto administrado en el Finalize
método de la clase. En un finalizador, libere solo los recursos no administrados que pertenezcan directamente a su clase. Si
la clase no posee ningún recurso no administrado, no incluya un método Finalize en la definición de clase. Para obtener
más información, consulte Recolección de elementos no utilizados.

Recuperación de varios conjuntos de resultados mediante NextResult


Si el DataReader devuelve varios conjuntos de resultados, llame a la NextResult método para recorrer en
iteración los conjuntos de resultados secuencialmente. En el siguiente ejemplo se muestra el SqlDataReader
mientras procesa los resultados de las dos instrucciones SELECT mediante el método ExecuteReader.
static void RetrieveMultipleResults(SqlConnection connection)
{
using (connection)
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM dbo.Categories;" +
"SELECT EmployeeID, LastName FROM dbo.Employees",
connection);
connection.Open();

SqlDataReader reader = command.ExecuteReader();

while (reader.HasRows)
{
Console.WriteLine("\t{0}\t{1}", reader.GetName(0),
reader.GetName(1));

while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0),
reader.GetString(1));
}
reader.NextResult();
}
}
}

Private Sub RetrieveMultipleResults(ByVal connection As SqlConnection)


Using connection
Dim command As SqlCommand = New SqlCommand( _
"SELECT CategoryID, CategoryName FROM Categories;" & _
"SELECT EmployeeID, LastName FROM Employees", connection)
connection.Open()

Dim reader As SqlDataReader = command.ExecuteReader()

Do While reader.HasRows
Console.WriteLine(vbTab & reader.GetName(0) _
& vbTab & reader.GetName(1))

Do While reader.Read()
Console.WriteLine(vbTab & reader.GetInt32(0) _
& vbTab & reader.GetString(1))
Loop

reader.NextResult()
Loop
End Using
End Sub

Obtener información de esquema del DataReader


Mientras un DataReader está abierto, puede recuperar información de esquema sobre el conjunto de resultados
actual mediante el GetSchemaTable método. GetSchemaTable devuelve DataTable un objeto rellenado con
filas y columnas que contienen la información de esquema para el conjunto de resultados actual. DataTable
contiene una fila para cada columna del conjunto de resultados. Cada columna de la tabla de esquema se asigna
a una propiedad de las columnas devueltas en las filas del conjunto de resultados, donde ColumnName es el
nombre de la propiedad y el valor de la columna es el valor de la propiedad. En el ejemplo siguiente se escribe la
información de esquema para DataReader .
static void GetSchemaInfo(SqlConnection connection)
{
using (connection)
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM Categories;",
connection);
connection.Open();

SqlDataReader reader = command.ExecuteReader();


DataTable schemaTable = reader.GetSchemaTable();

foreach (DataRow row in schemaTable.Rows)


{
foreach (DataColumn column in schemaTable.Columns)
{
Console.WriteLine(String.Format("{0} = {1}",
column.ColumnName, row[column]));
}
}
}
}

Private Sub GetSchemaInfo(ByVal connection As SqlConnection)


Using connection
Dim command As SqlCommand = New SqlCommand( _
"SELECT CategoryID, CategoryName FROM Categories;", _
connection)
connection.Open()

Dim reader As SqlDataReader = command.ExecuteReader()


Dim schemaTable As DataTable = reader.GetSchemaTable()

Dim row As DataRow


Dim column As DataColumn

For Each row In schemaTable.Rows


For Each column In schemaTable.Columns
Console.WriteLine(String.Format("{0} = {1}", _
column.ColumnName, row(column)))
Next
Console.WriteLine()
Next
reader.Close()
End Using
End Sub

Trabajar con capítulos OLE DB


Los conjuntos de filas jerárquicos o los capítulos (tipo OleDbDataReaderOLE DB DBTYPE_HCHAPTER , tipo aDO
adChapter ), se pueden recuperar mediante el archivo . Cuando una consulta que incluye un capítulo se devuelve
como un DataReader , el capítulo se devuelve como una columna en ese DataReader y se expone como un
DataReader objeto.
El ADO.NET DataSet también se puede utilizar para representar conjuntos de filas jerárquicos mediante el uso de
relaciones primario-secundario entre tablas. Para obtener más información, vea DataSets, DataTables y
DataViews.
En el ejemplo de código siguiente se utiliza el proveedor MSDataShape para generar un capítulo con la columna
de pedidos realizados por cada uno de los clientes de una lista.
Using connection As OleDbConnection = New OleDbConnection(
"Provider=MSDataShape;Data Provider=SQLOLEDB;" &
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

Using custCMD As OleDbCommand = New OleDbCommand(


"SHAPE {SELECT CustomerID, CompanyName FROM Customers} " &
"APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " &
"RELATE CustomerID TO CustomerID)", connection)

connection.Open()

Using custReader As OleDbDataReader = custCMD.ExecuteReader()

Do While custReader.Read()
Console.WriteLine("Orders for " & custReader.GetString(1))
' custReader.GetString(1) = CompanyName

Using orderReader As OleDbDataReader = custReader.GetValue(2)


' custReader.GetValue(2) = Orders chapter as DataReader

Do While orderReader.Read()
Console.WriteLine(vbTab & orderReader.GetInt32(1))
' orderReader.GetInt32(1) = OrderID
Loop
orderReader.Close()
End Using
Loop
' Make sure to always close readers and connections.
custReader.Close()
End Using
End Using
End Using
using (OleDbConnection connection = new OleDbConnection(
"Provider=MSDataShape;Data Provider=SQLOLEDB;" +
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind"))
{
using (OleDbCommand custCMD = new OleDbCommand(
"SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +
"APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " +
"RELATE CustomerID TO CustomerID)", connection))
{
connection.Open();

using (OleDbDataReader custReader = custCMD.ExecuteReader())


{

while (custReader.Read())
{
Console.WriteLine("Orders for " + custReader.GetString(1));
// custReader.GetString(1) = CompanyName

using (OleDbDataReader orderReader = (OleDbDataReader)custReader.GetValue(2))


{
// custReader.GetValue(2) = Orders chapter as DataReader

while (orderReader.Read())
Console.WriteLine("\t" + orderReader.GetInt32(1));
// orderReader.GetInt32(1) = OrderID
orderReader.Close();
}
}
// Make sure to always close readers and connections.
custReader.Close();
}
}
}

Devolución de resultados con Oracle REF CURSOR


El proveedor de datos .NET Framework para Oracle admite el uso de cursores REF CURSOR de Oracle para
devolver los resultados de una consulta. Un REF CURSOR de Oracle se devuelve en forma de objeto
OracleDataReader.
Puede recuperar OracleDataReader un objeto que representa un REF ExecuteReader CURSOR de Oracle mediante
el método. También puede especificar OracleCommand un que devuelve uno o varios CURSORes
OracleDataAdapter ref de DataSetOracle como SelectCommand para un archivo .
Para tener acceso a un REF CURSOR OracleCommand devuelto desde un origen de datos de Oracle, cree un para
la consulta y agregue un parámetro de salida que haga referencia a REF CURSOR a la Parameters colección de su
OracleCommandarchivo . El nombre del parámetro debe coincidir con el nombre del parámetro REF CURSOR de
la consulta. Establezca el tipo del OracleType.Cursorparámetro en . El OracleCommand.ExecuteReader() método
OracleCommand de OracleDataReader sus devuelve un para el REF CURSOR.
Si OracleCommand devuelve varios REF CURSORS, agregue varios parámetros de salida. Puede acceder a los
diferentes REF OracleCommand.ExecuteReader() CURSOR llamando al método. La llamada ExecuteReader() a
OracleDataReader devuelve una referencia al primer REF CURSOR. A continuación, OracleDataReader.NextResult()
puede llamar al método para acceder a ref CURSOR posteriores. Aunque los parámetros de la colección
OracleCommand.Parameters coinciden OracleDataReader con los parámetros de salida REF Parameters CURSOR
por nombre, el acceso a ellos en el orden en que se agregaron a la colección.
Por ejemplo, considere el siguiente paquete de Oracle y, concretamente, el cuerpo del paquete.
CREATE OR REPLACE PACKAGE CURSPKG AS
TYPE T_CURSOR IS REF CURSOR;
PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
DEPTCURSOR OUT T_CURSOR);
END CURSPKG;

CREATE OR REPLACE PACKAGE BODY CURSPKG AS


PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
DEPTCURSOR OUT T_CURSOR)
IS
BEGIN
OPEN EMPCURSOR FOR SELECT * FROM DEMO.EMPLOYEE;
OPEN DEPTCURSOR FOR SELECT * FROM DEMO.DEPARTMENT;
END OPEN_TWO_CURSORS;
END CURSPKG;

El código siguiente OracleCommand crea un que devuelve los REF CURSOR del paquete OracleType.Cursor de
OracleCommand.Parameters Oracle anterior agregando dos parámetros de tipo a la colección.

Dim cursCmd As OracleCommand = New OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn)


cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output

OracleCommand cursCmd = new OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn);


cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;

El código siguiente devuelve los resultados Read() del NextResult() comando OracleDataReaderanterior mediante
los métodos y del archivo . Los parámetros REF CURSOR se devuelven en orden.

oraConn.Open()

Dim cursCmd As OracleCommand = New OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn)


cursCmd.CommandType = CommandType.StoredProcedure
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output

Dim reader As OracleDataReader = cursCmd.ExecuteReader()

Console.WriteLine(vbCrLf & "Emp ID" & vbTab & "Name")

Do While reader.Read()
Console.WriteLine("{0}" & vbTab & "{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1),
reader.GetString(2))
Loop

reader.NextResult()

Console.WriteLine(vbCrLf & "Dept ID" & vbTab & "Name")

Do While reader.Read()
Console.WriteLine("{0}" & vbTab & "{1}", reader.GetOracleNumber(0), reader.GetString(1))
Loop
' Make sure to always close readers and connections.
reader.Close()
oraConn.Close()
oraConn.Open();

OracleCommand cursCmd = new OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn);


cursCmd.CommandType = CommandType.StoredProcedure;
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;

OracleDataReader reader = cursCmd.ExecuteReader();

Console.WriteLine("\nEmp ID\tName");

while (reader.Read())
Console.WriteLine("{0}\t{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1), reader.GetString(2));

reader.NextResult();

Console.WriteLine("\nDept ID\tName");

while (reader.Read())
Console.WriteLine("{0}\t{1}", reader.GetOracleNumber(0), reader.GetString(1));
// Make sure to always close readers and connections.
reader.Close();
oraConn.Close();

En el ejemplo siguiente se DataSet utiliza el comando anterior para rellenar a con los resultados del paquete de
Oracle.

Dim ds As DataSet = New DataSet()

Dim adapter As OracleDataAdapter = New OracleDataAdapter(cursCmd)


adapter.TableMappings.Add("Table", "Employees")
adapter.TableMappings.Add("Table1", "Departments")

adapter.Fill(ds)

DataSet ds = new DataSet();

OracleDataAdapter adapter = new OracleDataAdapter(cursCmd);


adapter.TableMappings.Add("Table", "Employees");
adapter.TableMappings.Add("Table1", "Departments");

adapter.Fill(ds);

NOTE
Para evitar una OverflowException , se recomienda controlar también cualquier conversión del tipo Oracle NUMBER a
DataRowun tipo válido de .NET Framework antes de almacenar el valor en un archivo . Puede usar FillError el evento para
determinar si se ha producido una excepción OverflowException. Para obtener más FillError información sobre el
evento, vea Controlar eventos DataAdapter.

Consulte también
Objetos DataAdapter y DataReader
Comandos y parámetros
Recuperación de información del esquema de la base de datos
Información general de ADO.NET
Rellenar un conjunto de datos desde un objeto
DataAdapter
21/03/2020 • 18 minutes to read • Edit Online

El DataSet ADO.NET es una representación residente en memoria de datos que proporciona un modelo de
programación relacional coherente independiente del origen de datos. DataSet representa un conjunto completo
de datos que incluye tablas, restricciones y relaciones entre las tablas. Dado que DataSet es independiente del
origen de datos, DataSet puede incluir datos locales de la aplicación y datos de otros muchos orígenes. La
interacción con los orígenes de datos existentes se controla mediante el DataAdapter .
La propiedad SelectCommand de DataAdapter es un objeto Command que recupera datos del origen de datos. Las
propiedades InsertCommand , UpdateCommand y DeleteCommand de DataAdapter son objetos Command que permiten
administrar las actualizaciones de los datos en el origen de datos para reflejar las modificaciones efectuadas en los
datos de DataSet . Estas propiedades se tratan con más detalle en Actualización de orígenes de datos con
DataAdapters.
El método Fill de DataAdapter se usa para rellenar un objeto DataSet con los resultados del elemento
SelectCommand de DataAdapter . Fill toma como argumentos un elemento DataSet que se debe rellenar y un
objeto DataTable o el nombre del objeto DataTable que se debe rellenar con las filas que devuelve
SelectCommand .

NOTE
El uso de DataAdapter para recuperar la totalidad de una tabla lleva tiempo, en especial si la tabla incluye un gran número
de filas. Esto se debe a que el acceso a la base de datos, la localización y el procesamiento de los datos, y la posterior
transferencia de los mismos al cliente son procesos largos. La extracción de la tabla completa al cliente también bloquea
todas las filas en el servidor. Para mejorar el rendimiento, puede usar la cláusula WHERE para reducir en gran medida el
número de filas que se devuelven al cliente. También puede reducir la cantidad de datos que se devuelven al cliente si
enumera de forma explícita las columnas necesarias en la instrucción SELECT . Otra solución consiste en recuperar las filas
por lotes (por ejemplo varios cientos de filas de una vez) y recuperar solo el siguiente lote cuando el cliente haya finalizado
con el lote actual.

El método Fill utiliza el objeto DataReader de forma implícita para devolver los nombres y tipos de columna
que se usan para crear las tablas de DataSet , y los datos para rellenar las filas de las tablas en DataSet . Las tablas
y columnas solo se crean cuando no existen; en caso contrario, Fill utiliza el esquema existente de DataSet . Los
tipos de columna se crean como tipos de .NET Framework según las tablas de Asignacionesde tipos de datos de
ADO.NET . Las claves principales no se crean a DataAdapter menos que existan en el origen de datos y .
MissingSchemaAction se establece MissingSchemaAction en . AddWithKey . Si el método Fill encuentra que una
tabla tiene una clave principal, sobrescribe los datos de DataSet con los del origen de datos en las filas donde los
valores de columna de clave principal coinciden con los de la fila que devuelve el origen de datos. Si no se detecta
ninguna clave principal, los datos se agregan a las tablas de DataSet . Fill utiliza las asignaciones que pueden
DataSet existir al rellenar el (consulte DataAdapter DataTable y DataColumn Mappings).

NOTE
Si SelectCommand devuelve los resultados de OUTER JOIN, DataAdapter no establece un valor PrimaryKey para el
objeto DataTable resultante. Debe definir PrimaryKey para asegurarse de que las filas duplicadas se resuelven
correctamente. Para obtener más información, consulte Definición de claves principales.
En el ejemplo de código siguiente se crea una instancia de SqlDataAdapter que utiliza un objeto SqlConnection a la
base de datos Northwind de Microsoft SQL Server y se rellena un objeto DataTable en un DataSet con la lista de
clientes. La instrucción SQL y los argumentos SqlConnection pasados al constructor SqlDataAdapter se utilizan
para crear la propiedad SelectCommand del SqlDataAdapter.

Ejemplo
' Assumes that connection is a valid SqlConnection object.
Dim queryString As String = _
"SELECT CustomerID, CompanyName FROM dbo.Customers"
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
queryString, connection)

Dim customers As DataSet = New DataSet


adapter.Fill(customers, "Customers")

// Assumes that connection is a valid SqlConnection object.


string queryString =
"SELECT CustomerID, CompanyName FROM dbo.Customers";
SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection);

DataSet customers = new DataSet();


adapter.Fill(customers, "Customers");

NOTE
El código que se muestra en este ejemplo no abre ni cierra explícitamente el objeto Connection . El método Fill abre de
forma implícita el objeto Connection que DataAdapter utiliza cuando encuentra que la conexión no está abierta todavía.
Si el método Fill ha abierto la conexión, también la cierra cuando el método Fill deja de utilizarla. Este hecho simplifica
el código cuando se trabaja con una operación única, como Fill o Update . Sin embargo, en el caso de que se estén
realizando varias operaciones que necesiten tener abierta una conexión, se puede mejorar el rendimiento de la aplicación
llamando explícitamente al método Open de Connection , realizando las operaciones en el origen de datos y, finalmente,
llamando al método Close de Connection . Es conveniente mantener abiertas las conexiones con el origen de datos el
menor tiempo posible para liberar recursos, de manera que estén disponibles para otras aplicaciones cliente.

Varios conjuntos de resultados


Si DataAdapter encuentra varios conjuntos de resultados, crea varias tablas en DataSet . Las tablas reciben de
forma predeterminada el nombre secuencial TableN, comenzando por "Table" que representa Table0. Si se pasa un
nombre de tabla como argumento al método Fill , las tablas reciben de forma predeterminada el nombre
secuencial TableNameN, comenzando por "TableName" que representa TableName0.

Llenar un DataSet desde múltiples DataAdapter


Cualquier número DataAdapter de objetos DataSet se puede utilizar con un archivo . Cada DataAdapter se puede
usar para rellenar uno o varios objetos DataTable y resolver de nuevo las actualizaciones en el origen de datos
correspondiente. Se pueden agregar objetos DataRelation y Constraint a DataSet localmente, lo que permite
relacionar datos procedentes de varios orígenes distintos. Por ejemplo, un DataSet puede contener datos de una
base de datos de Microsoft SQL Server, una base de datos de IBM DB2 expuesta mediante OLE DB y un origen de
datos que genera secuencias XML. La comunicación con cada origen de datos se puede controlar usando uno o
varios objetos DataAdapter .
Ejemplo
En el ejemplo de código siguiente se rellena una lista de clientes a partir de la base de datos Northwind
almacenada en Microsoft SQL Server, y una lista de pedidos a partir de la base de datos Northwind almacenada en
Microsoft Access 2000. Las tablas rellenas se relacionan entre sí mediante DataRelation , con lo que se puede
mostrar una lista de clientes con los pedidos que ha realizado cada uno. Para obtener DataRelation más
información acerca de los objetos, vea Agregar DataRelations y Navegar por DataRelations.

' Assumes that customerConnection is a valid SqlConnection object.


' Assumes that orderConnection is a valid OleDbConnection object.
Dim custAdapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT * FROM dbo.Customers", customerConnection)

Dim ordAdapter As OleDbDataAdapter = New OleDbDataAdapter( _


"SELECT * FROM Orders", orderConnection)

Dim customerOrders As DataSet = New DataSet()


custAdapter.Fill(customerOrders, "Customers")
ordAdapter.Fill(customerOrders, "Orders")

Dim relation As DataRelation = _


customerOrders.Relations.Add("CustOrders", _
customerOrders.Tables("Customers").Columns("CustomerID"), _
customerOrders.Tables("Orders").Columns("CustomerID"))

Dim pRow, cRow As DataRow


For Each pRow In customerOrders.Tables("Customers").Rows
Console.WriteLine(pRow("CustomerID").ToString())

For Each cRow In pRow.GetChildRows(relation)


Console.WriteLine(vbTab & cRow("OrderID").ToString())
Next
Next

// Assumes that customerConnection is a valid SqlConnection object.


// Assumes that orderConnection is a valid OleDbConnection object.
SqlDataAdapter custAdapter = new SqlDataAdapter(
"SELECT * FROM dbo.Customers", customerConnection);
OleDbDataAdapter ordAdapter = new OleDbDataAdapter(
"SELECT * FROM Orders", orderConnection);

DataSet customerOrders = new DataSet();

custAdapter.Fill(customerOrders, "Customers");
ordAdapter.Fill(customerOrders, "Orders");

DataRelation relation = customerOrders.Relations.Add("CustOrders",


customerOrders.Tables["Customers"].Columns["CustomerID"],
customerOrders.Tables["Orders"].Columns["CustomerID"]);

foreach (DataRow pRow in customerOrders.Tables["Customers"].Rows)


{
Console.WriteLine(pRow["CustomerID"]);
foreach (DataRow cRow in pRow.GetChildRows(relation))
Console.WriteLine("\t" + cRow["OrderID"]);
}

Tipo decimal de SQL Server


De forma DataSet predeterminada, los datos almacenan mediante tipos de datos de .NET Framework. En la
mayor parte de las aplicaciones, estos tipos proporcionan una representación adecuada de la información del
origen de datos. Sin embargo, esa representación puede ocasionar problemas cuando el tipo de datos del origen
de datos es decimal o numérico de SQL Server. El tipo decimal de datos de .NET Framework permite un máximo
decimal de 28 dígitos significativos, mientras que el tipo de datos de SQL Serversql Server permite 38 dígitos
significativos. Si SqlDataAdapter determina durante una operación Fill que la precisión de un campo decimal
de SQL Server es superior a 28 caracteres, la fila actual no se agrega a DataTable . En su lugar, se produce el
evento FillError que permite determinar si se va a producir o no una pérdida de precisión y tomar las medidas
adecuadas. Para obtener más FillError información sobre el evento, vea Controlar eventos DataAdapter. Para
obtener el valor decimal de SQL Server, también se puede utilizar un objeto SqlDataReader y llamar al método
GetSqlDecimal .
ADO.NET 2.0 introdujo System.Data.SqlTypes soporte DataSet mejorado para en el archivo . Para obtener más
información, consulta SqlTypes and the DataSet.

Capítulos de OLE DB
Se pueden usar conjuntos jerárquicos de filas, o capítulos (tipo DBTYPE_HCHAPTER de OLE DB y tipo adChapter de
ADO), para rellenar el contenido de DataSet . Cuando OleDbDataAdapter encuentra una columna que tiene un
capítulo durante una operación Fill , se crea un objeto DataTable para dicha columna y la tabla se rellena con
las columnas y filas del capítulo. Para asignar un nombre a la tabla creada para la columna con capítulo se usa
tanto el nombre de la tabla primaria como el de la columna con capítulo. El formato del nombre es
"nombreDeTablaPrimariaNombreDeColumnaConCapítulo". Si ya existe una tabla en DataSet que tenga el nombre
de la columna con capítulo, la tabla actual se rellena con los datos del capítulo. Si ninguna de las columnas de la
tabla existente coincide con una de las columnas del capítulo, se agrega una nueva columna a la tabla.
Antes de que las tablas de DataSet se rellenen con los datos de las columnas con capítulos, se crea una relación
entre las tablas primaria y secundaria del conjunto jerárquico de filas; para ello, se agrega una columna de tipo
entero a las tablas primaria y secundaria, se establece el valor de incremento automático para la columna de la
tabla primaria y se crea un objeto DataRelation usando las columnas agregadas de ambas tablas. Para asignar un
nombre a la relación se utilizan los nombres de la tabla primaria y de la columna con capítulo. El formato es
"nombreDeTablaPrimariaNombreDeColumnaConCapítulo".
Tenga en cuenta que la columna relacionada solo existe en DataSet . Las operaciones de relleno que se realicen
posteriormente desde el origen de datos pueden provocar que se agreguen nuevas filas a las tablas en lugar de
que se introduzcan los cambios en las filas existentes.
Tenga en cuenta además que, si se utiliza una sobrecarga de DataAdapter.Fill que acepte un objeto DataTable ,
solo se rellanará esa tabla. En este caso también se agrega a la tabla una columna de tipo entero y con incremento
automático, aunque no se crea ni rellena ninguna tabla secundaria, ni se crea ninguna relación.
En el ejemplo siguiente se utiliza el proveedor MSDataShape para generar un capítulo con la columna de pedidos
realizados por cada uno de los clientes de una lista. A continuación, se rellena un DataSet con los datos.

Using connection As OleDbConnection = New OleDbConnection( _


"Provider=MSDataShape;Data Provider=SQLOLEDB;" & _
"Data Source=(local);Integrated " & _
"Security=SSPI;Initial Catalog=northwind")

Dim adapter As OleDbDataAdapter = New OleDbDataAdapter( _


"SHAPE {SELECT CustomerID, CompanyName FROM Customers} " & _
"APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders " & _
"RELATE CustomerID TO CustomerID)", connection)

Dim customers As DataSet = New DataSet()

adapter.Fill(customers, "Customers")
End Using
using (OleDbConnection connection = new OleDbConnection("Provider=MSDataShape;Data Provider=SQLOLEDB;" +
"Data Source=(local);Integrated Security=SSPI;Initial Catalog=northwind"))
{
OleDbDataAdapter adapter = new OleDbDataAdapter("SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +
"APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders " +
"RELATE CustomerID TO CustomerID)", connection);

DataSet customers = new DataSet();


adapter.Fill(customers, "Customers");
}

Una vez completada la operación Fill , DataSet contiene dos tablas: Customers y CustomersOrders , donde
CustomersOrders representa la columna con capítulo. Se agrega una columna adicional denominada Orders a la
tabla Customers , y una columna adicional denominada CustomersOrders a la tabla CustomersOrders . Se establece
el valor de incremento automático para la columna Orders de la tabla Customers . Se crea también una relación
DataRelation , CustomersOrders , utilizando las columnas que se han agregado a las tablas, siendo Customers la
tabla primaria. Las siguientes tablas muestran algunos ejemplos de los resultados.
Nombre de tabla: Customers
C USTO M ERID C O M PA N Y N A M E O RDERS

ALFKI Alfreds Futterkiste 0

ANATR Ana Trujillo Emparedados y helados 1

Nombre de tabla: CustomersOrders


C USTO M ERID O RDERID C USTO M ERSO RDERS

ALFKI 10643 0

ALFKI 10692 0

ANATR 10308 1

ANATR 10625 1

Consulte también
Objetos DataAdapter y DataReader
Asignaciones de tipos de datos en ADO.NET
Modificación de datos con un objeto DbDataAdapter
Conjuntos de resultados activos múltiples (MARS)
Información general de ADO.NET
Parámetros de DataAdapter
21/03/2020 • 12 minutes to read • Edit Online

DbDataAdapter tiene cuatro propiedades que se utilizan para recuperar y actualizar datos en el origen de datos: la
propiedad SelectCommand devuelve datos del origen de datos y las propiedades InsertCommand,
UpdateCommand y DeleteCommand se utilizan para administrar los cambios en el origen de datos. La propiedad
SelectCommand debe establecerse antes de llamar al método Fill de DataAdapter . Las propiedades
InsertCommand , UpdateCommand o DeleteCommand se deben establecer antes llamar al método Update de
DataAdapter , en función de las modificaciones realizadas en los datos en DataTable. Por ejemplo, si se han
agregado filas, se debe establecer InsertCommand antes de llamar a Update . Cuando Update procesa una fila
insertada, actualizada o eliminada, DataAdapter utiliza la propiedad Command que corresponde a la acción en
cuestión. La información actual relacionada con la fila modificada se pasa al objeto Command a través de la
colección Parameters .
Cuando se actualiza una fila en el origen de datos, se llama a la instrucción UPDATE, que usa un identificador único
para identificar la fila de la tabla que se va a actualizar. El identificador único suele ser el valor del campo de clave
principal. La instrucción UPDATE utiliza parámetros que contienen el identificador único y las columnas y valores
que se van a actualizar, como muestra la siguiente instrucción Transact-SQL.

UPDATE Customers SET CompanyName = @CompanyName


WHERE CustomerID = @CustomerID

NOTE
La sintaxis de los marcadores de posición de parámetros depende del origen de datos. En este ejemplo se muestran
marcadores de posición para un origen de datos de SQL Server. Utilice signos de interrogación de cierre (?) como
marcadores de posición de para los parámetros System.Data.OleDb y System.Data.Odbc.

En este ejemplo de CompanyName Visual Basic, el @CompanyName campo se actualiza CustomerID con el valor
@CustomerID del parámetro para la fila donde es igual al valor del parámetro. Los parámetros recuperan
información SourceColumn de la SqlParameter fila modificada mediante la propiedad del objeto. A continuación
se muestran los parámetros del ejemplo anterior de la instrucción UPDATE. En el código se parte de que el
adapter de la variable representa a un objeto SqlDataAdapter válido.

adapter.Parameters.Add( _
"@CompanyName", SqlDbType.NChar, 15, "CompanyName")
Dim parameter As SqlParameter = _
adapter.UpdateCommand.Parameters.Add("@CustomerID", _
SqlDbType.NChar, 5, "CustomerID")
parameter.SourceVersion = DataRowVersion.Original

El método Add de la colección Parameters toma el nombre del parámetro, el tipo de datos, el tamaño (si
corresponde al tipo) y el nombre de la propiedad SourceColumn de DataTable . Tenga en cuenta que
SourceVersion del parámetro @CustomerID se establece en Original . De esta forma se garantiza que la fila
existente en el origen de datos se actualice cuando el valor de la columna o columnas identificadas haya cambiado
en la fila DataRow modificada. En ese caso, el valor de la fila Original coincidiría con el valor actual en el origen
de datos y el valor de la fila Current contendría el valor actualizado. No se asigna ningún valor a SourceVersion
para el parámetro @CompanyName , por lo que se utiliza el valor predeterminado, el de la fila Current .
NOTE
Para las Fill operaciones DataAdapter del Get DataReader , el tipo de .NET Framework se deduce del tipo devuelto
por el proveedor de datos de .NET Framework. Los tipos de .NET Framework deducidos y los métodos de descriptor de
acceso para los tipos de datos de Microsoft SQL Server, OLE DB y ODBC se describen en Asignacionesde tipos de datos en
ADO.NET .

Parameter.SourceColumn, Parameter.SourceVersion
SourceColumn y SourceVersion se pueden pasar como argumentos al constructor Parameter , o también se
pueden establecer como propiedades de un Parameter existente. SourceColumn es el nombre de DataColumn de
DataRow en la que se recupera el valor de Parameter . SourceVersion especifica la versión de DataRow que utiliza
DataAdapter para recuperar el valor.

En la tabla siguiente se muestran los valores de la enumeración DataRowVersion disponibles para su uso con
SourceVersion .

EN UM ERA C IÓ N DATA RO W VERSIO N DESC RIP C IÓ N

Current El parámetro utiliza el valor actual de la columna. Este es el


valor predeterminado.

Default El parámetro utiliza el DefaultValue de la columna.

Original El parámetro utiliza el valor original de la columna.

Proposed El parámetro utiliza un valor propuesto.

En el ejemplo de código de SqlClient de la siguiente sección se define un parámetro para UpdateCommand


donde la columna CustomerID se utiliza como SourceColumn para dos parámetros: @CustomerID (
SET CustomerID = @CustomerID ) y @OldCustomerID ( WHERE CustomerID = @OldCustomerID ). El @CustomerID parámetro
se utiliza para actualizar la columna DataRow CustomerID al valor actual en el archivo . Como resultado,
CustomerID SourceColumn se SourceVersion utiliza Current el con un. El @OldCustomerID parámetro se utiliza
para identificar la fila actual en el origen de datos. Dado que el valor de la columna coincidente se encuentra en la
versión Original de la fila, también se usa el mismo objeto SourceColumn ( CustomerID ) con SourceVersion de
Original .

Trabajar con parámetros SqlClient


En el ejemplo siguiente se muestra cómo crear SqlDataAdapter y establecer MissingSchemaAction en AddWithKey
para recuperar información de esquema adicional de la base de datos. Las propiedades SelectCommand,
InsertCommand, UpdateCommand y DeleteCommand establecen sus correspondientes objetos SqlParameter
agregados a la colección Parameters. El método devuelve un objeto SqlDataAdapter .
public static SqlDataAdapter CreateSqlDataAdapter(SqlConnection connection)
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

// Create the commands.


adapter.SelectCommand = new SqlCommand(
"SELECT CustomerID, CompanyName FROM CUSTOMERS", connection);
adapter.InsertCommand = new SqlCommand(
"INSERT INTO Customers (CustomerID, CompanyName) " +
"VALUES (@CustomerID, @CompanyName)", connection);
adapter.UpdateCommand = new SqlCommand(
"UPDATE Customers SET CustomerID = @CustomerID, CompanyName = @CompanyName " +
"WHERE CustomerID = @oldCustomerID", connection);
adapter.DeleteCommand = new SqlCommand(
"DELETE FROM Customers WHERE CustomerID = @CustomerID", connection);

// Create the parameters.


adapter.InsertCommand.Parameters.Add("@CustomerID",
SqlDbType.Char, 5, "CustomerID");
adapter.InsertCommand.Parameters.Add("@CompanyName",
SqlDbType.VarChar, 40, "CompanyName");

adapter.UpdateCommand.Parameters.Add("@CustomerID",
SqlDbType.Char, 5, "CustomerID");
adapter.UpdateCommand.Parameters.Add("@CompanyName",
SqlDbType.VarChar, 40, "CompanyName");
adapter.UpdateCommand.Parameters.Add("@oldCustomerID",
SqlDbType.Char, 5, "CustomerID").SourceVersion =
DataRowVersion.Original;

adapter.DeleteCommand.Parameters.Add("@CustomerID",
SqlDbType.Char, 5, "CustomerID").SourceVersion =
DataRowVersion.Original;

return adapter;
}
Public Function CreateSqlDataAdapter( _
ByVal connection As SqlConnection) As SqlDataAdapter

Dim adapter As New SqlDataAdapter()


adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey

' Create the commands.


adapter.SelectCommand = New SqlCommand( _
"SELECT CustomerID, CompanyName FROM CUSTOMERS", connection)
adapter.InsertCommand = New SqlCommand( _
"INSERT INTO Customers (CustomerID, CompanyName) " & _
"VALUES (@CustomerID, @CompanyName)", connection)
adapter.UpdateCommand = New SqlCommand( _
"UPDATE Customers SET CustomerID = @CustomerID, CompanyName = " & _
"@CompanyName WHERE CustomerID = @oldCustomerID", connection)
adapter.DeleteCommand = New SqlCommand( _
"DELETE FROM Customers WHERE CustomerID = @CustomerID", connection)

' Create the parameters.


adapter.InsertCommand.Parameters.Add("@CustomerID", _
SqlDbType.Char, 5, "CustomerID")
adapter.InsertCommand.Parameters.Add("@CompanyName", _
SqlDbType.VarChar, 40, "CompanyName")

adapter.UpdateCommand.Parameters.Add("@CustomerID", _
SqlDbType.Char, 5, "CustomerID")
adapter.UpdateCommand.Parameters.Add("@CompanyName", _
SqlDbType.VarChar, 40, "CompanyName")
adapter.UpdateCommand.Parameters.Add("@oldCustomerID", _
SqlDbType.Char, 5, "CustomerID").SourceVersion = _
DataRowVersion.Original

adapter.DeleteCommand.Parameters.Add("@CustomerID", _
SqlDbType.Char, 5, "CustomerID").SourceVersion = _
DataRowVersion.Original

Return adapter
End Function

Marcadores de posición de parámetros OleDb


En el caso de los objetos OleDbDataAdapter y OdbcDataAdapter, debe utilizar signos de interrogación de cierre (?)
como marcadores de posición para identificar los parámetros.

Dim selectSQL As String = _


"SELECT CustomerID, CompanyName FROM Customers " & _
"WHERE CountryRegion = ? AND City = ?"
Dim insertSQL AS String = _
"INSERT INTO Customers (CustomerID, CompanyName) VALUES (?, ?)"
Dim updateSQL AS String = _
"UPDATE Customers SET CustomerID = ?, CompanyName = ? " & _
WHERE CustomerID = ?"
Dim deleteSQL As String = "DELETE FROM Customers WHERE CustomerID = ?"
string selectSQL =
"SELECT CustomerID, CompanyName FROM Customers " +
"WHERE CountryRegion = ? AND City = ?";
string insertSQL =
"INSERT INTO Customers (CustomerID, CompanyName) " +
"VALUES (?, ?)";
string updateSQL =
"UPDATE Customers SET CustomerID = ?, CompanyName = ? " +
"WHERE CustomerID = ? ";
string deleteSQL = "DELETE FROM Customers WHERE CustomerID = ?";

Las instrucciones de consulta con parámetros definen qué parámetros de entrada y de salida se deben crear. Para
crear un parámetro, se utiliza el método Parameters.Add o el constructor Parameter con el fin de especificar el
nombre de columna, tipo de datos y tamaño. En el caso de tipos de datos intrínsecos, como Integer , no es
necesario incluir el tamaño, aunque se puede especificar el tamaño predeterminado.
En el ejemplo de código siguiente se crean los parámetros para una instrucción SQL y, a continuación, se llena un
DataSet .

Ejemplo de OleDb
' Assumes that connection is a valid OleDbConnection object.
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter

Dim selectCMD AS OleDbCommand = New OleDbCommand(selectSQL, connection)


adapter.SelectCommand = selectCMD

' Add parameters and set values.


selectCMD.Parameters.Add( _
"@CountryRegion", OleDbType.VarChar, 15).Value = "UK"
selectCMD.Parameters.Add( _
"@City", OleDbType.VarChar, 15).Value = "London"

Dim customers As DataSet = New DataSet


adapter.Fill(customers, "Customers")

// Assumes that connection is a valid OleDbConnection object.


OleDbDataAdapter adapter = new OleDbDataAdapter();

OleDbCommand selectCMD = new OleDbCommand(selectSQL, connection);


adapter.SelectCommand = selectCMD;

// Add parameters and set values.


selectCMD.Parameters.Add(
"@CountryRegion", OleDbType.VarChar, 15).Value = "UK";
selectCMD.Parameters.Add(
"@City", OleDbType.VarChar, 15).Value = "London";

DataSet customers = new DataSet();


adapter.Fill(customers, "Customers");

Parámetros Odbc
' Assumes that connection is a valid OdbcConnection object.
Dim adapter As OdbcDataAdapter = New OdbcDataAdapter

Dim selectCMD AS OdbcCommand = New OdbcCommand(selectSQL, connection)


adapter.SelectCommand = selectCMD

' Add Parameters and set values.


selectCMD.Parameters.Add("@CountryRegion", OdbcType.VarChar, 15).Value = "UK"
selectCMD.Parameters.Add("@City", OdbcType.VarChar, 15).Value = "London"

Dim customers As DataSet = New DataSet


adapter.Fill(customers, "Customers")

// Assumes that connection is a valid OdbcConnection object.


OdbcDataAdapter adapter = new OdbcDataAdapter();

OdbcCommand selectCMD = new OdbcCommand(selectSQL, connection);


adapter.SelectCommand = selectCMD;

//Add Parameters and set values.


selectCMD.Parameters.Add("@CountryRegion", OdbcType.VarChar, 15).Value = "UK";
selectCMD.Parameters.Add("@City", OdbcType.VarChar, 15).Value = "London";

DataSet customers = new DataSet();


adapter.Fill(customers, "Customers");

NOTE
Si no se proporciona un nombre de parámetro para un parámetro, el parámetro recibe un nombre predeterminado
incremental de ParámetroN , empezando por "Parameter1". Se recomienda evitar la convención de nomenclatura
ParámetroN al proporcionar un nombre de parámetro, ya que el nombre que proporcione podría entrar en conflicto con un
nombre de parámetro predeterminado existente en el ParameterCollection archivo . Si el nombre proporcionado ya
existe, se inicia una excepción.

Consulte también
Objetos DataAdapter y DataReader
Comandos y parámetros
Actualizar orígenes de datos con objetos DataAdapter
Modificación de datos con procedimientos almacenados
Asignaciones de tipos de datos en ADO.NET
Información general de ADO.NET
Agregar restricciones existentes a un conjunto de
datos
23/10/2019 • 5 minutes to read • Edit Online

El método Fill de DataAdapter rellena una DataSet solo con las columnas y filas de la tabla de un origen de datos;
Aunque el origen de datos suele establecer restricciones, el método Fill no agrega esta información de esquema al
conjunto de datos de forma predeterminada. Para rellenar un conjunto de datos con información de restricciones
PRIMARY KEY de un origen de datos, puede llamar al método FillSchema de DataAdapter o establecer la
propiedad MissingSchemaAction de DataAdapter . para que el método sea AddWithKey antes de llamar a
Fill . Esto garantizará que las restricciones PRIMARY KEY del conjunto de datos reflejen las del origen de datos. La
información de restricción de clave externa no se incluye y se debe crear explícitamente, como se muestra en
restricciones de DataTable.
Agregar información de esquema a un conjunto de datos antes de rellenarlo con datos garantiza que las
restricciones PRIMARY KEY se incluyen con los objetos DataTable del conjunto de datos. Como resultado, cuando
se realizan llamadas adicionales para rellenar el conjunto de datos, la información de la columna de clave
principal se utiliza para hacer coincidir las nuevas filas del origen de datos con las filas actuales de cada DataTable ,
y los datos actuales de las tablas se sobrescriben con los datos del origen de datos. Sin la información del esquema,
las nuevas filas del origen de datos se anexan al conjunto de datos, lo que da lugar a filas duplicadas.

NOTE
Si una columna de un origen de datos se identifica como de incremento automático, el método FillSchema o el método Fill
con el MissingSchemaAction de AddWithKey , crea un DataColumn con una propiedad AutoIncrement . establezca en
true . Sin embargo, tendrá que establecer los valores de AutoIncrementStep y AutoIncrementSeed . Para obtener más
información acerca de las columnas de incremento automático, vea crear columnas de incrementoautomático.

El uso de FillSchema o el establecimiento de MissingSchemaAction en AddWithKey requiere un


procesamiento adicional en el origen de datos para determinar la información de la columna de clave principal.
Este proceso adicional puede reducir el rendimiento. Si conoce en la fase de diseño la información de la clave
principal, es aconsejable especificar de modo explícito la columna o columnas que la forman para mejorar el
rendimiento. Para obtener información sobre cómo establecer explícitamente la información de clave principal de
una tabla, vea definir claves principales.
En el ejemplo de código siguiente se muestra cómo agregar información de esquema a un conjunto de datos
mediante FillSchema :

Dim custDataSet As New DataSet()

custAdapter.FillSchema(custDataSet, SchemaType.Source, "Customers")


custAdapter.Fill(custDataSet, "Customers")

var custDataSet = new DataSet();

custAdapter.FillSchema(custDataSet, SchemaType.Source, "Customers");


custAdapter.Fill(custDataSet, "Customers");

En el ejemplo de código siguiente se muestra cómo agregar información de esquema a un conjunto de datos
mediante la propiedad MissingSchemaAction. AddWithKey del método Fill :
Dim custDataSet As New DataSet()

custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
custAdapter.Fill(custDataSet, "Customers")

var custDataSet = new DataSet();

custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
custAdapter.Fill(custDataSet, "Customers");

Controlar varios conjuntos de resultados


Si el DataAdapter encuentra varios conjuntos de resultados devueltos por SelectCommand , creará varias tablas
en el conjunto de DataSet . A las tablas se les asignará un nombre predeterminado incremental basado en cero
de la tabla N, empezando por la tabla en lugar de "Table0". Si se pasa un nombre de tabla como argumento al
método FillSchema , se asignará a las tablas un nombre incremental basado en cero de TableName N,
comenzando por TableName en lugar de "representa tablename0".

NOTE
Si se llama al método FillSchema del objeto OleDbDataAdapter para un comando que devuelve varios conjuntos de
resultados, solo se devuelve la información del esquema del primer conjunto de resultados. Al devolver información de
esquema para varios conjuntos de resultados mediante OleDbDataAdapter , se recomienda especificar el
MissingSchemaAction de AddWithKey y obtener la información del esquema al llamar al método Fill . forma.

Vea también
Objetos DataAdapter y DataReader
Objetos DataSet, DataTable y DataView
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
Correspondencias de DataTable y DataColumn en un
objeto DataAdapter
21/03/2020 • 6 minutes to read • Edit Online

Un DataAdapter contiene una colección de cero o más DataTableMapping objetos en su TableMappings


propiedad. Un DataTableMapping proporciona una asignación maestra entre los datos devueltos de una
consulta en un origen de datos y un DataTablearchivo . El DataTableMapping nombre se puede pasar en lugar
del nombre DataTable a la Fill método de la DataAdapter . En el ejemplo siguiente se crea un
DataTableMapping denominado AuthorsMapping para el Authors tabla.

workAdapter.TableMappings.Add("AuthorsMapping", "Authors")

workAdapter.TableMappings.Add("AuthorsMapping", "Authors");

Un DataTableMapping le permite usar nombres de columna en un DataTable que son diferentes de los de la
base de datos. El DataAdapter usa la asignación para que coincida con las columnas cuando se actualiza la tabla.
Si no especifica un TableName o un DataTableMapping nombre al llamar a la Fill o Update método de la
DataAdapter , el DataAdapter busca un DataTableMapping denominado "Table". Si ese DataTableMapping
no existe, el TableName de la DataTable es "Table". Puede especificar un valor predeterminado
DataTableMapping mediante la creación de un DataTableMapping con el nombre de "Table".
En el ejemplo de código siguiente System.Data.Common se crea un DataTableMapping (desde el espacio de
nombres) y lo convierte en la asignación predeterminada para el DataAdapter especificado nombrándolo "Table".
A continuación, el ejemplo asigna las columnas de la primera tabla del resultado de la consulta (la tabla
Customers de DataSetla base de datos Nor thwind) a un conjunto de nombres más fáciles de usar en la tabla
Nor thwind Customers del archivo . En las columnas que no se asignan se usa el nombre de la columna en el
origen de datos.

Dim mapping As DataTableMapping = _


adapter.TableMappings.Add("Table", "NorthwindCustomers")
mapping.ColumnMappings.Add("CompanyName", "Company")
mapping.ColumnMappings.Add("ContactName", "Contact")
mapping.ColumnMappings.Add("PostalCode", "ZIPCode")

adapter.Fill(custDS)

DataTableMapping mapping =
adapter.TableMappings.Add("Table", "NorthwindCustomers");
mapping.ColumnMappings.Add("CompanyName", "Company");
mapping.ColumnMappings.Add("ContactName", "Contact");
mapping.ColumnMappings.Add("PostalCode", "ZIPCode");

adapter.Fill(custDS);

En situaciones más avanzadas, puede decidir que desea que el mismo DataAdapter admita la carga de tablas
diferentes con asignaciones diferentes. Para ello, simplemente agregue objetos DataTableMapping adicionales.
Cuando el Fill método se pasa una instancia de un DataSet y un DataTableMapping nombre, si existe una
asignación con ese nombre se utiliza; de lo contrario, se utiliza un DataTable con ese nombre.
En los ejemplos siguientes se crea un DataTableMapping con un nombre de Customers y un dataTable
nombre de BizTalkSchema . A continuación, el ejemplo asigna las filas devueltas por la instrucción SELECT a
BizTalkSchema DataTable .

Dim mapping As ITableMapping = _


adapter.TableMappings.Add("Customers", "BizTalkSchema")
mapping.ColumnMappings.Add("CustomerID", "ClientID")
mapping.ColumnMappings.Add("CompanyName", "ClientName")
mapping.ColumnMappings.Add("ContactName", "Contact")
mapping.ColumnMappings.Add("PostalCode", "ZIP")

adapter.Fill(custDS, "Customers")

ITableMapping mapping =
adapter.TableMappings.Add("Customers", "BizTalkSchema");
mapping.ColumnMappings.Add("CustomerID", "ClientID");
mapping.ColumnMappings.Add("CompanyName", "ClientName");
mapping.ColumnMappings.Add("ContactName", "Contact");
mapping.ColumnMappings.Add("PostalCode", "ZIP");

adapter.Fill(custDS, "Customers");

NOTE
Si no se proporciona un nombre de columna de origen en una asignación de columnas o no se identifica un nombre de tabla
de origen en una asignación de tablas, se generan nombres predeterminados de forma automática. Si no se proporciona
ninguna columna de origen para una asignación de columnas, la asignación de columnas recibe un nombre predeterminado
incremental de SourceColumn N, empezando por SourceColumn1 . Si no se proporciona ningún nombre de tabla de
origen para una asignación de tabla, la asignación de tabla recibe un nombre predeterminado incremental de SourceTable
N, empezando por SourceTable1 .

NOTE
Se recomienda evitar la convención de nomenclatura de SourceColumn N para una asignación de columnas o
SourceTable N para una asignación de tabla, ya que el nombre que proporcione puede entrar en conflicto con un nombre
de asignación de columna predeterminado existente en ColumnMappingCollection o nombre de asignación de tabla en
DataTableMappingCollection . Si el nombre proporcionado ya existe, se iniciará una excepción.

Controlar varios conjuntos de resultados


Si SelectCommand devuelve varias tablas, Fill genera automáticamente nombres de tabla con valores
incrementales para las tablas del conjunto de datos , empezando por el nombre de tabla especificado y
continuando con el formulario TableName N, empezando por TableName1 . Puede utilizar asignaciones de
tablas para asignar el nombre de tabla generado automáticamente a un nombre que desee especificar para la
tabla en el conjunto de datos . Por ejemplo, para un SelectCommand que devuelve dos tablas, Customers y
Orders , emita la siguiente llamada a Fill .

adapter.Fill(customersDataSet, "Customers")

adapter.Fill(customersDataSet, "Customers");
Se crean dos tablas en el conjunto de datos : Clientes y clientes1 . Puede utilizar asignaciones de tablas para
asegurarse de que la segunda tabla se denomina Pedidos en lugar de Customers1 . Para ello, asigne la tabla de
origen de Customers1 a la tabla DataSet Orders , como se muestra en el ejemplo siguiente.

adapter.TableMappings.Add("Customers1", "Orders")
adapter.Fill(customersDataSet, "Customers")

adapter.TableMappings.Add("Customers1", "Orders");
adapter.Fill(customersDataSet, "Customers");

Consulte también
Objetos DataAdapter y DataReader
Recuperar y modificar datos en ADO.NET
Información general de ADO.NET
Paginar un resultado de consulta
21/03/2020 • 6 minutes to read • Edit Online

La paginación a través del resultado de una consulta es un proceso que consiste en devolver los resultados de una
consulta en subconjuntos menores de datos, o páginas. Se trata de una práctica frecuente para presentar los
resultados a un usuario en fragmentos pequeños y fáciles de administrar.
El DataAdapter proporciona un recurso para devolver solo una página de datos, a través de sobrecargas de la Fill
método. Sin embargo, esta podría no ser la mejor opción para paginar a DataTable DataSet través de resultados de
consultas grandes porque, aunque el DataAdapter rellena el destino o solo con los registros solicitados, los
recursos para devolver toda la consulta se siguen utilizando. Para devolver una página de datos a partir de un
origen de datos sin utilizar los recursos necesarios para devolver toda la consulta, hay que especificar otros
criterios adicionales para la consulta que reduzcan las filas devueltas a las filas únicamente necesarias.
Para usar el método Fill para devolver una página de datos, especifique un parámetro star tRecord para el primer
registro de la página de datos y un parámetro maxRecords para el número de registros de la página de datos.
En el ejemplo de código siguiente se muestra cómo utilizar el Fill método para devolver la primera página de un
resultado de consulta donde el tamaño de página es de cinco registros.

Dim currentIndex As Integer = 0


Dim pageSize As Integer = 5

Dim orderSQL As String = "SELECT * FROM dbo.Orders ORDER BY OrderID"


' Assumes that connection is a valid SqlConnection object.
Dim adapter As SqlDataAdapter = _
New SqlDataAdapter(orderSQL, connection)

Dim dataSet As DataSet = New DataSet()


adapter.Fill(dataSet, currentIndex, pageSize, "Orders")

int currentIndex = 0;
int pageSize = 5;

string orderSQL = "SELECT * FROM Orders ORDER BY OrderID";


// Assumes that connection is a valid SqlConnection object.
SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);

DataSet dataSet = new DataSet();


adapter.Fill(dataSet, currentIndex, pageSize, "Orders");

En el ejemplo anterior, el DataSet solo se rellena con cinco registros, pero se devuelve toda la tabla Orders. Para
rellenar el DataSet con esos mismos cinco registros, pero solo devolver cinco registros, use las cláusulas TOP y
WHERE en la instrucción SQL, como en el ejemplo de código siguiente.
Dim pageSize As Integer = 5

Dim orderSQL As String = "SELECT TOP " & pageSize & _


" * FROM Orders ORDER BY OrderID"
Dim adapter As SqlDataAdapter = _
New SqlDataAdapter(orderSQL, connection)

Dim dataSet As DataSet = New DataSet()


adapter.Fill(dataSet, "Orders")

int pageSize = 5;

string orderSQL = "SELECT TOP " + pageSize +


" * FROM Orders ORDER BY OrderID";
SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);

DataSet dataSet = new DataSet();


adapter.Fill(dataSet, "Orders");

Hay que tener en cuenta que, al paginar a través del resultado de una consulta de esta forma, hay que conservar el
identificador único por el que están ordenadas las filas con el fin de pasar el identificador único al comando con el
fin de devolver la siguiente página de registros, tal y como se muestra en el ejemplo de código siguiente.

Dim lastRecord As String = _


dataSet.Tables("Orders").Rows(pageSize - 1)("OrderID").ToString()

string lastRecord =
dataSet.Tables["Orders"].Rows[pageSize - 1]["OrderID"].ToString();

Para devolver la página siguiente de registros mediante la sobrecarga del método Fill que toma los parámetros
star tRecord y maxRecords, incremente el índice de registros actual por el tamaño de página y rellene la tabla.
Recuerde que el servidor de base de datos devuelve todos los resultados de la consulta aunque solo se agregue
una página de registros al conjunto de datos . En el siguiente ejemplo de código se vacía el contenido de las filas de
la tabla antes de rellenarse con la siguiente página de datos. Quizás se desee conservar un cierto número de filas
devueltas en una caché local para reducir los viajes al servidor de bases de datos.

currentIndex = currentIndex + pageSize

dataSet.Tables("Orders").Rows.Clear()

adapter.Fill(dataSet, currentIndex, pageSize, "Orders")

currentIndex += pageSize;

dataSet.Tables["Orders"].Rows.Clear();

adapter.Fill(dataSet, currentIndex, pageSize, "Orders");

Para devolver la siguiente página de registros sin que el servidor de bases de datos tenga que devolver toda la
consulta, hay que especificar criterios restrictivos en la instrucción SELECT. Como el ejemplo anterior conservaba el
último registro devuelto, es posible utilizarlo en la cláusula WHERE con el fin de especificar un punto de partida
para la consulta, como se muestra en el ejemplo de código siguiente.
orderSQL = "SELECT TOP " & pageSize & _
" * FROM Orders WHERE OrderID > " & lastRecord & " ORDER BY OrderID"
adapter.SelectCommand.CommandText = orderSQL

dataSet.Tables("Orders").Rows.Clear()

adapter.Fill(dataSet, "Orders")

orderSQL = "SELECT TOP " + pageSize +


" * FROM Orders WHERE OrderID > " + lastRecord + " ORDER BY OrderID";
adapter.SelectCommand.CommandText = orderSQL;

dataSet.Tables["Orders"].Rows.Clear();

adapter.Fill(dataSet, "Orders");

Consulte también
Objetos DataAdapter y DataReader
Información general de ADO.NET
Actualizar orígenes de datos con objetos
DataAdapter
23/10/2019 • 22 minutes to read • Edit Online

El método Update de DataAdapter se llama para reflejar en el origen de datos todos los cambios efectuados en
DataSet. El método Update , al igual que el método Fill , acepta como argumentos una instancia de DataSet y,
de forma opcional, un objeto DataTable o un nombre de DataTable . La instancia de DataSet es el DataSet que
contiene los cambios efectuados, y DataTable identifica la tabla desde la que se pueden recuperar esos cambios.
Si no se especifica DataTable , se utiliza el primer DataTable de DataSet .
Al llamar al método Update , DataAdapter analiza los cambios efectuados y ejecuta el comando apropiado
(INSERT, UPDATE o DELETE). Cuando DataAdapter encuentra un cambio en DataRow, utiliza los comandos
InsertCommand, UpdateCommand o DeleteCommand para reflejarlo. De esta forma, se obtiene el máximo
rendimiento de la aplicación de ADO.NET al especificar la sintaxis del comando en la fase de diseño y utilizar,
siempre que es posible, procedimientos almacenados. Antes de llamar a Update deben establecerse de forma
explícita los comandos. Si se llama a Update y el comando correspondiente a una actualización determinada no
existe (por ejemplo, no hay un comando DeleteCommand para las filas eliminadas), se inicia una excepción.

NOTE
Si está utilizando procedimientos almacenados de SQL Server para editar o eliminar datos con DataAdapter , asegúrese
de que no utiliza SET NOCOUNT ON en la definición del procedimiento almacenado. Esto hace que el recuento de filas
afectadas vuelva a cero, lo que DataAdapter interpreta como un conflicto de simultaneidad. En este caso, se iniciará una
DBConcurrencyException.

Se pueden usar los parámetros de comando para especificar los valores de entrada y salida de una instrucción
SQL o un procedimiento almacenado para cada fila modificada en DataSet . Para obtener más información, vea
DataAdapter Parameters.

NOTE
Es importante comprender la diferencia entre eliminar una fila de una DataTable y quitar la fila. Al llamar al método
Remove o RemoveAt , la fila se quita inmediatamente. Cualquier fila correspondiente en el origen de datos back end no se
verá afectada si a continuación se pasa DataTable o DataSet a DataAdapter y se llama a Update . Al utilizar el
método Delete , la fila permanece en DataTable y se marca para eliminación. Si a continuación se pasa DataTable o
DataSet a DataAdapter y se llama a Update , la fila correspondiente en el origen de datos back end se elimina.

Si DataTable está asignada a una única base de datos o se ha generado a partir de ella, puede utilizar el objeto
DbCommandBuilder para generar automáticamente los objetos DeleteCommand , InsertCommand y UpdateCommand
de DataAdapter . Para obtener más información, vea generar comandos con objetos CommandBuilder.

Utilizar UpdatedRowSource para asignar valores a DataSet


Puede controlar la forma en que los valores devueltos desde el origen de datos se asignan a DataTable después
de una llamada al método Update de DataAdapter , utilizando la propiedad UpdatedRowSource de un objeto
DbCommand. Al asignar la propiedad UpdatedRowSource a uno de los valores de enumeración UpdateRowSource,
puede determinar si los parámetros que devuelven los comandos DataAdapter se deben omitir o se deben
aplicar a la fila cambiada en DataSet . También puede especificar si la primera fila devuelta (si existe) se aplica a
la fila modificada en DataTable .
En la tabla siguiente se describen los distintos valores de la enumeración UpdateRowSource y la forma en que
afectan al comportamiento del comando utilizado con DataAdapter .

EN UM ERA C IÓ N UP DAT EDRO W SO URC E DESC RIP C IÓ N

Both Tanto los parámetros de salida como la primera fila del


conjunto de resultados devuelto se pueden asignar a la fila
modificada en DataSet .

FirstReturnedRecord Solo los datos de la primera fila del conjunto de resultados


devuelto se pueden asignar a la fila modificada en el
DataSet .

None Se pasan por alto todos los parámetros de salida y las filas
del conjunto de resultados devuelto.

OutputParameters Solo los parámetros de salida se pueden asignar a la fila


modificada en DataSet .

El método Update vuelve a resolver los cambios en el origen de datos; sin embargo, puede que otros clientes
hayan modificado datos en el origen de datos desde la última vez que se llenó DataSet . Para actualizar DataSet
con datos actuales, utilice el DataAdapter y el método Fill . De esta forma se agregan las filas nuevas a la tabla
y se actualiza la información en las filas ya existentes. El método Fill determina si se va a agregar una nueva
fila o si se va a actualizar una fila existente mediante el examen de los valores de clave principal de las filas de
DataSet y las filas devueltas por SelectCommand . Si el método Fill encuentra un valor de clave principal de una
fila de DataSet que coincide con un valor de clave principal de una fila de los resultados devueltos por
SelectCommand , éste actualiza la fila existente con la información de la fila devuelta por SelectCommand y establece
el RowState de la fila existente en Unchanged . Si una fila devuelta por SelectCommand tiene un valor de clave
principal que no coincide con ninguno de los valores de clave principal de las filas de DataSet , el método Fill
agrega una nueva fila con un RowState de Unchanged .

NOTE
Si SelectCommand devuelve los resultados de una combinación externa (OUTER JOIN), DataAdapter no establecerá un
valor PrimaryKey para la tabla DataTable resultante. Debe definir PrimaryKey para asegurarse de que las filas
duplicadas se resuelven correctamente. Para obtener más información, vea definir claves principales.

Para controlar las excepciones que se pueden producir al Update llamar al método, puede utilizar RowUpdated el
evento para responder a los errores de actualización de fila a medida que se producen (vea controlar eventos
DataAdapter) true , o puede establecer DataAdapter.ContinueUpdateOnError en antes de llamar Update a y
responder a la información de error almacenada RowError en la propiedad de una fila determinada cuando se
completa la actualización (vea la información de error de fila).
NOTE
Llamar AcceptChanges a DataRow DataRow Current en DataSet , DataTable o Original hará que todos los
valores de se sobrescriban con los valores de. DataRow Si se han modificado los valores de campo que identifican de
forma única a una fila, los valores AcceptChanges dejarán de coincidir con los valores del origen de datos después de
llamar a Original . Se llama automáticamente a AcceptChanges para cada fila durante una llamada al método Update
de DataAdapter . Puede conservar los valores originales durante una llamada al método Update estableciendo primero la
propiedad AcceptChangesDuringUpdate de DataAdapter en false o creando un controlador de eventos para el evento
RowUpdated y estableciendo Status en SkipCurrentRow. Para obtener más información, vea combinar el contenido de un
conjunto de datos y controlar eventos DataAdapter.

Ejemplo
En los siguientes ejemplos se muestra cómo realizar actualizaciones de las filas modificadas estableciendo
UpdateCommand explícitamente DataAdapter el valor de Update y llamando a su método. Observe cómo el
parámetro especificado en la cláusula WHERE de la instrucción UPDATE tiene el valor adecuado para usar el valor
Original de SourceColumn . Este hecho es muy importante ya que el valor Current puede haber sido
modificado de forma que ya no coincida con el valor del origen de datos. El valor Original es el que se usó para
rellenar la tabla DataTable a partir del origen de datos.

private static void AdapterUpdate(string connectionString)


{
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlDataAdapter dataAdpater = new SqlDataAdapter(
"SELECT CategoryID, CategoryName FROM Categories",
connection);

dataAdpater.UpdateCommand = new SqlCommand(


"UPDATE Categories SET CategoryName = @CategoryName " +
"WHERE CategoryID = @CategoryID", connection);

dataAdpater.UpdateCommand.Parameters.Add(
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add(


"@CategoryID", SqlDbType.Int);
parameter.SourceColumn = "CategoryID";
parameter.SourceVersion = DataRowVersion.Original;

DataTable categoryTable = new DataTable();


dataAdpater.Fill(categoryTable);

DataRow categoryRow = categoryTable.Rows[0];


categoryRow["CategoryName"] = "New Beverages";

dataAdpater.Update(categoryTable);

Console.WriteLine("Rows after update.");


foreach (DataRow row in categoryTable.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
Private Sub AdapterUpdate(ByVal connectionString As String)

Using connection As SqlConnection = New SqlConnection( _


connectionString)

Dim adapter As SqlDataAdapter = New SqlDataAdapter( _


"SELECT CategoryID, CategoryName FROM dbo.Categories", _
connection)

adapter.UpdateCommand = New SqlCommand( _


"UPDATE Categories SET CategoryName = @CategoryName " & _
"WHERE CategoryID = @CategoryID", connection)

adapter.UpdateCommand.Parameters.Add( _
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim parameter As SqlParameter = _


adapter.UpdateCommand.Parameters.Add( _
"@CategoryID", SqlDbType.Int)
parameter.SourceColumn = "CategoryID"
parameter.SourceVersion = DataRowVersion.Original

Dim categoryTable As New DataTable


adapter.Fill(categoryTable)

Dim categoryRow As DataRow = categoryTable.Rows(0)


categoryRow("CategoryName") = "New Beverages"

adapter.Update(categoryTable)

Console.WriteLine("Rows after update.")


Dim row As DataRow
For Each row In categoryTable.Rows
Console.WriteLine("{0}: {1}", row(0), row(1))
Next
End Using
End Sub

Columnas AutoIncrement
Si las tablas del origen de datos tienen columnas con incremento automático, puede rellenar las columnas de
DataSet devolviendo el valor de incremento automático como un parámetro de salida de un procedimiento
almacenado y asignándolo a una columna de la tabla, o devolviendo el valor de incremento automático de la
primera fila de un conjunto de resultados devuelto por un procedimiento almacenado o una instrucción SQL, o
mediante el evento RowUpdated de DataAdapter para ejecutar una instrucción SELECT adicional. Para obtener
más información y un ejemplo, vea recuperar valores de identidad o Autonumérico.

Orden de las inserciones, actualizaciones y eliminaciones


En algunas circunstancias, es importante el orden en que se envían al origen de datos los cambios realizados en
el DataSet . Por ejemplo, si se actualiza el valor de una clave principal de una fila existente y se ha agregado una
nueva fila con el nuevo valor de la clave principal como una clave externa, es importante que la actualización de
la fila se procese antes que la inserción.
Puede usar el método Select de DataTable para devolver una matriz DataRow que solo haga referencia a filas
con un estado RowState determinado. A continuación, puede pasar la matriz DataRow al método Update de
DataAdapter para procesar las filas modificadas. Al especificar un subconjunto de filas que modificar, puede
controlar el orden en que se procesan las inserciones, actualizaciones y eliminaciones.
Ejemplo
Por ejemplo, en el código siguiente se garantiza que en primer lugar se realizan en la tabla las eliminaciones de
filas, después las actualizaciones y finalmente las inserciones.

Dim table As DataTable = dataSet.Tables("Customers")

' First process deletes.


dataSet.Update(table.Select(Nothing, Nothing, _
DataViewRowState.Deleted))

' Next process updates.


adapter.Update(table.Select(Nothing, Nothing, _
DataViewRowState.ModifiedCurrent))

' Finally, process inserts.


adapter.Update(table.Select(Nothing, Nothing, _
DataViewRowState.Added))

DataTable table = dataSet.Tables["Customers"];

// First process deletes.


adapter.Update(table.Select(null, null, DataViewRowState.Deleted));

// Next process updates.


adapter.Update(table.Select(null, null,
DataViewRowState.ModifiedCurrent));

// Finally, process inserts.


adapter.Update(table.Select(null, null, DataViewRowState.Added));

Usar un objeto DataAdapter para recuperar y actualizar datos


Puede usar un objeto DataAdapter para recuperar y actualizar los datos.
El ejemplo usa DataAdapter.AcceptChangesDuringFill para clonar los datos en la base de datos. Si la
propiedad se establece como false, no se llama a AcceptChanges al rellenar la tabla y las filas que se
acaban de agregar se tratan como filas insertadas. Por lo tanto, el ejemplo usa estas filas para insertar las
filas nuevas en la base de datos.
Los ejemplos usan DataAdapter.TableMappings para definir la asignación entre la tabla de origen y
DataTable.
El ejemplo usa DataAdapter.FillLoadOption para determinar cómo rellena el adaptador el objeto DataTable
de DbDataReader. Al crear un objeto DataTable, solo puede escribir los datos de la base de datos en la
versión actual o en la versión original si establece la propiedad como LoadOption.Upsert o
LoadOption.PreserveChanges.
El ejemplo también actualizará la tabla mediante DbDataAdapter.UpdateBatchSize para realizar
operaciones por lotes.
Antes de compilar y ejecutar el ejemplo, debe crear la base de datos de ejemplo:

USE [master]
GO

CREATE DATABASE [MySchool]

GO
USE [MySchool]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Course]([CourseID] [nvarchar](10) NOT NULL,
[Year] [smallint] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[Credits] [int] NOT NULL,
[DepartmentID] [int] NOT NULL,
CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED
(
[CourseID] ASC,
[Year] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Department]([DepartmentID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Budget] [money] NOT NULL,
[StartDate] [datetime] NOT NULL,
[Administrator] [int] NULL,
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DepartmentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

GO

INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C1045', 2012,
N'Calculus', 4, 7)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C1061', 2012,
N'Physics', 4, 1)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C2021', 2012,
N'Composition', 3, 2)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C2042', 2012,
N'Literature', 4, 2)

SET IDENTITY_INSERT [dbo].[Department] ON

INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (1,


N'Engineering', 350000.0000, CAST(0x0000999C00000000 AS DateTime), 2)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (2,
N'English', 120000.0000, CAST(0x0000999C00000000 AS DateTime), 6)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (4,
N'Economics', 200000.0000, CAST(0x0000999C00000000 AS DateTime), 4)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (7,
N'Mathematics', 250024.0000, CAST(0x0000999C00000000 AS DateTime), 3)
SET IDENTITY_INSERT [dbo].[Department] OFF

ALTER TABLE [dbo].[Course] WITH CHECK ADD CONSTRAINT [FK_Course_Department] FOREIGN KEY([DepartmentID])
REFERENCES [dbo].[Department] ([DepartmentID])
GO
ALTER TABLE [dbo].[Course] CHECK CONSTRAINT [FK_Course_Department]
GO

C#y Visual Basic proyectos con este ejemplo de código se pueden encontrar en ejemplos de código para
desarrolladores.
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using CSDataAdapterOperations.Properties;

namespace CSDataAdapterOperations.Properties {
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {

private static Settings defaultInstance = ((Settings)


(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

public static Settings Default {


get {
return defaultInstance;
}
}

[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Data Source=(local);Initial
Catalog=MySchool;Integrated Security=True")]
public string MySchoolConnectionString {
get {
return ((string)(this["MySchoolConnectionString"]));
}
}
}
}

class Program {
static void Main(string[] args) {
Settings settings = new Settings();

// Copy the data from the database. Get the table Department and Course from the database.
String selectString = @"SELECT [DepartmentID],[Name],[Budget],[StartDate],[Administrator]
FROM [MySchool].[dbo].[Department];

SELECT [CourseID],@Year as [Year],Max([Title]) as [Title],


Max([Credits]) as [Credits],Max([DepartmentID]) as [DepartmentID]
FROM [MySchool].[dbo].[Course]
Group by [CourseID]";

DataSet mySchool = new DataSet();

SqlCommand selectCommand = new SqlCommand(selectString);


SqlParameter parameter = selectCommand.Parameters.Add("@Year", SqlDbType.SmallInt, 2);
parameter.Value = new Random(DateTime.Now.Millisecond).Next(9999);

// Use DataTableMapping to map the source tables and the destination tables.
DataTableMapping[] tableMappings = {new DataTableMapping("Table", "Department"), new
DataTableMapping("Table1", "Course")};
CopyData(mySchool, settings.MySchoolConnectionString, selectCommand, tableMappings);

Console.WriteLine("The following tables are from the database.");


foreach (DataTable table in mySchool.Tables) {
Console.WriteLine(table.TableName);
ShowDataTable(table);
}

// Roll back the changes


DataTable department = mySchool.Tables["Department"];
DataTable course = mySchool.Tables["Course"];

department.Rows[0]["Name"] = "New" + department.Rows[0][1];


course.Rows[0]["Title"] = "New" + course.Rows[0]["Title"];
course.Rows[0]["Credits"] = 10;
Console.WriteLine("After we changed the tables:");
foreach (DataTable table in mySchool.Tables) {
Console.WriteLine(table.TableName);
ShowDataTable(table);
}

department.RejectChanges();
Console.WriteLine("After use the RejectChanges method in Department table to roll back the changes:");
ShowDataTable(department);

DataColumn[] primaryColumns = { course.Columns["CourseID"] };


DataColumn[] resetColumns = { course.Columns["Title"] };
ResetCourse(course, settings.MySchoolConnectionString, primaryColumns, resetColumns);
Console.WriteLine("After use the ResetCourse method in Course table to roll back the changes:");
ShowDataTable(course);

// Batch update the table.


String insertString = @"Insert into [MySchool].[dbo].[Course]([CourseID],[Year],[Title],
[Credits],[DepartmentID])
values (@CourseID,@Year,@Title,@Credits,@DepartmentID)";
SqlCommand insertCommand = new SqlCommand(insertString);
insertCommand.Parameters.Add("@CourseID", SqlDbType.NVarChar, 10, "CourseID");
insertCommand.Parameters.Add("@Year", SqlDbType.SmallInt, 2, "Year");
insertCommand.Parameters.Add("@Title", SqlDbType.NVarChar, 100, "Title");
insertCommand.Parameters.Add("@Credits", SqlDbType.Int, 4, "Credits");
insertCommand.Parameters.Add("@DepartmentID", SqlDbType.Int, 4, "DepartmentID");

const Int32 batchSize = 10;


BatchInsertUpdate(course, settings.MySchoolConnectionString, insertCommand, batchSize);
}

private static void CopyData(DataSet dataSet, String connectionString, SqlCommand selectCommand,


DataTableMapping[] tableMappings) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
selectCommand.Connection = connection;

connection.Open();

using (SqlDataAdapter adapter = new SqlDataAdapter(selectCommand))


{adapter.TableMappings.AddRange(tableMappings);
// If set the AcceptChangesDuringFill as the false, AcceptChanges will not be called on a
// DataRow after it is added to the DataTable during any of the Fill operations.
adapter.AcceptChangesDuringFill = false;

adapter.Fill(dataSet);
}
}
}

// Roll back only one column or several columns data of the Course table by call ResetDataTable method.
private static void ResetCourse(DataTable table, String connectionString,
DataColumn[] primaryColumns, DataColumn[] resetColumns) {
table.PrimaryKey = primaryColumns;

// Build the query string


String primaryCols = String.Join(",", primaryColumns.Select(col => col.ColumnName));
String resetCols = String.Join(",", resetColumns.Select(col => $"Max({col.ColumnName}) as
{col.ColumnName}"));

String selectString = $"Select {primaryCols},{resetCols} from Course Group by {primaryCols}");

SqlCommand selectCommand = new SqlCommand(selectString);

ResetDataTable(table, connectionString, selectCommand);


}

// RejectChanges will roll back all changes made to the table since it was loaded, or the last time
AcceptChanges
// was called. When you copy from the database, you can lose all the data after calling RejectChanges
// was called. When you copy from the database, you can lose all the data after calling RejectChanges
// The ResetDataTable method rolls back one or more columns of data.
private static void ResetDataTable(DataTable table, String connectionString,
SqlCommand selectCommand) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
selectCommand.Connection = connection;

connection.Open();

using (SqlDataAdapter adapter = new SqlDataAdapter(selectCommand)) {


// The incoming values for this row will be written to the current version of each
// column. The original version of each column's data will not be changed.
adapter.FillLoadOption = LoadOption.Upsert;

adapter.Fill(table);
}
}
}

private static void BatchInsertUpdate(DataTable table, String connectionString,


SqlCommand insertCommand, Int32 batchSize) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
insertCommand.Connection = connection;
// When setting UpdateBatchSize to a value other than 1, all the commands
// associated with the SqlDataAdapter have to have their UpdatedRowSource
// property set to None or OutputParameters. An exception is thrown otherwise.
insertCommand.UpdatedRowSource = UpdateRowSource.None;

connection.Open();

using (SqlDataAdapter adapter = new SqlDataAdapter()) {


adapter.InsertCommand = insertCommand;
// Gets or sets the number of rows that are processed in each round-trip to the server.
// Setting it to 1 disables batch updates, as rows are sent one at a time.
adapter.UpdateBatchSize = batchSize;

adapter.Update(table);

Console.WriteLine("Successfully to update the table.");


}
}
}

private static void ShowDataTable(DataTable table) {


foreach (DataColumn col in table.Columns) {
Console.Write("{0,-14}", col.ColumnName);
}
Console.WriteLine("{0,-14}", "RowState");

foreach (DataRow row in table.Rows) {


foreach (DataColumn col in table.Columns) {
if (col.DataType.Equals(typeof(DateTime)))
Console.Write("{0,-14:d}", row[col]);
else if (col.DataType.Equals(typeof(Decimal)))
Console.Write("{0,-14:C}", row[col]);
else
Console.Write("{0,-14}", row[col]);
}
Console.WriteLine("{0,-14}", row.RowState);
}
}
}

Vea también
Objetos DataAdapter y DataReader
Estados y versiones de filas
AcceptChange y RejectChange
Combinar contenido de DataSet
Recuperación de valores autonuméricos y de identidad
Información general sobre ADO.NET
Controlar eventos de DataAdapter
21/03/2020 • 10 minutes to read • Edit Online

DataAdapter de ADO.NET expone tres eventos que se pueden utilizar para responder a los cambios efectuados
en los datos en el origen. En la siguiente tabla se muestran los eventos de DataAdapter .

EVEN TO DESC RIP C IÓ N

RowUpdating Está a punto de comenzar una operación UPDATE, INSERT o


DELETE en una fila (mediante una llamada a uno de los
métodos Update ).

RowUpdated Se ha completado una operación UPDATE, INSERT o DELETE


en una fila (mediante una llamada a uno de los métodos
Update ).

FillError Se ha producido un error durante una operación Fill .

RowUpdating y RowUpdated
El evento RowUpdating se activa antes de que se produzca la actualización de una fila del DataSet en el origen de
datos. El evento RowUpdated se activa después de que se produzca la actualización de una fila del DataSet en el
origen de datos. Por lo tanto, puede utilizar RowUpdating para modificar el comportamiento de la actualización
antes de que tenga lugar, proporcionar un control adicional del proceso durante la actualización, conservar una
referencia a la fila actualizada, cancelar la actualización actual y programarla como parte de un proceso por lotes
que se ejecutará después, entre otras acciones. RowUpdated es útil para reaccionar cuando se producen errores y
excepciones durante la actualización. Puede agregar información de errores al DataSet , así como
procedimientos de reintento, etcétera.
Los argumentos RowUpdatingEventArgs y RowUpdatedEventArgs que se pasan a los eventos RowUpdating y
RowUpdated incluyen lo siguiente: una propiedad Command que hace referencia al objeto Command que se está
utilizando para realizar la actualización; una propiedad Row que hace referencia al objeto DataRow que contiene
la información actualizada; una propiedad StatementType para el tipo de actualización que se está llevando a
cabo; el valor de TableMapping , si es pertinente y el valor de Status de la operación.
Puede utilizar la propiedad Status para determinar si se ha producido o no un error durante la operación y, si lo
desea, controlar las acciones que se emprenden en las filas actuales y las resultantes de la operación. Cuando se
produce el evento, la propiedad Status toma el valor Continue o ErrorsOccurred . En la tabla siguiente se
muestran los valores que se pueden asignar a la propiedad Status para controlar las acciones posteriores en el
proceso de actualización.

STAT US DESC RIP C IÓ N

Continue Continuar la operación de actualización.

ErrorsOccurred Anular la operación de actualización e iniciar una excepción.

SkipCurrentRow Omitir la fila actual y continuar la operación de actualización.


STAT US DESC RIP C IÓ N

SkipAllRemainingRows Anular la operación de actualización sin iniciar una excepción.

Al establecer a la propiedad Status en el valor ErrorsOccurred se inicia una excepción. Puede controlar qué
excepciones se inician si establece la propiedad Errors en la excepción que desee. El uso de un valor distinto
para la propiedad Status evita que se inicie una excepción.
También puede utilizar la propiedad ContinueUpdateOnError para controlar los errores producidos en las filas
actualizadas. Cuando DataAdapter.ContinueUpdateOnError tiene el valor true y la actualización de una fila inicia
una excepción, el texto de la excepción se coloca en la información RowError de la fila en cuestión y el proceso
continúa sin que se inicie una excepción. De esta forma, puede responder a los errores cuando se complete el
proceso Update , a diferencia del evento RowUpdated , que permite responder a los errores cuando se detectan.
En el ejemplo de código siguiente se muestra cómo se pueden agregar y quitar controladores de eventos. El
controlador de eventos RowUpdating mantiene un registro de todos los registros eliminados y una marca de
tiempo asociada a cada uno de ellos. El RowUpdated controlador de eventos agrega RowError información de
error DataSet a la propiedad de la fila en ContinueUpdateOnError = true el , suprime la excepción y continúa el
procesamiento (reflejando el comportamiento de ).

' Assumes that connection is a valid SqlConnection object.


Dim custAdapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CustomerID, CompanyName FROM Customers", connection)

' Add handlers.


AddHandler custAdapter.RowUpdating, New SqlRowUpdatingEventHandler( _
AddressOf OnRowUpdating)
AddHandler custAdapter.RowUpdated, New SqlRowUpdatedEventHandler(
AddressOf OnRowUpdated)

' Set DataAdapter command properties, fill DataSet, and modify DataSet.

custAdapter.Update(custDS, "Customers")

' Remove handlers.


RemoveHandler custAdapter.RowUpdating, _
New SqlRowUpdatingEventHandler(AddressOf OnRowUpdating)
RemoveHandler custAdapter.RowUpdated, _
New SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)

Private Shared Sub OnRowUpdating(sender As Object, _


args As SqlRowUpdatingEventArgs)
If args.StatementType = StatementType.Delete Then
Dim tw As System.IO.TextWriter = _
System.IO.File.AppendText("Deletes.log")
tw.WriteLine( _
"{0}: Customer {1} Deleted.", DateTime.Now, args.Row(_
"CustomerID", DataRowVersion.Original))
tw.Close()
End If
End Sub

Private Shared Sub OnRowUpdated( _


sender As Object, args As SqlRowUpdatedEventArgs)
If args.Status = UpdateStatus.ErrorsOccurred
args.Status = UpdateStatus.SkipCurrentRow
args.Row.RowError = args.Errors.Message
End If
End Sub
// Assumes that connection is a valid SqlConnection object.
SqlDataAdapter custAdapter = new SqlDataAdapter(
"SELECT CustomerID, CompanyName FROM Customers", connection);

// Add handlers.
custAdapter.RowUpdating += new SqlRowUpdatingEventHandler(OnRowUpdating);
custAdapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);

// Set DataAdapter command properties, fill DataSet, modify DataSet.

custAdapter.Update(custDS, "Customers");

// Remove handlers.
custAdapter.RowUpdating -= new SqlRowUpdatingEventHandler(OnRowUpdating);
custAdapter.RowUpdated -= new SqlRowUpdatedEventHandler(OnRowUpdated);

protected static void OnRowUpdating(


object sender, SqlRowUpdatingEventArgs args)
{
if (args.StatementType == StatementType.Delete)
{
System.IO.TextWriter tw = System.IO.File.AppendText("Deletes.log");
tw.WriteLine(
"{0}: Customer {1} Deleted.", DateTime.Now,
args.Row["CustomerID", DataRowVersion.Original]);
tw.Close();
}
}

protected static void OnRowUpdated(


object sender, SqlRowUpdatedEventArgs args)
{
if (args.Status == UpdateStatus.ErrorsOccurred)
{
args.Row.RowError = args.Errors.Message;
args.Status = UpdateStatus.SkipCurrentRow;
}
}

FillError
Cuando se produce un error durante una operación DataAdapter , el objeto FillError provoca el evento Fill .
Este tipo de error suele producirse si al convertir los datos de la fila que se agrega a un tipo de .NET Framework
se produce una pérdida de precisión.
Si el error se produce durante una operación Fill , la fila actual no se agrega al objeto DataTable . El evento
FillError permite resolver el error y agregar la fila o bien pasar por alto la fila excluida y continuar con la
operación Fill .
El objeto FillErrorEventArgs que se pasa al evento FillError puede contener varias propiedades que permiten
reaccionar en caso de errores y resolverlos. En la tabla siguiente se muestran las propiedades del objeto
FillErrorEventArgs .

P RO P IEDA D DESC RIP C IÓ N

Errors Exception que se ha producido.

DataTable Objeto DataTable que se estaba llenando cuando ocurrió


el error.
P RO P IEDA D DESC RIP C IÓ N

Values Matriz de objetos que contiene los valores de la fila que se


está agregando cuando se produce el error. Las referencias
de orden de la matriz Values coinciden con las de las
columnas de la fila que se está agregando. Por ejemplo,
Values[0] es el valor que se agrega en la primera columna
de la fila.

Continue Permite elegir si desea iniciar una excepción o no. Si establece


la propiedad Continue en false , la operación Fill en
curso se detiene y se inicia una excepción. Si establece la
propiedad Continue en true , la operación Fill
continúa pese al error.

En el siguiente ejemplo de código se agrega un controlador para el evento FillError del objeto DataAdapter .
En el código del evento FillError , el ejemplo determina si hay una posible pérdida de precisión y ofrece la
posibilidad de reaccionar ante la excepción.

AddHandler adapter.FillError, New FillErrorEventHandler( _


AddressOf FillError)

Dim dataSet As DataSet = New DataSet


adapter.Fill(dataSet, "ThisTable")

Private Shared Sub FillError(sender As Object, _


args As FillErrorEventArgs)
If args.Errors.GetType() Is Type.GetType("System.OverflowException") Then
' Code to handle precision loss.
' Add a row to table using the values from the first two columns.
DataRow myRow = args.DataTable.Rows.Add(New Object() _
{args.Values(0), args.Values(1), DBNull.Value})
' Set the RowError containing the value for the third column.
myRow.RowError = _
"OverflowException encountered. Value from data source: " & _
args.Values(2)
args.Continue = True
End If
End Sub

adapter.FillError += new FillErrorEventHandler(FillError);

DataSet dataSet = new DataSet();


adapter.Fill(dataSet, "ThisTable");

protected static void FillError(object sender, FillErrorEventArgs args)


{
if (args.Errors.GetType() == typeof(System.OverflowException))
{
// Code to handle precision loss.
//Add a row to table using the values from the first two columns.
DataRow myRow = args.DataTable.Rows.Add(new object[]
{args.Values[0], args.Values[1], DBNull.Value});
//Set the RowError containing the value for the third column.
myRow.RowError =
"OverflowException Encountered. Value from data source: " +
args.Values[2];
args.Continue = true;
}
}
Consulte también
Objetos DataAdapter y DataReader
Controlar eventos de DataSet
Control de eventos de DataTable
Eventos
Información general de ADO.NET
Realizar operaciones por lotes utilizando objetos
DataAdapter
21/03/2020 • 9 minutes to read • Edit Online

La compatibilidad con las operaciones por lotes en ADO.NET permite que un DataAdapter agrupe operaciones
INSERT, UPDATE y DELETE desde un DataSet o una DataTable al servidor, en lugar de enviar las operaciones de una
en una. La reducción del número de viajes de ida y vuelta (round trip) al servidor tiene como resultado una mejora
considerable del rendimiento. Las actualizaciones por lotes son compatibles con los proveedores de datos de .NET
para SQL Server (System.Data.SqlClient) y Oracle (System.Data.OracleClient).
Al actualizar una base de datos con modificaciones de un DataSet en versiones anteriores de ADO.NET, el método
Update de un DataAdapter realizaba actualizaciones de las filas de la base de datos de una en una. A medida que
recorría las filas de la DataTable especificada, examinaba cada DataRow para ver si se había modificado. Si se había
modificado la fila, llamaba al UpdateCommand , InsertCommand o DeleteCommand apropiado, en función del valor de
propiedad RowState de la fila. Cada actualización de una fila implicaba un viaje de ida y vuelta (round trip) a la base
de datos.
A partir de ADO.NET 2.0, DbDataAdapter expone una propiedad UpdateBatchSize. Si se establece el
UpdateBatchSize en un valor entero positivo, se producen actualizaciones en la base de datos que se envían como
lotes del tamaño especificado. Por ejemplo, si se establece el UpdateBatchSize en 10, se agrupan 10 instrucciones
separadas y se envían en un único lote. Si se establece el UpdateBatchSize en 0, el DataAdapter utilizará el mayor
tamaño de lote admitido por el servidor. Si se establece el valor en 1, se deshabilitan las actualizaciones por lotes y
las filas se envían de una en una.
Si se ejecuta un lote demasiado grande, el rendimiento podría verse afectado. Por tanto, es conveniente realizar
pruebas a fin de determinar el valor óptimo del tamaño del lote antes de implementar la aplicación.

Utilizar la propiedad UpdateBatchSize


Al habilitar las actualizaciones por lotes, el valor de propiedad UpdatedRowSource de UpdateCommand ,
InsertCommand y DeleteCommand del DataAdapter debe establecerse en None o OutputParameters. Al realizar una
actualización por lotes, el valor UpdatedRowSource o FirstReturnedRecord de la propiedad Both del comando no es
válido.
En el siguiente procedimiento se muestra cómo se utiliza la propiedad UpdateBatchSize . El procedimiento toma
dos DataSet argumentos, un objeto que tiene columnas que representan los campos ProductCategor yID y
Name de la tabla Production.ProductCategor y y un entero que representa el tamaño del lote (el número de
filas del lote). El código crea un objeto SqlDataAdapter nuevo y se establecen las propiedades UpdateCommand,
InsertCommand y DeleteCommand. En el código se supone que el objeto DataSet tiene filas modificadas. Se
establece la propiedad UpdateBatchSize y se ejecuta la actualización.
Public Sub BatchUpdate( _
ByVal dataTable As DataTable, ByVal batchSize As Int32)
' Assumes GetConnectionString() returns a valid connection string.
Dim connectionString As String = GetConnectionString()

' Connect to the AdventureWorks database.


Using connection As New SqlConnection(connectionString)
' Create a SqlDataAdapter.
Dim adapter As New SqlDataAdapter()

'Set the UPDATE command and parameters.


adapter.UpdateCommand = New SqlCommand( _
"UPDATE Production.ProductCategory SET " _
& "Name=@Name WHERE ProductCategoryID=@ProdCatID;", _
connection)
adapter.UpdateCommand.Parameters.Add("@Name", _
SqlDbType.NVarChar, 50, "Name")
adapter.UpdateCommand.Parameters.Add("@ProdCatID", _
SqlDbType.Int, 4, " ProductCategoryID ")
adapter.UpdateCommand.UpdatedRowSource = _
UpdateRowSource.None

'Set the INSERT command and parameter.


adapter.InsertCommand = New SqlCommand( _
"INSERT INTO Production.ProductCategory (Name) VALUES (@Name);", _
connection)
adapter.InsertCommand.Parameters.Add("@Name", _
SqlDbType.NVarChar, 50, "Name")
adapter.InsertCommand.UpdatedRowSource = _
UpdateRowSource.None

'Set the DELETE command and parameter.


adapter.DeleteCommand = New SqlCommand( _
"DELETE FROM Production.ProductCategory " _
& "WHERE ProductCategoryID=@ProdCatID;", connection)
adapter.DeleteCommand.Parameters.Add("@ProdCatID", _
SqlDbType.Int, 4, " ProductCategoryID ")
adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None

' Set the batch size.


adapter.UpdateBatchSize = batchSize

' Execute the update.


adapter.Update(dataTable)
End Using
End Sub
public static void BatchUpdate(DataTable dataTable,Int32 batchSize)
{
// Assumes GetConnectionString() returns a valid connection string.
string connectionString = GetConnectionString();

// Connect to the AdventureWorks database.


using (SqlConnection connection = new
SqlConnection(connectionString))
{

// Create a SqlDataAdapter.
SqlDataAdapter adapter = new SqlDataAdapter();

// Set the UPDATE command and parameters.


adapter.UpdateCommand = new SqlCommand(
"UPDATE Production.ProductCategory SET "
+ "Name=@Name WHERE ProductCategoryID=@ProdCatID;",
connection);
adapter.UpdateCommand.Parameters.Add("@Name",
SqlDbType.NVarChar, 50, "Name");
adapter.UpdateCommand.Parameters.Add("@ProdCatID",
SqlDbType.Int, 4, "ProductCategoryID");
adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;

// Set the INSERT command and parameter.


adapter.InsertCommand = new SqlCommand(
"INSERT INTO Production.ProductCategory (Name) VALUES (@Name);",
connection);
adapter.InsertCommand.Parameters.Add("@Name",
SqlDbType.NVarChar, 50, "Name");
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;

// Set the DELETE command and parameter.


adapter.DeleteCommand = new SqlCommand(
"DELETE FROM Production.ProductCategory "
+ "WHERE ProductCategoryID=@ProdCatID;", connection);
adapter.DeleteCommand.Parameters.Add("@ProdCatID",
SqlDbType.Int, 4, "ProductCategoryID");
adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;

// Set the batch size.


adapter.UpdateBatchSize = batchSize;

// Execute the update.


adapter.Update(dataTable);
}
}

Controlar errores y eventos relacionados con la actualización por lotes


El DataAdapter tiene dos eventos relacionados con la actualización: RowUpdating y RowUpdated . En las
versiones anteriores de ADO.NET, cuando se deshabilita el procesamiento por lotes, cada uno de estos eventos se
genera una vez para cada fila procesada. RowUpdating se genera antes de que se produzca la actualización y
RowUpdated se genera después de que se haya completado la actualización de la base de datos.
Cambios en el comportamiento de eventos con actualizaciones por lotes
Si se habilita el procesamiento por lotes, se actualizan varias filas en una única operación de base de datos. Por
tanto, solo se produce un evento RowUpdated para cada lote, mientras que el evento RowUpdating se produce para
cada fila procesada. Si se deshabilita el procesamiento por lotes, los dos eventos se activan con entrelazado
individualizado, donde los eventos RowUpdating y RowUpdated se activan para una fila y, a continuación, se activan
los eventos RowUpdating y RowUpdated para la siguiente fila, hasta que se hayan procesado todas las filas.
Obtener acceso a filas actualizadas
Si se deshabilita el procesamiento por lotes, se puede obtener acceso a la fila que se está actualizando mediante la
propiedad Row de la clase RowUpdatedEventArgs.
Cuando se habilita el procesamiento por lotes, se genera un único evento RowUpdated para varias filas. Por tanto, el
valor de la propiedad Row para cada fila es nulo. Aún así, los eventos RowUpdating se generarán para cada fila. El
método CopyToRows de la clase RowUpdatedEventArgs permite obtener acceso a las filas procesadas al copiar
referencias a las mismas en una matriz. Si no se está procesando ninguna fila, CopyToRows inicia una
ArgumentNullException. Utilice la propiedad RowCount para devolver el número de filas procesadas antes de
llamar al método CopyToRows.
Controlar errores de datos
La ejecución por lotes tiene el mismo efecto que la ejecución de cada instrucción por separado. Las instrucciones se
ejecutan en el mismo orden en el que se agregaron al lote. Los errores se controlan de la misma forma en el modo
de procesamiento por lotes que cuando éste se encuentra deshabilitado. Cada fila se procesa por separado. Solo
aquellas filas procesadas correctamente en la base de datos se actualizarán en la DataRow correspondiente dentro
de la DataTable.
El proveedor de datos y el servidor de bases de datos back-end determinan qué construcciones SQL son
compatibles para la ejecución por lotes. Es posible que se inicie una excepción si se envía una instrucción no
compatible para su ejecución.

Consulte también
Objetos DataAdapter y DataReader
Actualizar orígenes de datos con objetos DataAdapter
Controlar eventos de DataAdapter
Información general de ADO.NET
Transacciones y simultaneidad
23/10/2019 • 2 minutes to read • Edit Online

Una transacción consiste en un comando único o en un grupo de comandos que se ejecutan como un paquete.
Las transacciones permiten combinar varias operaciones en una sola unidad de trabajo. Si en un punto de la
transacción se produjera un error, todas las actualizaciones podrían revertirse y devolverse al estado que tenían
antes de la transacción.
Una transacción debe ajustarse a las propiedades ACID (atomicidad, coherencia, aislamiento y durabilidad) para
poder garantizar la coherencia de datos. La mayoría de los sistemas de bases de datos relacionales, como
Microsoft SQL Server, admiten transacciones, al proporcionar funciones de bloqueo, registro y administración
de transacciones cada vez que una aplicación cliente realiza una operación de actualización, inserción o
eliminación.

NOTE
Las transacciones que requieren varios recursos pueden reducir la simultaneidad si la duración del bloqueo es demasiado
larga. Por ello, haga la transacción lo más corta posible.

Las transacciones explícitas en procedimientos almacenados suelen dar mejores resultados cuando una
transacción implica el uso de varias tablas en la misma base de datos o servidor. Se pueden crear transacciones
en procedimientos almacenados de SQL Server mediante las instrucciones BEGIN TRANSACTION ,
COMMIT TRANSACTION o ROLLBACK TRANSACTION de Transact-SQL. Para obtener más información, vea los Libros en
pantalla de SQL Server.
Las transacciones que implican diferentes administradores de recursos, como una transacción entre SQL Server
y Oracle, requieren una transacción distribuida.

En esta sección
Transacciones locales
Muestra cómo realizar transacciones en una base de datos.
Transacciones distribuidas
Describe cómo realizar transacciones distribuidas en ADO.NET.
Integración de System.Transactions con SQL Server
Describe System.Transactions la integración con SQL Server para trabajar con transacciones distribuidas.
Simultaneidad optimista
Describe la simultaneidad optimista y pesimista, y cómo puede probar las infracciones de simultaneidad.

Vea también
Principios de la transacción
Conexión a un origen de datos
Comandos y parámetros
Objetos DataAdapter y DataReader
Objetos DbProviderFactory
Información general sobre ADO.NET
Transacciones locales
23/10/2019 • 6 minutes to read • Edit Online

Las transacciones de ADO.NET se usan cuando se desea enlazar varias tareas juntas para que se ejecuten como una
sola unidad de trabajo. Por ejemplo, imagine que una aplicación realiza dos tareas. Primero, actualiza una tabla con
información de pedidos. Luego, actualiza una tabla que contiene la información de inventario, cargando en cuenta
los elementos pedidos. Si se produce un error en cualquiera de las tareas, se revierten ambas.

Determinación del tipo de transacción


Una transacción se considera una transacción local cuando es una transacción de una sola fase y se controla
directamente por la base de datos. Una transacción se considera una transacción distribuida cuando está
coordinada por un monitor de transacciones y utiliza mecanismos de error (como confirmación en dos fases) para
la resolución de transacciones.
Cada uno de los proveedores de datos de .NET Framework Transaction tiene su propio objeto para realizar
transacciones locales. Si necesita que se realice una transacción en una base de datos de SQL Server, seleccione
una transacción de System.Data.SqlClient. En transacciones de Oracle, utilice el proveedor
System.Data.OracleClient. Además, hay una DbTransaction clase que está disponible para escribir código
independiente del proveedor que requiere transacciones.

NOTE
Las transacciones son más eficaces cuando se realizan en el servidor. Si trabaja con una base de datos de SQL Server que
hace uso masivo de transacciones explícitas, debería estudiar la posibilidad de escribirlas como procedimientos almacenados
mediante la instrucción BEGIN TRANSACTION de Transact-SQL.

Realización de una transacción mediante una única conexión


En ADO.net, las transacciones se controlan Connection con el objeto. Puede iniciar una transacción local con el
método BeginTransaction . Una vez iniciada una transacción, puede inscribir un comando en esa transacción con la
propiedad Transaction de un objeto Command . Luego, puede confirmar o revertir las modificaciones realizadas en
el origen de datos según el resultado correcto o incorrecto de los componentes de la transacción.

NOTE
El método EnlistDistributedTransaction no se debe emplear en transacciones locales.

El ámbito de la transacción está limitado a la conexión. En el siguiente ejemplo se realiza una transacción explícita
que consta de por dos comandos independientes en el bloque try . Los comandos ejecutan instrucciones INSERT
en la tabla Production. ScrapReason de la base de datos de ejemplo AdventureWorks SQL Server, que se confirman
si no se inicia ninguna excepción. El código del bloque catch revierte la transacción si se produce una excepción.
Si la transacción se anula o la conexión se cierra antes de que se haya completado la transacción, ésta se revierte
automáticamente.

Ejemplo
Para llevar a cabo una transacción, siga estos pasos.
1. Llame al método BeginTransaction del objeto SqlConnection para marcar el comienzo de la transacción. El
método BeginTransaction devuelve una referencia a la transacción. Esta referencia se asigna a los objetos
SqlCommand que están inscritos en la transacción.
2. Asigne el objeto Transaction a la propiedad Transaction del objeto SqlCommand que se va a ejecutar. Si el
comando se ejecuta en una conexión con una transacción activa y el objeto Transaction no se ha asignado
a la propiedad Transaction del objeto Command , se inicia una excepción.
3. Ejecute los comandos necesarios.
4. Llame al método Commit del objeto SqlTransaction para completar la transacción, o al método Rollback para
finalizarla. Si la conexión se cierra o elimina antes de que se hayan ejecutado los métodos Commit o
Rollback, la transacción se revierte.
En el ejemplo de código siguiente se muestra la lógica transaccional mediante ADO.NET con Microsoft SQL Server.

using (SqlConnection connection = new SqlConnection(connectionString))


{
connection.Open();

// Start a local transaction.


SqlTransaction sqlTran = connection.BeginTransaction();

// Enlist a command in the current transaction.


SqlCommand command = connection.CreateCommand();
command.Transaction = sqlTran;

try
{
// Execute two separate commands.
command.CommandText =
"INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')";
command.ExecuteNonQuery();
command.CommandText =
"INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')";
command.ExecuteNonQuery();

// Commit the transaction.


sqlTran.Commit();
Console.WriteLine("Both records were written to database.");
}
catch (Exception ex)
{
// Handle the exception if the transaction fails to commit.
Console.WriteLine(ex.Message);

try
{
// Attempt to roll back the transaction.
sqlTran.Rollback();
}
catch (Exception exRollback)
{
// Throws an InvalidOperationException if the connection
// is closed or the transaction has already been rolled
// back on the server.
Console.WriteLine(exRollback.Message);
}
}
}
Using connection As New SqlConnection(connectionString)
connection.Open()

' Start a local transaction.


Dim sqlTran As SqlTransaction = connection.BeginTransaction()

' Enlist a command in the current transaction.


Dim command As SqlCommand = connection.CreateCommand()
command.Transaction = sqlTran

Try
' Execute two separate commands.
command.CommandText = _
"INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')"
command.ExecuteNonQuery()
command.CommandText = _
"INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')"
command.ExecuteNonQuery()

' Commit the transaction


sqlTran.Commit()
Console.WriteLine("Both records were written to database.")

Catch ex As Exception
' Handle the exception if the transaction fails to commit.
Console.WriteLine(ex.Message)

Try
' Attempt to roll back the transaction.
sqlTran.Rollback()

Catch exRollback As Exception


' Throws an InvalidOperationException if the connection
' is closed or the transaction has already been rolled
' back on the server.
Console.WriteLine(exRollback.Message)
End Try
End Try
End Using

Vea también
Transacciones y simultaneidad
Transacciones distribuidas
Integración de System.Transactions con SQL Server
Información general sobre ADO.NET
Transacciones distribuidas
23/10/2019 • 9 minutes to read • Edit Online

Entre otras cosas, una transacción es un conjunto de tareas relacionadas que se ejecutan correctamente
(confirman) o dan error (anulan) como una unidad. Una transacción distribuida es una transacción que afecta a
varios recursos. Para que una transacción distribuida se confirme, todos los participantes deben garantizar que los
cambios en los datos serán permanentes. Los cambios deben conservarse a pesar de bloqueos del sistema u otros
eventos imprevistos. Si alguno de los participantes no cumple esta garantía, toda la transacción da error y se
revertirán los cambios en los datos en el ámbito de la transacción.

NOTE
Si intenta confirmar o revertir una transacción al iniciar un DataReader mientras la transacción está activa, se produce una
excepción.

Trabajo con System.Transactions


En .NET Framework, las transacciones distribuidas se administran a través de la API del espacio de nombres
System.Transactions. Cuando hay implicados varios administradores de recursos persistentes, la API
System.Transactions delegará el control de las transacciones distribuidas en un monitor de transacciones como el
Coordinador de transacciones distribuidas de Microsoft (MS DTC). Para obtener más información, vea conceptos
básicosde las transacciones.
ADO.NET 2.0 incorporó la compatibilidad con la inscripción en una transacción distribuida mediante el método
EnlistTransaction , que inscribe una conexión en una instancia de Transaction. En las versiones anteriores de
ADO.NET, la inscripción explícita en transacciones distribuidas se realizaba mediante el método
EnlistDistributedTransaction de una conexión que inscribía ésta en una instancia ITransaction, en la que se
permitía la compatibilidad con versiones anteriores. Para obtener más información acerca de las transacciones de
Enterprise Services, consulte interoperabilidad con Enterprise Services y transacciones de com+.
Cuando se usa una transacción System.Transactions con el proveedor .NET Framework para SQL Server en una
base de datos de SQL Server, se usará automáticamente un objeto Transaction ligero. A continuación, la
transacción se puede promover a una transacción distribuida completa si es necesario. Para obtener más
información, vea integración de System. Transactions con SQL Server.

NOTE
El número máximo de transacciones distribuidas en las que puede participar una base de datos de Oracle a la vez se
establece de forma predeterminada en 10. Después de la décima transacción en una conexión a una base de datos de Oracle,
se inicia una excepción. Oracle no admite DDL en las transacciones distribuidas.

Inscripción automática en una transacción distribuida


La inscripción automática es el método predeterminado (y preferido) de integrar conexiones ADO.NET con
System.Transactions . Un objeto de conexión se inscribirá automáticamente en una transacción distribuida
existente si se determina que hay una transacción activa, que, en términos de System.Transaction , significa que
Transaction.Current no es nula. La inscripción automática en transacciones tiene lugar cuando se abre la
conexión. No ocurrirá después, incluso si se ejecuta un comando dentro del ámbito de una transacción. Puede
deshabilitar la inscripción automática en transacciones existentes si especifica Enlist=false como un parámetro
de cadena de conexión para una SqlConnection.ConnectionString, o OLE DB Services=-7 como un parámetro de
cadena de conexión para una OleDbConnection.ConnectionString. Para obtener más información acerca de los
parámetros de cadena de conexión de Oracle y ODBC, vea OracleConnection.ConnectionString y
OdbcConnection.ConnectionString.

Inscripción manual en una transacción distribuida


Si la inscripción automática está deshabilitada o es necesario inscribir la conexión en una transacción que se ha
iniciado después de abierta la conexión, puede inscribirla en una transacción distribuida existente mediante el
método EnlistTransaction del objeto DbConnection correspondiente al proveedor con el que esté trabajando. La
inscripción en una transacción distribuida existente garantiza que, si la transacción se confirma o revierte, también
se confirmarán o revertirán las modificaciones realizadas por el código en el origen de datos.
La inscripción en transacciones distribuidas es especialmente conveniente al agrupar objetos empresariales. Si se
agrupa un objeto empresarial con una conexión abierta, la inscripción automática en transacciones sólo se
produce cuando se abre esa conexión. Si se realizan varias transacciones con el objeto empresarial agrupado, la
conexión abierta para ese objeto no se inscribirá automáticamente en las transacciones recién iniciadas. En este
caso, puede deshabilitar la inscripción automática de la conexión en la transacción e inscribir la conexión en las
transacciones mediante EnlistTransaction .
EnlistTransaction toma un único argumento de tipo Transaction que es una referencia a la transacción existente.
Después de llamar al método EnlistTransaction de la conexión, todas las modificaciones realizadas en el origen
de datos mediante la conexión se incluyen en la transacción. Si se pasa un valor nulo, se anula la inscripción de la
conexión de su inscripción actual en transacciones distribuidas. Tenga en cuenta que la conexión se debe abrir
antes de llamar a EnlistTransaction .

NOTE
Una vez que una conexión se inscribe explícitamente en una transacción, no se puede anular su inscripción ni inscribirse en
otra transacción hasta que finaliza la primera transacción.

Cau t i on

Si la conexión ya ha comenzado una transacción mediante el método EnlistTransaction de la conexión,


BeginTransaction inicia una excepción. No obstante, si la transacción es una transacción local iniciada en el origen
de datos (por ejemplo, al ejecutar la instrucción BEGIN TRANSACTION de forma explícita mediante un
SqlCommand), EnlistTransaction la revertirá e inscribirá en la transacción distribuida existente como se ha
solicitado. No recibirá aviso de que la transacción local se ha revertido y deberá administrar todas las
transacciones locales no iniciadas mediante BeginTransaction. Si usa el proveedor de datos .NET Framework para
SQL Server ( SqlClient ) con SQL Server, se producirá una excepción al intentar una inscripción. Todos los demás
casos no se detectarán.

Transacciones que se pueden promover en SQL Server


SQL Server 2005 admite transacciones que se pueden promover en las que se pueda promover automáticamente
una transacción ligera local a una transacción distribuida solamente cuando es necesario. Las transacciones
promovibles no invocan la sobrecarga adicional de las transacciones distribuidas a menos que sea necesario. Para
obtener más información y un ejemplo de código, vea integración de System. Transactions con SQL Server.

Configuración de transacciones distribuidas


Puede que necesite habilitar MS DTC a través de la red para usar transacciones distribuidas. Si tiene Firewall de
Windows habilitado, debe permitir que el servicio MS DTC use la red o abrir el puerto MS DTC.
Vea también
Transacciones y simultaneidad
Integración de System.Transactions con SQL Server
Información general sobre ADO.NET
Integración de System.Transactions con SQL Server
21/03/2020 • 14 minutes to read • Edit Online

La versión 2.0 de .NET Framework introdujo System.Transactions un marco de transacciones al que se puede
tener acceso a través del espacio de nombres. Este marco de trabajo expone las transacciones de una manera que
está totalmente integrada en .NET Framework, incluida ADO.NET.
Además de las mejoras System.Transactions de programación, y ADO.NET pueden trabajar juntas para coordinar
optimizaciones cuando se trabaja con transacciones. Una transacción promovible es una transacción ligera (local)
que, en caso necesario, se puede promover automáticamente a una transacción completamente distribuida.
A partir de ADO.NET System.Data.SqlClient 2.0, admite transacciones promocionables cuando se trabaja con SQL
Server. Las transacciones promovibles no invocan la sobrecarga adicional de las transacciones distribuidas a
menos que sea necesario. Las transacciones promocionables son automáticas y no requieren intervención del
desarrollador.
Las transacciones promocionables solo están disponibles cuando se SqlClient usa el proveedor de datos de .NET
Framework para SQL Server ( ) con SQL Server.

Creación de transacciones promocionadas


El proveedor de .NET Framework para SQL Server proporciona compatibilidad con transacciones
promocionables, que se controlan a través de las clases del espacio de nombres de .NET Framework.
System.Transactions Las transacciones promocionadas optimizan las transacciones distribuidas ya que aplazan la
creación de las mismas hasta que es necesario. Si solo se necesita un administrador de recursos, no tiene lugar
ninguna transacción distribuida.

NOTE
En un caso que no es de plena confianza, se requiere DistributedTransactionPermission cuando la transacción se promueve
al nivel de transacción distribuida.

Situaciones de uso de transacciones promocionadas


Normalmente, las transacciones distribuidas consumen muchos recursos del sistema, siendo el encargado de
administrarlas Microsoft DTC (Coordinador de transacciones distribuidas), que integra todos los administradores
de recursos a los que se tiene acceso en la transacción. Una transacción promocionable es System.Transactions
una forma especial de una transacción que delega eficazmente el trabajo en una transacción de SQL Server
simple. System.Transactions, System.Data.SqlClienty SQL Server coordinan el trabajo implicado en el control de la
transacción, promoviéndola a una transacción distribuida completa según sea necesario.
La ventaja de utilizar transacciones promocionadas es que cuando se abre una conexión utilizando una
transacción TransactionScope activa, y no hay ninguna otra conexión abierta, la transacción se confirma como
una transacción ligera, en lugar de incurrir en la sobrecarga adicional de una transacción completamente
distribuida.
Palabras clave de cadena de conexión
La propiedad ConnectionString admite una palabra clave, Enlist , que indica si System.Data.SqlClient detectará
contextos transaccionales e inscribirá automáticamente la conexión en una transacción distribuida. Si
Enlist=true , la conexión se inscribe automáticamente en el contexto de transacción actual del subproceso de
apertura. Si Enlist=false , la conexión SqlClient no interactúa con una transacción distribuida. El valor
predeterminado de Enlist es true. Si no se especifica Enlist en la cadena de conexión, la conexión se da de alta
automáticamente en una transacción distribuida si se detecta una al abrirse la conexión.
Las palabras clave Transaction Binding en una cadena de conexión SqlConnection controlan la asociación de la
conexión con una transacción System.Transactions dada de alta. También está disponible mediante la propiedad
TransactionBinding de SqlConnectionStringBuilder.
La siguiente tabla describe los posibles valores.

PA L A B RA C L AVE DESC RIP C IÓ N

Desenlace implícito El valor predeterminado. La conexión se separa de la


transacción cuando termina, y vuelve a cambiar al modo de
confirmación automática.

Desenlace explícito La conexión sigue adjuntada a la transacción hasta que ésta


se cierra. La conexión producirá errores si no está activa o no
coincide con Current.

Uso de TransactionScope
La clase TransactionScope crea un bloque de código transaccional dando de alta implícitamente las conexiones en
una transacción distribuida. Debe llamar al método Complete al final del bloque TransactionScope antes de
abandonarlo. Al salir del bloque se invoca el método Dispose . Si se ha producido una excepción que ocasiona
que el código salga del ámbito, la transacción se considera anulada.
Se recomienda el uso de un bloque using para asegurarse de que se llama a Dispose en el objeto
TransactionScope cuando se sale de dicho bloque. Si no se confirman ni revierten las transacciones pendientes, el
rendimiento puede verse seriamente afectado ya que el tiempo de espera predeterminado de TransactionScope
es un minuto. Si no utiliza una instrucción using , debe realizar todo el trabajo de un bloque Try y llamar
explícitamente al método Dispose del bloque Finally .
Si se produce una excepción en TransactionScope, la transacción se marca como incoherente y se abandona. Se
revertirá cuando se elimine el TransactionScope . Si no se produce ninguna excepción, las transacciones
participantes se confirman.

NOTE
La clase TransactionScope crea una transacción con IsolationLevel de Serializable de forma predeterminada.
Dependiendo de la aplicación, puede considerar la opción de reducir el nivel de aislamiento para evitar conflictos en ella.

NOTE
Se recomienda que solo realice actualizaciones, inserciones y eliminaciones en transacciones distribuidas, ya que consumen
una cantidad considerable de recursos de base de datos. Las instrucciones SELECT pueden bloquear los recursos de base de
datos de forma innecesaria y, en algunas situaciones, es posible que tengan que utilizarse transacciones para las selecciones.
Todo el trabajo que no sea de base de datos debe realizarse fuera del ámbito de la transacción, a menos que estén
implicados otros administradores de recursos de transacción. Aunque una excepción en el ámbito de la transacción impide
que se confirme la misma, la clase TransactionScope no deja revertir los cambios que haya realizado el código fuera del
ámbito de la propia transacción. Si es necesario realizar alguna acción cuando se revierta la transacción, deberá escribir su
propia implementación de la interfaz IEnlistmentNotification y darla de alta explícitamente en la transacción.
Ejemplo
Trabajar con System.Transactions requiere disponer de una referencia a System.Transactions.dll.
La siguiente función muestra cómo crear una transacción promocionada en dos instancias de SQL Server
diferentes, representadas por dos objetos SqlConnection diferentes, que se incluyen en un bloque
TransactionScope . El código crea el bloque TransactionScope con una instrucción using y abre la primera
conexión, que automáticamente se da de alta en TransactionScope. La transacción se da de alta inicialmente como
una transacción ligera, no como una transacción distribuida completa. La segunda conexión se inscribe en
TransactionScope únicamente si el comando de la primera conexión no produce una excepción. Cuando se abre la
segunda conexión, la transacción se promociona automáticamente a una transacción completamente distribuida.
Se invoca el método Complete , que confirma la transacción únicamente si no se han producido excepciones. Si
en algún punto del bloque TransactionScope se ha producido una excepción, no se llamará a Complete y, cuando
se elimine TransactionScope al final de su bloque using , se revertirá la transacción distribuida.

// This function takes arguments for the 2 connection strings and commands in order
// to create a transaction involving two SQL Servers. It returns a value > 0 if the
// transaction committed, 0 if the transaction rolled back. To test this code, you can
// connect to two different databases on the same server by altering the connection string,
// or to another RDBMS such as Oracle by altering the code in the connection2 code block.
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();

// Create the TransactionScope in which to execute the commands, guaranteeing


// that both commands will commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
try
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();

// Create the SqlCommand object and execute the first command.


SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);

// if you get here, this means that command1 succeeded. By nesting


// the using block for connection2 inside that of connection1, you
// conserve server and network resources by opening connection2
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
try
{
// The transaction is promoted to a full distributed
// transaction when connection2 is opened.
connection2.Open();

// Execute the second command in the second database.


returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
catch (Exception ex)
{
// Display information that command2 failed.
// Display information that command2 failed.
writer.WriteLine("returnValue for command2: {0}", returnValue);
writer.WriteLine("Exception Message2: {0}", ex.Message);
}
}
catch (Exception ex)
{
// Display information that command1 failed.
writer.WriteLine("returnValue for command1: {0}", returnValue);
writer.WriteLine("Exception Message1: {0}", ex.Message);
}
}

// If an exception has been thrown, Complete will not


// be called and the transaction is rolled back.
scope.Complete();
}

// The returnValue is greater than 0 if the transaction committed.


if (returnValue > 0)
{
writer.WriteLine("Transaction was committed.");
}
else
{
// You could write additional business logic here, notify the caller by
// throwing a TransactionAbortedException, or log the failure.
writer.WriteLine("Transaction rolled back.");
}

// Display messages.
Console.WriteLine(writer.ToString());

return returnValue;
}

' This function takes arguments for the 2 connection strings and commands in order
' to create a transaction involving two SQL Servers. It returns a value > 0 if the
' transaction committed, 0 if the transaction rolled back. To test this code, you can
' connect to two different databases on the same server by altering the connection string,
' or to another RDBMS such as Oracle by altering the code in the connection2 code block.
Public Function CreateTransactionScope( _
ByVal connectString1 As String, ByVal connectString2 As String, _
ByVal commandText1 As String, ByVal commandText2 As String) As Integer

' Initialize the return value to zero and create a StringWriter to display results.
Dim returnValue As Integer = 0
Dim writer As System.IO.StringWriter = New System.IO.StringWriter

' Create the TransactionScope in which to execute the commands, guaranteeing


' that both commands will commit or roll back as a single unit of work.
Using scope As New TransactionScope()
Using connection1 As New SqlConnection(connectString1)
Try
' Opening the connection automatically enlists it in the
' TransactionScope as a lightweight transaction.
connection1.Open()

' Create the SqlCommand object and execute the first command.
Dim command1 As SqlCommand = New SqlCommand(commandText1, connection1)
returnValue = command1.ExecuteNonQuery()
writer.WriteLine("Rows to be affected by command1: {0}", returnValue)

' If you get here, this means that command1 succeeded. By nesting
' the Using block for connection2 inside that of connection1, you
' conserve server and network resources by opening connection2
' only when there is a chance that the transaction can commit.
Using connection2 As New SqlConnection(connectString2)
Using connection2 As New SqlConnection(connectString2)
Try
' The transaction is promoted to a full distributed
' transaction when connection2 is opened.
connection2.Open()

' Execute the second command in the second database.


returnValue = 0
Dim command2 As SqlCommand = New SqlCommand(commandText2, connection2)
returnValue = command2.ExecuteNonQuery()
writer.WriteLine("Rows to be affected by command2: {0}", returnValue)

Catch ex As Exception
' Display information that command2 failed.
writer.WriteLine("returnValue for command2: {0}", returnValue)
writer.WriteLine("Exception Message2: {0}", ex.Message)
End Try
End Using

Catch ex As Exception
' Display information that command1 failed.
writer.WriteLine("returnValue for command1: {0}", returnValue)
writer.WriteLine("Exception Message1: {0}", ex.Message)
End Try
End Using

' If an exception has been thrown, Complete will


' not be called and the transaction is rolled back.
scope.Complete()
End Using

' The returnValue is greater than 0 if the transaction committed.


If returnValue > 0 Then
writer.WriteLine("Transaction was committed.")
Else
' You could write additional business logic here, notify the caller by
' throwing a TransactionAbortedException, or log the failure.
writer.WriteLine("Transaction rolled back.")
End If

' Display messages.


Console.WriteLine(writer.ToString())

Return returnValue
End Function

Consulte también
Transacciones y simultaneidad
Información general de ADO.NET
Simultaneidad optimista
21/03/2020 • 16 minutes to read • Edit Online

En un entorno multiusuario existen dos modelos para actualizar datos en una base de datos: simultaneidad
optimista y simultaneidad pesimista. El objeto DataSet está diseñado para fomentar el uso de la simultaneidad
optimista en actividades cuya ejecución tiene una larga duración, como cuando se trabaja con interacción remota y
cuando los usuarios interactúan con datos.
La simultaneidad pesimista implica bloquear filas en el origen de datos para impedir que otros usuarios
modifiquen los datos de tal forma que el usuario actual resulte afectado. En un modelo pesimista, cuando un
usuario realiza una acción que hace que se aplique un bloqueo, otros usuarios no pueden realizar acciones que
entrarían en conflicto con el bloqueo hasta que el propietario del bloqueo lo libere. Este modelo se utiliza
principalmente en aquellos entornos en los que hay mucha contención de datos, de manera que el costo de
proteger los datos con bloqueos es menor que el costo de revertir transacciones si se producen conflictos de
simultaneidad.
Por tanto, en un modelo de simultaneidad pesimista, un usuario que actualiza una fila establece un bloqueo. Hasta
que el usuario no haya terminado la actualización y liberado el bloqueo, nadie más podrá modificar dicha fila. Por
este motivo, la simultaneidad pesimista resulta más adecuada cuando los tiempos de bloqueo son cortos, como
ocurre en el procesamiento de registros mediante programación. La simultaneidad pesimista no es una opción
escalable cuando los usuarios interactúan con los datos y hacen que los registros queden bloqueados durante
períodos de tiempo relativamente largos.

NOTE
Si necesita actualizar varias filas en una misma operación, entonces crear una transacción es una opción más escalable que
utilizar el bloqueo pesimista.

Por el contrario, los usuarios que utilizan la simultaneidad optimista no bloquean una fila cuando la leen. Cuando
un usuario desea actualizar una fila, la aplicación debe determinar si otro usuario la ha modificado o no desde que
se leyó. La simultaneidad optimista suele utilizarse en entornos con poca contención de datos. Esto mejora el
rendimiento porque no es necesario bloquear registros, a la vez que el bloqueo de registros requiere recursos
adicionales del servidor. Además, para mantener bloqueos de registros es necesaria una conexión persistente con
el servidor de bases de datos. Como éste no es el caso en un modelo de simultaneidad optimista, las conexiones
con el servidor pueden atender a un mayor número de clientes en menos tiempo.
En un modelo de simultaneidad optimista, se considera que ha habido una infracción si, después de que un usuario
recibe un valor de la base de datos, otro usuario modifica el valor antes de que el primer usuario haya intentado
modificarlo. En el ejemplo siguiente se describe cómo el servidor resuelve una infracción de simultaneidad.
Las siguientes tablas muestran un ejemplo de simultaneidad optimista.
A la 1:00 p.m., el Usuario1 lee una fila de la base de datos con los valores siguientes:
IdCliente Apellido Nombre
101 Martínez Cris
VA LO R EN L A B A SE DE
N O M B RE DE L A C O L UM N A VA LO R O RIGIN A L VA LO R A C T UA L DATO S

IdCliente 101 101 101

Apellidos Smith Smith Smith

Nombre Bob Bob Bob

A la 1:01 p.m., el Usuario2 lee la misma fila.


A la 1:03 p.m., User2 cambia FirstName de "Bob" a "Robert" y actualiza la base de datos.

VA LO R EN L A B A SE DE
N O M B RE DE L A C O L UM N A VA LO R O RIGIN A L VA LO R A C T UA L DATO S

IdCliente 101 101 101

Apellidos Smith Smith Smith

Nombre Bob Cristina Bob

La actualización se realiza correctamente porque los valores contenidos en la base de datos en el momento de la
actualización coinciden con los valores originales que tiene el Usuario2.
A la 1:05 p.m., el Usuario1 cambia el nombre de "Cris" a "Jaime" e intenta actualizar la fila.

VA LO R EN L A B A SE DE
N O M B RE DE L A C O L UM N A VA LO R O RIGIN A L VA LO R A C T UA L DATO S

IdCliente 101 101 101

Apellidos Smith Smith Smith

Nombre Bob Jaime Cristina

En este momento, el Usuario1 encuentra una infracción de la simultaneidad optimista porque los valores de la base
de datos ("Jaime") ya no coinciden con los valores originales que esperaba el Usuario1 ("Cris"). La infracción de
simultaneidad simplemente permite saber que se ha producido un error de actualización. Ahora hay que tomar la
decisión de sobrescribir los cambios realizados por el Usuario2 con los efectuados por el Usuario1 o cancelar los
cambios del Usuario1.

Probar si hay infracciones de la simultaneidad optimista


Existen varias técnicas para probar si se ha producido una infracción de la simultaneidad optimista. Una de ellas
consiste en incluir una columna de marca de tiempo en la tabla. Las bases de datos suelen ofrecer funcionalidad de
marca de tiempo que puede utilizarse para identificar la fecha y la hora en que se actualizó el registro por última
vez. Mediante esta técnica se incluye una columna de marca de tiempo en la definición de la tabla. Siempre que se
actualiza el registro, se actualiza la marca de tiempo de manera que queden reflejadas la fecha y la hora actuales. Al
hacer una prueba para ver si hay infracciones de la simultaneidad optimista, la columna de marca de tiempo se
devuelve con cualquier consulta del contenido de la tabla. Cuando se intenta realizar una actualización, se compara
el valor de marca de tiempo de la base de datos con el valor de marca de tiempo original contenido en la fila
modificada. Si coinciden, se realiza la actualización y se actualiza la columna de marca de tiempo con la hora actual
con el fin de reflejar la actualización. Si no coinciden, se ha producido una infracción de la simultaneidad optimista.
Otra técnica para probar si hay alguna infracción relacionada con la simultaneidad optimista consiste en
comprobar que todos los valores de columna originales de una fila siguen coincidiendo con los existentes en la
base de datos. Por ejemplo, considere la siguiente consulta:

SELECT Col1, Col2, Col3 FROM Table1

Para comprobar si hay una infracción de simultaneidad optimista al actualizar una fila en Table1 , se emitirá la
siguiente instrucción UPDATE:

UPDATE Table1 Set Col1 = @NewCol1Value,


Set Col2 = @NewCol2Value,
Set Col3 = @NewCol3Value
WHERE Col1 = @OldCol1Value AND
Col2 = @OldCol2Value AND
Col3 = @OldCol3Value

La actualización se realizará siempre y cuando los valores originales coincidan con los valores de la base de datos.
Si se ha modificado algún valor, la actualización no modificará la fila porque la cláusula WHERE no encontrará
ninguna coincidencia.
Se recomienda devolver siempre un valor de clave principal único en la consulta. De lo contrario, la instrucción
UPDATE anterior puede actualizar más de una fila, lo que quizás no sea su intención.
Si una columna del origen de datos admite valores nulos, quizás sea necesario extender la cláusula WHERE para
comprobar si hay alguna referencia nula coincidente en la tabla local y en el origen de datos. Por ejemplo, la
siguiente instrucción UPDATE comprueba que una referencia nula de la fila local sigue coincidiendo con una
referencia nula del origen de datos o que el valor de la fila local sigue coincidiendo con el valor del origen de datos.

UPDATE Table1 Set Col1 = @NewVal1


WHERE (@OldVal1 IS NULL AND Col1 IS NULL) OR Col1 = @OldVal1

También se puede decidir la aplicación de criterios menos restrictivos al utilizar un modelo de simultaneidad
optimista. Por ejemplo, si solo se utilizan las columnas de clave principal en la cláusula WHERE se sobrescribirán
los datos, independientemente de que las otras columnas se hayan actualizado o no desde la última consulta.
También se puede aplicar una cláusula WHERE solo a determinadas columnas, lo que hará que se sobrescriban los
datos a menos que se hayan actualizado ciertos campos desde que se consultaron por última vez.
Evento DataAdapter.RowUpdated
El evento RowUpdated del DataAdapter objeto se puede usar junto con las técnicas descritas anteriormente para
proporcionar una notificación a la aplicación de infracciones de simultaneidad optimista. RowUpdated se produce
después de cada intento de actualizar una fila Modified desde un DataSet . Esto permite agregar código especial
de control, incluyendo el procesamiento cuando se produce una excepción, agregar información de error
personalizada, agregar lógica de reintento, etc. El RowUpdatedEventArgs objeto devuelve una propiedad
RecordsAffected que contiene el número de filas afectadas por un comando de actualización determinado para
una fila modificada de una tabla. Al establecer el comando update para probar la simultaneidad optimista, la
propiedad RecordsAffected, como resultado, devolverá un valor de 0 cuando se haya producido una infracción
de simultaneidad optimista, porque no se actualizó ningún registro. En tal caso se inicia una excepción. El evento
RowUpdated permite controlar esta aparición y evitar la excepción estableciendo un valor
RowUpdatedEventArgs.Status adecuado, como UpdateStatus.SkipCurrentRow . Para obtener más
información sobre el evento RowUpdated, vea Controlar eventos DataAdapter.
Opcionalmente, puede establecer DataAdapter.ContinueUpdateOnError en true , antes de llamar a Update y
responder a la información de error almacenada en la propiedad RowError de una fila determinada cuando se
completa la actualización. Para obtener más información, consulte Información de error de fila.
Ejemplo de simultaneidad optimista
A continuación se muestra un ejemplo sencillo que establece el UpdateCommand de un DataAdapter para
probar la simultaneidad optimista y, a continuación, usa el RowUpdated eventos para probar las infracciones de
simultaneidad optimista. Cuando se encuentra una infracción de simultaneidad optimista, la aplicación establece el
RowError de la fila para la que se emitió la actualización para reflejar una infracción de simultaneidad optimista.
Tenga en cuenta que los valores de parámetro pasados a la cláusula WHERE del comando UPDATE se asignan a los
valores Original de sus respectivas columnas.

' Assumes connection is a valid SqlConnection.


Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", _
connection)

' The Update command checks for optimistic concurrency violations


' in the WHERE clause.
adapter.UpdateCommand = New SqlCommand("UPDATE Customers " &
"(CustomerID, CompanyName) VALUES(@CustomerID, @CompanyName) " & _
"WHERE CustomerID = @oldCustomerID AND CompanyName = " &
"@oldCompanyName", connection)
adapter.UpdateCommand.Parameters.Add( _
"@CustomerID", SqlDbType.NChar, 5, "CustomerID")
adapter.UpdateCommand.Parameters.Add( _
"@CompanyName", SqlDbType.NVarChar, 30, "CompanyName")

' Pass the original values to the WHERE clause parameters.


Dim parameter As SqlParameter = adapter.UpdateCommand.Parameters.Add( _
"@oldCustomerID", SqlDbType.NChar, 5, "CustomerID")
parameter.SourceVersion = DataRowVersion.Original
parameter = adapter.UpdateCommand.Parameters.Add( _
"@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName")
parameter.SourceVersion = DataRowVersion.Original

' Add the RowUpdated event handler.


AddHandler adapter.RowUpdated, New SqlRowUpdatedEventHandler( _
AddressOf OnRowUpdated)

Dim dataSet As DataSet = New DataSet()


adapter.Fill(dataSet, "Customers")

' Modify the DataSet contents.


adapter.Update(dataSet, "Customers")

Dim dataRow As DataRow

For Each dataRow In dataSet.Tables("Customers").Rows


If dataRow.HasErrors Then
Console.WriteLine(dataRow (0) & vbCrLf & dataRow.RowError)
End If
Next

Private Shared Sub OnRowUpdated( _


sender As object, args As SqlRowUpdatedEventArgs)
If args.RecordsAffected = 0
args.Row.RowError = "Optimistic Concurrency Violation!"
args.Status = UpdateStatus.SkipCurrentRow
End If
End Sub
// Assumes connection is a valid SqlConnection.
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID",
connection);

// The Update command checks for optimistic concurrency violations


// in the WHERE clause.
adapter.UpdateCommand = new SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, CompanyName =
@CompanyName " +
"WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", connection);
adapter.UpdateCommand.Parameters.Add(
"@CustomerID", SqlDbType.NChar, 5, "CustomerID");
adapter.UpdateCommand.Parameters.Add(
"@CompanyName", SqlDbType.NVarChar, 30, "CompanyName");

// Pass the original values to the WHERE clause parameters.


SqlParameter parameter = adapter.UpdateCommand.Parameters.Add(
"@oldCustomerID", SqlDbType.NChar, 5, "CustomerID");
parameter.SourceVersion = DataRowVersion.Original;
parameter = adapter.UpdateCommand.Parameters.Add(
"@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName");
parameter.SourceVersion = DataRowVersion.Original;

// Add the RowUpdated event handler.


adapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);

DataSet dataSet = new DataSet();


adapter.Fill(dataSet, "Customers");

// Modify the DataSet contents.

adapter.Update(dataSet, "Customers");

foreach (DataRow dataRow in dataSet.Tables["Customers"].Rows)


{
if (dataRow.HasErrors)
Console.WriteLine(dataRow [0] + "\n" + dataRow.RowError);
}

protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args)


{
if (args.RecordsAffected == 0)
{
args.Row.RowError = "Optimistic Concurrency Violation Encountered";
args.Status = UpdateStatus.SkipCurrentRow;
}
}

Consulte también
Recuperar y modificar datos en ADO.NET
Actualizar orígenes de datos con objetos DataAdapter
Información de error de fila
Transacciones y simultaneidad
Información general de ADO.NET
Recuperar valores autonuméricos y de identidad
23/10/2019 • 33 minutes to read • Edit Online

Una clave principal de una base de datos relacional es una columna o combinación de columnas que siempre
contienen valores únicos. Conocer el valor de la clave principal permite localizar la fila que la contiene. Los
motores de bases de datos relacionales, como SQL Server, Oracle y Microsoft Access/Jet admiten la creación de
columnas de incremento automático que pueden designarse como claves principales. Estos valores los genera el
servidor cuando se agregan filas a una tabla. En SQL Server se establece la propiedad de identidad de una
columna, en Oracle se crea una secuencia y en Microsoft Access se crea una columna Autonumérica.
DataColumn también se puede utilizar para generar de manera automática valores incrementales estableciendo
la propiedad AutoIncrement en true. No obstante, podría haber valores duplicados en instancias distintas de
DataTable si varias aplicaciones cliente están generando por separado valores incrementales de manera
automática. Si se tiene un servidor que genera de manera automática valores incrementales se eliminan posibles
conflictos, pues se permite a cada usuario recuperar el valor generado para cada fila insertada.
Durante una llamada al método Update de DataAdapter , la base de datos puede volver a enviar datos a la
aplicación ADO.NET como parámetros de salida o como el primer registro devuelto del conjunto de resultados de
una instrucción SELECT ejecutada en el mismo lote que la instrucción INSERT. ADO.NET puede recuperar estos
valores y actualizar las columnas correspondientes en el DataRow que se está actualizando.
Algunos motores de base de datos, como los de Microsoft Access Jet, no admiten parámetros de salida y no
pueden procesar varias instrucciones en un único lote. Cuando trabaje con el motor de base de datos de Jet,
puede recuperar el nuevo valor Autonumérico generado para una fila insertada ejecutando un comando SELECT
distinto en un controlador de eventos para el evento RowUpdated de DataAdapter .

NOTE
Una opción alternativa al uso de un valor de incremento automático es utilizar el método NewGuid de un objeto Guid para
generar un GUID (identificador único global) en el equipo cliente que se pueda copiar al servidor cuando se inserte una
nueva fila. El método NewGuid genera un valor binario de 16 bits que se crea mediante un algoritmo que permite que
haya una alta probabilidad de que no se duplique ningún valor. En una base de datos de SQL Server, el GUID se almacena
en una columna uniqueidentifier que SQL Server puede generar automáticamente mediante la función Transact-SQL
NEWID() . Utilizar un GUID como clave principal puede afectar de manera negativa al rendimiento. SQL Server proporciona
compatibilidad con la NEWSEQUENTIALID() función, que genera un GUID secuencial que no se garantiza que sea
globalmente único, pero que se puede indizar de forma más eficaz.

Recuperar valores de columnas de identidad de SQL Server


Cuando trabaje con Microsoft SQL Server, puede crear procedimientos almacenados con un parámetro de salida
para devolver el valor de identidad de una fila insertada. La siguiente tabla describe las tres funciones de
Transact-SQL en SQL Server que se pueden utilizar para recuperar valores de columna de identidad.

F UN C IÓ N DESC RIP C IÓ N

SCOPE_IDENTITY Devuelve el último valor de identidad en el ámbito de


ejecución actual. SCOPE_IDENTITY se recomienda en la
mayoría de los casos.
F UN C IÓ N DESC RIP C IÓ N

@@IDENTITY Contiene el último valor de identidad generado en cualquier


tabla de la sesión actual. @@IDENTITY puede verse afectado
por los desencadenadores y es posible que no devuelva el
valor de identidad que espera.

IDENT_CURRENT Devuelve el último valor de identidad generado para una


tabla concreta de cualquier sesión y en cualquier ámbito.

El siguiente procedimiento almacenado muestra cómo insertar una fila en la tabla Categories y cómo utilizar un
parámetro de salida para devolver el nuevo valor de identidad generado por la función TRANSACT-SQL
SCOPE_IDENTITY ().

CREATE PROCEDURE dbo.InsertCategory


@CategoryName nvarchar(15),
@Identity int OUT
AS
INSERT INTO Categories (CategoryName) VALUES(@CategoryName)
SET @Identity = SCOPE_IDENTITY()

El procedimiento almacenado se puede especificar como el origen de InsertCommand de un objeto


SqlDataAdapter. La propiedad CommandType de InsertCommand debe establecerse en StoredProcedure. La
salida de identidad se recupera creando un SqlParameter que tiene un ParameterDirection de Output.
UpdateRowSource.Both UpdatedRowSource UpdateRowSource.OutputParameters Cuando se procesa, se devuelve el
valor de identidad de incremento automático y se coloca en la columna CategoryID de la fila actual si se establece
la propiedad del comando INSERT en o en. InsertCommand
Si el comando de inserción ejecuta un lote que incluye tanto una instrucción INSERT como una instrucción
SELECT que devuelven el nuevo valor de identidad, entonces puede recuperar el nuevo valor estableciendo la
propiedad UpdatedRowSource del comando de inserción en UpdateRowSource.FirstReturnedRecord .
private static void RetrieveIdentity(string connectionString)
{
using (SqlConnection connection =
new SqlConnection(connectionString))
{
// Create a SqlDataAdapter based on a SELECT query.
SqlDataAdapter adapter =
new SqlDataAdapter(
"SELECT CategoryID, CategoryName FROM dbo.Categories",
connection);

//Create the SqlCommand to execute the stored procedure.


adapter.InsertCommand = new SqlCommand("dbo.InsertCategory",
connection);
adapter.InsertCommand.CommandType = CommandType.StoredProcedure;

// Add the parameter for the CategoryName. Specifying the


// ParameterDirection for an input parameter is not required.
adapter.InsertCommand.Parameters.Add(
new SqlParameter("@CategoryName", SqlDbType.NVarChar, 15,
"CategoryName"));

// Add the SqlParameter to retrieve the new identity value.


// Specify the ParameterDirection as Output.
SqlParameter parameter =
adapter.InsertCommand.Parameters.Add(
"@Identity", SqlDbType.Int, 0, "CategoryID");
parameter.Direction = ParameterDirection.Output;

// Create a DataTable and fill it.


DataTable categories = new DataTable();
adapter.Fill(categories);

// Add a new row.


DataRow newRow = categories.NewRow();
newRow["CategoryName"] = "New Category";
categories.Rows.Add(newRow);

adapter.Update(categories);

Console.WriteLine("List All Rows:");


foreach (DataRow row in categories.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
Private Sub RetrieveIdentity(ByVal connectionString As String)
Using connection As SqlConnection = New SqlConnection( _
connectionString)

' Create a SqlDataAdapter based on a SELECT query.


Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CategoryID, CategoryName FROM dbo.Categories", _
connection)

' Create the SqlCommand to execute the stored procedure.


adapter.InsertCommand = New SqlCommand("dbo.InsertCategory", _
connection)
adapter.InsertCommand.CommandType = CommandType.StoredProcedure

' Add the parameter for the CategoryName. Specifying the


' ParameterDirection for an input parameter is not required.
adapter.InsertCommand.Parameters.Add( _
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

' Add the SqlParameter to retrieve the new identity value.


' Specify the ParameterDirection as Output.
Dim parameter As SqlParameter = _
adapter.InsertCommand.Parameters.Add( _
"@Identity", SqlDbType.Int, 0, "CategoryID")
parameter.Direction = ParameterDirection.Output

' Create a DataTable and fill it.


Dim categories As DataTable = New DataTable
adapter.Fill(categories)

' Add a new row.


Dim newRow As DataRow = categories.NewRow()
newRow("CategoryName") = "New Category"
categories.Rows.Add(newRow)

' Update the database.


adapter.Update(categories)

Console.WriteLine("List All Rows:")


Dim row As DataRow
For Each row In categories.Rows
Console.WriteLine("{0}: {1}", row(0), row(1))
Next
End Using
End Sub

Combinar nuevos valores de identidad


Un caso frecuente es llamar al método GetChanges de DataTable para crear una copia que contiene únicamente
filas modificadas y utilizar la nueva copia al llamar al método Update de DataAdapter . Esto es especialmente útil
cuando hay que serializar las filas modificadas en un componente independiente que realiza la actualización.
Después de la actualización, la copia puede contener nuevos valores de identidad que se deben volver a combinar
en el DataTable original. Probablemente los nuevos valores de identidad son diferentes a los valores originales
de DataTable . Para realizar la combinación, deben conservarse los valores originales de las columnas de
incremento automático de la copia, con el fin de poder localizar y actualizar las filas existentes en el DataTable
original, en lugar de anexar nuevas filas que contengan los nuevos valores de identidad. . No obstante, de manera
predeterminada estos valores se pierden después de una llamada al método Update de DataAdapter , debido a
que se llama implícitamente a AcceptChanges en cada DataRow actualizada.
Hay dos maneras de mantener los valores originales de DataColumn en DataRow durante una actualización de
DataAdapter :
El primer método para mantener los valores originales consiste en establecer la propiedad
AcceptChangesDuringUpdate de DataAdapter en false . Esto afecta a cada DataRow de DataTable que se
está actualizando. Para más información y ver un código de ejemplo, vea AcceptChangesDuringUpdate.
El segundo método consiste en escribir código en el controlador de eventos RowUpdated de DataAdapter
para establecer Status en SkipCurrentRow. DataRow se actualiza pero se mantiene el valor original de cada
DataColumn . Este método permite mantener los valores originales en algunas filas y no en otras. Por
ejemplo, el código puede mantener los valores originales de filas agregadas y no los de filas editadas o
eliminadas comprobando primero StatementType y, a continuación, estableciendo Status en
SkipCurrentRow únicamente para filas con un StatementType de Insert .

Cuando se utiliza alguno de estos métodos para mantener los valores originales de DataRow durante una
actualización de DataAdapter , ADO.NET realiza una serie de acciones para establecer los valores actuales de
DataRow a los nuevos valores devueltos por parámetros de salida o por la primera fila devuelta de un conjunto
de resultados, al tiempo que se mantiene el valor original de cada DataColumn . Primero, se llama al método
AcceptChanges de DataRow para mantener los valores actuales como valores originales y, a continuación, se
asignan los nuevos valores. Después de estas acciones, las DataRows que tienen la propiedad RowState
establecida en Added tendrán su propiedad RowState establecida en Modified, lo que puede ser inesperado.
El modo en que se aplican los resultados del comando a cada DataRow que se actualiza lo determina la propiedad
UpdatedRowSource de cada DbCommand. Esta propiedad se establece en un valor desde la enumeración
UpdateRowSource .

La siguiente tabla describe cómo afectan los valores de enumeración UpdateRowSource a la propiedad RowState
de las filas actualizadas.

N O M B RE DE M IEM B RO DESC RIP C IÓ N

Both Se llama a AcceptChanges y tanto los parámetros de salida


como los valores de la primera fila de cualquier conjunto de
resultados devuelto se colocan en la DataRow que se está
actualizando. Si no hay valores que aplicar, RowState será
Unchanged.

FirstReturnedRecord Si se devuelve una fila, se llama a AcceptChanges y la fila se


asigna a la fila modificada en DataTable , estableciendo
RowState en Modified . Si no se devuelve ninguna fila,
entonces no se llama a AcceptChanges y RowState
permanece en Added .

None Se pasan por alto todos los parámetros o filas devueltos. No


hay llamada a AcceptChanges y RowState permanece en
Added .

OutputParameters Se llama a AcceptChanges y todos los parámetros de salida


se asignan a la fila modificada en DataTable , estableciendo
RowState en Modified . Si no hay parámetros de salida,
RowState será Unchanged .

Ejemplo
Este ejemplo muestra la extracción de filas modificadas desde DataTable y el uso de SqlDataAdapter para
actualizar el origen de datos y recuperar un nuevo valor de columna de identidad. InsertCommand ejecuta dos
instrucciones Transact-SQL; la primera es la instrucción INSERT y la segunda es la instrucción SELECT.
INSERT INTO dbo.Shippers (CompanyName)
VALUES (@CompanyName);
SELECT ShipperID, CompanyName FROM dbo.Shippers
WHERE ShipperID = SCOPE_IDENTITY();

La propiedad UpdatedRowSource del comando de inserción se establece en UpdateRowSource.FirstReturnedRow y la


propiedad MissingSchemaAction de DataAdapter se establece en MissingSchemaAction.AddWithKey . DataTable se
rellena y el código agrega una nueva fila a DataTable . A continuación, las filas modificadas se extraen en un
nuevo DataTable , que se pasa a DataAdapter , el cual actualiza el servidor.
private static void MergeIdentityColumns(string connectionString)
{
using (SqlConnection connection =
new SqlConnection(connectionString))
{
// Create the DataAdapter
SqlDataAdapter adapter =
new SqlDataAdapter(
"SELECT ShipperID, CompanyName FROM dbo.Shippers",
connection);

//Add the InsertCommand to retrieve new identity value.


adapter.InsertCommand = new SqlCommand(
"INSERT INTO dbo.Shippers (CompanyName) " +
"VALUES (@CompanyName); " +
"SELECT ShipperID, CompanyName FROM dbo.Shippers " +
"WHERE ShipperID = SCOPE_IDENTITY();", connection);

// Add the parameter for the inserted value.


adapter.InsertCommand.Parameters.Add(
new SqlParameter("@CompanyName", SqlDbType.NVarChar, 40,
"CompanyName"));
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

// MissingSchemaAction adds any missing schema to


// the DataTable, including identity columns
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

// Fill the DataTable.


DataTable shipper = new DataTable();
adapter.Fill(shipper);

// Add a new shipper.


DataRow newRow = shipper.NewRow();
newRow["CompanyName"] = "New Shipper";
shipper.Rows.Add(newRow);

// Add changed rows to a new DataTable. This


// DataTable will be used by the DataAdapter.
DataTable dataChanges = shipper.GetChanges();

// Add the event handler.


adapter.RowUpdated +=
new SqlRowUpdatedEventHandler(OnRowUpdated);

adapter.Update(dataChanges);
connection.Close();

// Merge the updates.


shipper.Merge(dataChanges);

// Commit the changes.


shipper.AcceptChanges();

Console.WriteLine("Rows after merge.");


foreach (DataRow row in shipper.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
Private Sub MergeIdentityColumns(ByVal connectionString As String)

Using connection As SqlConnection = New SqlConnection( _


connectionString)

' Create the DataAdapter


Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT ShipperID, CompanyName FROM dbo.Shippers", connection)

' Add the InsertCommand to retrieve new identity value.


adapter.InsertCommand = New SqlCommand( _
"INSERT INTO dbo.Shippers (CompanyName) " & _
"VALUES (@CompanyName); " & _
"SELECT ShipperID, CompanyName FROM dbo.Shippers " & _
"WHERE ShipperID = SCOPE_IDENTITY();", _
connection)

' Add the parameter for the inserted value.


adapter.InsertCommand.Parameters.Add( _
New SqlParameter("@CompanyName", SqlDbType.NVarChar, 40, _
"CompanyName"))
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both

' MissingSchemaAction adds any missing schema to


' the DataTable, including identity columns
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey

' Fill the DataTable.


Dim shipper As New DataTable
adapter.Fill(shipper)

' Add a new shipper.


Dim newRow As DataRow = shipper.NewRow()
newRow("CompanyName") = "New Shipper"
shipper.Rows.Add(newRow)

' Add changed rows to a new DataTable. This


' DataTable will be used by the DataAdapter.
Dim dataChanges As DataTable = shipper.GetChanges()

' Add the event handler.


AddHandler adapter.RowUpdated, New _
SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)

' Update the datasource with the modified records.


adapter.Update(dataChanges)

' Merge the two DataTables.


shipper.Merge(dataChanges)

' Commit the changes.


shipper.AcceptChanges()

Console.WriteLine("Rows after merge.")


Dim row As DataRow
For Each row In shipper.Rows
Console.WriteLine("{0}: {1}", row(0), row(1))
Next
End Using
End Sub

El controlador de eventos OnRowUpdated comprueba StatementType de SqlRowUpdatedEventArgs para


determinar si la fila es una inserción. Si lo es, entonces la propiedad se establece Status en SkipCurrentRow. La fila
está actualizada, pero los valores originales de la fila se mantienen. En el cuerpo principal del procedimiento, se
llama al método Merge para fusión mediante combinación el nuevo valor de identidad en el DataTable original y,
finalmente, se llama a AcceptChanges .

protected static void OnRowUpdated(


object sender, SqlRowUpdatedEventArgs e)
{
// If this is an insert, then skip this row.
if (e.StatementType == StatementType.Insert)
{
e.Status = UpdateStatus.SkipCurrentRow;
}
}

Private Sub OnRowUpdated( _


ByVal sender As Object, ByVal e As SqlRowUpdatedEventArgs)
' If this is an insert, then skip this row.
If e.StatementType = StatementType.Insert Then
e.Status = UpdateStatus.SkipCurrentRow
End If
End Sub

Recuperar valores de autonumeración de Microsoft Access


Esta sección incluye un ejemplo que muestra cómo recuperar valores de Autonumber desde una base de datos de
Jet 4.0. El motor de la base de datos de Jet no admite la ejecución de varias instrucciones en un lote o el uso de
parámetros de salida, por lo que no es posible utilizar ninguna de estas técnicas para devolver el nuevo valor
Autonumber asignado a una fila insertada. Sin embargo, puede agregar código al RowUpdated controlador de
eventos que ejecuta una instrucción SELECT @@IDENTITY independiente para recuperar el nuevo Autonumber
valor.
Ejemplo
En lugar de agregar información de esquema utilizando MissingSchemaAction.AddWithKey , este ejemplo configura
DataTable con el esquema adecuado antes de llamar a OleDbDataAdapter para rellenar DataTable . En este caso,
la columna Categor yID se configura para disminuir el valor asignado a cada fila insertada a partir de cero
AutoIncrement , true estableciendo AutoIncrementSeed en, en 0 AutoIncrementStep y en-1. Entonces, el código
agrega dos filas nuevas y utiliza GetChanges para agregar las filas modificadas a un nuevo DataTable que se
pasa al método Update .

private static OleDbConnection connection = null;

private static void MergeIdentityColumns(OleDbConnection connection)


{
using (connection)
{
// Create a DataAdapter based on a SELECT query.
OleDbDataAdapter adapter = new OleDbDataAdapter(
"SELECT CategoryID, CategoryName FROM Categories",
connection);

// Create the INSERT command for the new category.


adapter.InsertCommand = new OleDbCommand(
"INSERT INTO Categories (CategoryName) Values(?)", connection);
adapter.InsertCommand.CommandType = CommandType.Text;

// Add the parameter for the CategoryName.


adapter.InsertCommand.Parameters.Add(
"@CategoryName", OleDbType.VarWChar, 15, "CategoryName");
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

// Create a DataTable
DataTable categories = new DataTable();

// Create the CategoryID column and set its auto


// incrementing properties to decrement from zero.
DataColumn column = new DataColumn();
column.DataType = System.Type.GetType("System.Int32");
column.ColumnName = "CategoryID";
column.AutoIncrement = true;
column.AutoIncrementSeed = 0;
column.AutoIncrementStep = -1;
categories.Columns.Add(column);

// Create the CategoryName column.


column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "CategoryName";
categories.Columns.Add(column);

// Set the primary key on CategoryID.


DataColumn[] pKey = new DataColumn[1];
pKey[0] = categories.Columns["CategoryID"];
categories.PrimaryKey = pKey;

// Fetch the data and fill the DataTable


adapter.Fill(categories);

// Add a new row.


DataRow newRow = categories.NewRow();
newRow["CategoryName"] = "New Category";
categories.Rows.Add(newRow);

// Add another new row.


DataRow newRow2 = categories.NewRow();
newRow2["CategoryName"] = "Another New Category";
categories.Rows.Add(newRow2);

// Add changed rows to a new DataTable that will be


// used to post the inserts to the database.
DataTable dataChanges = categories.GetChanges();

// Include an event to fill in the Autonumber value.


adapter.RowUpdated +=
new OleDbRowUpdatedEventHandler(OnRowUpdated);

// Update the database, inserting the new rows.


adapter.Update(dataChanges);

Console.WriteLine("Rows before merge:");


foreach (DataRow row in categories.Rows)
{
{
Console.WriteLine(" {0}: {1}", row[0], row[1]);
}
}

// Merge the two DataTables.


categories.Merge(dataChanges);

// Commit the changes.


categories.AcceptChanges();

Console.WriteLine("Rows after merge:");


foreach (DataRow row in categories.Rows)
{
{
Console.WriteLine(" {0}: {1}", row[0], row[1]);
}
}
}
}
}

Shared connection As OleDbConnection = Nothing

Private Shared Sub MergeIdentityColumns(ByVal connection As OleDbConnection)


Using connection

' Create a DataAdapter based on a SELECT query.


Dim adapter As OleDbDataAdapter = New OleDbDataAdapter( _
"SELECT CategoryID, CategoryName FROM Categories", _
connection)

' Create the INSERT command for the new category.


adapter.InsertCommand = New OleDbCommand( _
"INSERT INTO Categories (CategoryName) Values(?)", connection)
adapter.InsertCommand.CommandType = CommandType.Text

' Add the parameter for the CategoryName.


adapter.InsertCommand.Parameters.Add( _
"@CategoryName", OleDbType.VarWChar, 15, "CategoryName")
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both

' Create a DataTable.


Dim categories As DataTable = New DataTable

' Create the CategoryID column and set its auto


' incrementing properties to decrement from zero.
Dim column As New DataColumn()
column.DataType = System.Type.GetType("System.Int32")
column.ColumnName = "CategoryID"
column.AutoIncrement = True
column.AutoIncrementSeed = 0
column.AutoIncrementStep = -1
categories.Columns.Add(column)

' Create the CategoryName column.


column = New DataColumn()
column.DataType = System.Type.GetType("System.String")
column.ColumnName = "CategoryName"
categories.Columns.Add(column)

' Set the primary key on CategoryID.


Dim pKey(0) As DataColumn
pKey(0) = categories.Columns("CategoryID")
categories.PrimaryKey = pKey

' Fetch the data and fill the DataTable.


adapter.Fill(categories)

' Add a new row.


Dim newRow As DataRow = categories.NewRow()
newRow("CategoryName") = "New Category"
categories.Rows.Add(newRow)

' Add another new row.


Dim newRow2 As DataRow = categories.NewRow()
newRow2("CategoryName") = "Another New Category"
categories.Rows.Add(newRow2)

' Add changed rows to a new DataTable that will be


' used to post the inserts to the database.
Dim dataChanges As DataTable = categories.GetChanges()

' Include an event to fill in the Autonumber value.


AddHandler adapter.RowUpdated, _
New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
' Update the database, inserting the new rows.
adapter.Update(dataChanges)

Console.WriteLine("Rows before merge:")


Dim row1 As DataRow
For Each row1 In categories.Rows
Console.WriteLine(" {0}: {1}", row1(0), row1(1))
Next

' Merge the two DataTables.


categories.Merge(dataChanges)

' Commit the changes.


categories.AcceptChanges()

Console.WriteLine("Rows after merge:")


Dim row As DataRow
For Each row In categories.Rows
Console.WriteLine(" {0}: {1}", row(0), row(1))
Next
End Using
End Sub

El controlador de eventos RowUpdated utiliza el mismo OleDbConnection abierto que la instrucción Update de
OleDbDataAdapter . Comprueba el StatementType de OleDbRowUpdatedEventArgs de las filas insertadas. Para
cada fila insertada, OleDbCommand se crea un nuevo para ejecutar la@IDENTITY instrucción SELECT @ en la
conexión, devolviendo el nuevo Autonumber valor, que DataRow se coloca en la columna Categor yID de. La
propiedad Status se establece luego en UpdateStatus.SkipCurrentRow para suprimir la llamada oculta a
AcceptChanges . En el cuerpo principal del procedimiento, se llama al método Merge para fusionar mediante
combinación los dos objetos DataTable y, finalmente, se llama a AcceptChanges .

private static void OnRowUpdated(


object sender, OleDbRowUpdatedEventArgs e)
{
// Conditionally execute this code block on inserts only.
if (e.StatementType == StatementType.Insert)
{
OleDbCommand cmdNewID = new OleDbCommand("SELECT @@IDENTITY",
connection);
// Retrieve the Autonumber and store it in the CategoryID column.
e.Row["CategoryID"] = (int)cmdNewID.ExecuteScalar();
e.Status = UpdateStatus.SkipCurrentRow;
}
}

Private Shared Sub OnRowUpdated( _


ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs)
' Conditionally execute this code block on inserts only.
If e.StatementType = StatementType.Insert Then
' Retrieve the Autonumber and store it in the CategoryID column.
Dim cmdNewID As New OleDbCommand("SELECT @@IDENTITY", _
connection)
e.Row("CategoryID") = CInt(cmdNewID.ExecuteScalar)
e.Status = UpdateStatus.SkipCurrentRow
End If
End Sub

Recuperar valores de identidad


A menudo se establece la columna como identidad cuando los valores de la columna deben ser únicos. A veces se
necesita el valor de identidad de los nuevos datos. En este ejemplo se muestra cómo recuperar los valores de
identidad:
Crea un procedimiento almacenado para insertar los datos y devolver un valor de identidad.
Ejecuta un comando para insertar los nuevos datos y mostrar el resultado.
Usa SqlDataAdapter para insertar nuevos datos y mostrar el resultado.
Antes de compilar y ejecutar el ejemplo, debe crear la base de datos de ejemplo mediante el script siguiente:

USE [master]
GO

CREATE DATABASE [MySchool]


GO

USE [MySchool]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[CourseExtInfo] @CourseId int
as
select c.CourseID,c.Title,c.Credits,d.Name as DepartmentName
from Course as c left outer join Department as d on c.DepartmentID=d.DepartmentID
where c.CourseID=@CourseId

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create procedure [dbo].[DepartmentInfo] @DepartmentId int,@CourseCount int output
as
select @CourseCount=Count(c.CourseID)
from course as c
where c.DepartmentID=@DepartmentId

select d.DepartmentID,d.Name,d.Budget,d.StartDate,d.Administrator
from Department as d
where d.DepartmentID=@DepartmentId

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create PROCEDURE [dbo].[GetDepartmentsOfSpecifiedYear]
@Year int,@BudgetSum money output
AS
BEGIN
SELECT @BudgetSum=SUM([Budget])
FROM [MySchool].[dbo].[Department]
Where YEAR([StartDate])=@Year

SELECT [DepartmentID]
,[Name]
,[Budget]
,[StartDate]
,[Administrator]
FROM [MySchool].[dbo].[Department]
Where YEAR([StartDate])=@Year

END
END
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[GradeOfStudent]
-- Add the parameters for the stored procedure here
@CourseTitle nvarchar(100),@FirstName nvarchar(50),
@LastName nvarchar(50),@Grade decimal(3,2) output
AS
BEGIN
select @Grade=Max(Grade)
from [dbo].[StudentGrade] as s join [dbo].[Course] as c on
s.CourseID=c.CourseID join [dbo].[Person] as p on s.StudentID=p.PersonID
where c.Title=@CourseTitle and p.FirstName=@FirstName
and p.LastName= @LastName
END
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[InsertPerson]
-- Add the parameters for the stored procedure here
@FirstName nvarchar(50),@LastName nvarchar(50),
@PersonID int output
AS
BEGIN
insert [dbo].[Person](LastName,FirstName) Values(@LastName,@FirstName)

set @PersonID=SCOPE_IDENTITY()
END
Go

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Course]([CourseID] [nvarchar](10) NOT NULL,
[Year] [smallint] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[Credits] [int] NOT NULL,
[DepartmentID] [int] NOT NULL,
CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED
(
[CourseID] ASC,
[Year] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Department]([DepartmentID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Budget] [money] NOT NULL,
[StartDate] [datetime] NOT NULL,
[Administrator] [int] NULL,
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DepartmentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Person]([PersonID] [int] IDENTITY(1,1) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[HireDate] [datetime] NULL,
[EnrollmentDate] [datetime] NULL,
[Picture] [varbinary](max) NULL,
CONSTRAINT [PK_School.Student] PRIMARY KEY CLUSTERED
(
[PersonID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[StudentGrade]([EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
[CourseID] [nvarchar](10) NOT NULL,
[StudentID] [int] NOT NULL,
[Grade] [decimal](3, 2) NOT NULL,
CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED
(
[EnrollmentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create view [dbo].[EnglishCourse]
as
select c.CourseID,c.Title,c.Credits,c.DepartmentID
from Course as c join Department as d on c.DepartmentID=d.DepartmentID
where d.Name=N'English'

GO
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C1045', 2012,
N'Calculus', 4, 7)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C1061', 2012,
N'Physics', 4, 1)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C2021', 2012,
N'Composition', 3, 2)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C2042', 2012,
N'Literature', 4, 2)
SET IDENTITY_INSERT [dbo].[Department] ON

INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (1,


N'Engineering', 350000.0000, CAST(0x0000999C00000000 AS DateTime), 2)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (2,
N'English', 120000.0000, CAST(0x0000999C00000000 AS DateTime), 6)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (4,
N'Economics', 200000.0000, CAST(0x0000999C00000000 AS DateTime), 4)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (7,
N'Mathematics', 250024.0000, CAST(0x0000999C00000000 AS DateTime), 3)
SET IDENTITY_INSERT [dbo].[Department] OFF
SET IDENTITY_INSERT [dbo].[Person] ON
SET IDENTITY_INSERT [dbo].[Person] ON

INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (1, N'Hu',
N'Nan', NULL, CAST(0x0000A0BF00000000 AS DateTime))
INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (2,
N'Norman', N'Laura', NULL, CAST(0x0000A0BF00000000 AS DateTime))
INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (3,
N'Olivotto', N'Nino', NULL, CAST(0x0000A0BF00000000 AS DateTime))
INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (4,
N'Anand', N'Arturo', NULL, CAST(0x0000A0BF00000000 AS DateTime))
INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (5, N'Jai',
N'Damien', NULL, CAST(0x0000A0BF00000000 AS DateTime))
INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (6, N'Holt',
N'Roger', CAST(0x000097F100000000 AS DateTime), NULL)
INSERT [dbo].[Person] ([PersonID], [LastName], [FirstName], [HireDate], [EnrollmentDate]) VALUES (7,
N'Martin', N'Randall', CAST(0x00008B1A00000000 AS DateTime), NULL)
SET IDENTITY_INSERT [dbo].[Person] OFF
SET IDENTITY_INSERT [dbo].[StudentGrade] ON

INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (1, N'C1045', 1,


CAST(3.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (2, N'C1045', 2,
CAST(3.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (3, N'C1045', 3,
CAST(2.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (4, N'C1045', 4,
CAST(4.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (5, N'C1045', 5,
CAST(3.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (6, N'C1061', 1,
CAST(4.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (7, N'C1061', 3,
CAST(3.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (8, N'C1061', 4,
CAST(2.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (9, N'C1061', 5,
CAST(1.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (10, N'C2021', 1,
CAST(2.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (11, N'C2021', 2,
CAST(3.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (12, N'C2021', 4,
CAST(3.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (13, N'C2021', 5,
CAST(3.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (14, N'C2042', 1,
CAST(2.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (15, N'C2042', 2,
CAST(3.50 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (16, N'C2042', 3,
CAST(4.00 AS Decimal(3, 2)))
INSERT [dbo].[StudentGrade] ([EnrollmentID], [CourseID], [StudentID], [Grade]) VALUES (17, N'C2042', 5,
CAST(3.00 AS Decimal(3, 2)))
SET IDENTITY_INSERT [dbo].[StudentGrade] OFF
ALTER TABLE [dbo].[Course] WITH CHECK ADD CONSTRAINT [FK_Course_Department] FOREIGN KEY([DepartmentID])
REFERENCES [dbo].[Department] ([DepartmentID])
GO
ALTER TABLE [dbo].[Course] CHECK CONSTRAINT [FK_Course_Department]
GO
ALTER TABLE [dbo].[StudentGrade] WITH CHECK ADD CONSTRAINT [FK_StudentGrade_Student] FOREIGN
KEY([StudentID])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[StudentGrade] CHECK CONSTRAINT [FK_StudentGrade_Student]
GO

A continuación se incluye la lista de código:


TIP
La lista de código hace referencia a un archivo de base de datos de Access denominado MySchool.mdb. Puede descargar el.
mdb (como parte del proyecto de ejemplo Full C# o Visual Basic) de code.msdn.Microsoft.com.

using System;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;

class Program {
static void Main(string[] args) {
String SqlDbConnectionString = "Data Source=(local);Initial Catalog=MySchool;Integrated
Security=True;Asynchronous Processing=true;";

InsertPerson(SqlDbConnectionString, "Janice", "Galvin");


Console.WriteLine();

InsertPersonInAdapter(SqlDbConnectionString, "Peter", "Krebs");


Console.WriteLine();

String oledbConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=Database\\MySchool.mdb";


InsertPersonInJet4Database(oledbConnectionString, "Janice", "Galvin");
Console.WriteLine();

Console.WriteLine("Please press any key to exit.....");


Console.ReadKey();
}

// Using stored procedure to insert a new row and retrieve the identity value
static void InsertPerson(String connectionString, String firstName, String lastName) {
String commandText = "dbo.InsertPerson";

using (SqlConnection conn = new SqlConnection(connectionString)) {


using (SqlCommand cmd = new SqlCommand(commandText, conn)) {
cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add(new SqlParameter("@FirstName", firstName));


cmd.Parameters.Add(new SqlParameter("@LastName", lastName));
SqlParameter personId = new SqlParameter("@PersonID", SqlDbType.Int);
personId.Direction = ParameterDirection.Output;
cmd.Parameters.Add(personId);

conn.Open();
cmd.ExecuteNonQuery();

Console.WriteLine("Person Id of new person:{0}", personId.Value);


}
}
}

// Using stored procedure in adapter to insert new rows and update the identity value.
static void InsertPersonInAdapter(String connectionString, String firstName, String lastName) {
String commandText = "dbo.InsertPerson";
using (SqlConnection conn = new SqlConnection(connectionString)) {
SqlDataAdapter mySchool = new SqlDataAdapter("Select PersonID,FirstName,LastName from [dbo].
[Person]", conn);

mySchool.InsertCommand = new SqlCommand(commandText, conn);


mySchool.InsertCommand.CommandType = CommandType.StoredProcedure;

mySchool.InsertCommand.Parameters.Add(
new SqlParameter("@FirstName", SqlDbType.NVarChar, 50, "FirstName"));
mySchool.InsertCommand.Parameters.Add(
new SqlParameter("@LastName", SqlDbType.NVarChar, 50, "LastName"));
SqlParameter personId = mySchool.InsertCommand.Parameters.Add(new SqlParameter("@PersonID",
SqlDbType.Int, 0, "PersonID"));
personId.Direction = ParameterDirection.Output;

DataTable persons = new DataTable();


mySchool.Fill(persons);

DataRow newPerson = persons.NewRow();


newPerson["FirstName"] = firstName;
newPerson["LastName"] = lastName;
persons.Rows.Add(newPerson);

mySchool.Update(persons);
Console.WriteLine("Show all persons:");
ShowDataTable(persons, 14);
}
}

/// For a Jet 4.0 database, we need use the single statement and event handler to insert new rows and
retrieve the identity value.
static void InsertPersonInJet4Database(String connectionString, String firstName, String lastName) {
String commandText = "Insert into Person(FirstName,LastName) Values(?,?)";
using (OleDbConnection conn = new OleDbConnection(connectionString)) {
OleDbDataAdapter mySchool = new OleDbDataAdapter("Select PersonID,FirstName,LastName from Person",
conn);

// Create Insert Command


mySchool.InsertCommand = new OleDbCommand(commandText, conn);
mySchool.InsertCommand.CommandType = CommandType.Text;

mySchool.InsertCommand.Parameters.Add(new OleDbParameter("@FirstName", OleDbType.VarChar, 50,


"FirstName"));
mySchool.InsertCommand.Parameters.Add(new OleDbParameter("@LastName", OleDbType.VarChar, 50,
"LastName"));
mySchool.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

DataTable persons = CreatePersonsTable();

mySchool.Fill(persons);

DataRow newPerson = persons.NewRow();


newPerson["FirstName"] = firstName;
newPerson["LastName"] = lastName;
persons.Rows.Add(newPerson);

DataTable dataChanges = persons.GetChanges();

mySchool.RowUpdated += OnRowUpdated;

mySchool.Update(dataChanges);

Console.WriteLine("Data before merging:");


ShowDataTable(persons, 14);
Console.WriteLine();

persons.Merge(dataChanges);
persons.AcceptChanges();

Console.WriteLine("Data after merging");


ShowDataTable(persons, 14);
}
}

static void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs e) {


if (e.StatementType == StatementType.Insert) {
// Retrieve the identity value
OleDbCommand cmdNewId = new OleDbCommand("Select @@IDENTITY", e.Command.Connection);
e.Row["PersonID"] = (Int32)cmdNewId.ExecuteScalar();
// After the status is changed, the original values in the row are preserved. And the
// Merge method will be called to merge the new identity value into the original DataTable.
e.Status = UpdateStatus.SkipCurrentRow;
}
}

// Create the Persons table before filling.


private static DataTable CreatePersonsTable() {
DataTable persons = new DataTable();

DataColumn personId = new DataColumn();


personId.DataType = Type.GetType("System.Int32");
personId.ColumnName = "PersonID";
personId.AutoIncrement = true;
personId.AutoIncrementSeed = 0;
personId.AutoIncrementStep = -1;
persons.Columns.Add(personId);

DataColumn firstName = new DataColumn();


firstName.DataType = Type.GetType("System.String");
firstName.ColumnName = "FirstName";
persons.Columns.Add(firstName);

DataColumn lastName = new DataColumn();


lastName.DataType = Type.GetType("System.String");
lastName.ColumnName = "LastName";
persons.Columns.Add(lastName);

DataColumn[] pkey = { personId };


persons.PrimaryKey = pkey;

return persons;
}

private static void ShowDataTable(DataTable table, Int32 length) {


foreach (DataColumn col in table.Columns) {
Console.Write("{0,-" + length + "}", col.ColumnName);
}
Console.WriteLine();

foreach (DataRow row in table.Rows) {


foreach (DataColumn col in table.Columns) {
if (col.DataType.Equals(typeof(DateTime)))
Console.Write("{0,-" + length + ":d}", row[col]);
else if (col.DataType.Equals(typeof(Decimal)))
Console.Write("{0,-" + length + ":C}", row[col]);
else
Console.Write("{0,-" + length + "}", row[col]);
}

Console.WriteLine();
}
}
}

Vea también
Recuperar y modificar datos en ADO.NET
Objetos DataAdapter y DataReader
Estados y versiones de filas
AcceptChange y RejectChange
Combinar contenido de DataSet
Actualizar orígenes de datos con objetos DataAdapter
Información general sobre ADO.NET
Recuperación de datos binarios
21/03/2020 • 8 minutes to read • Edit Online

De forma predeterminada, el DataReader carga los datos entrantes como una fila tan pronto como una fila
completa de datos está disponible. Sin embargo, los objetos binarios grandes (BLOB) se deben tratar de otra forma,
ya que pueden llegar a contener grandes cantidades de datos (del orden de gigabytes) que no pueden almacenarse
en una sola fila. El método Command.ExecuteReader tiene una CommandBehavior sobrecarga que tomará un
argumento para modificar el comportamiento predeterminado de DataReader . Puede pasar SequentialAccess al
método ExecuteReader para modificar el comportamiento predeterminado de DataReader para que, en lugar de
cargar filas de datos, cargue los datos secuencialmente a medida que se reciban. Este sistema es idóneo para
cargar BLOB y otras estructuras de datos grandes. Tenga en cuenta que este comportamiento puede depender del
origen de datos. Por ejemplo, si se devuelve un BLOB desde Microsoft Access, el BLOB completo se cargará en
memoria, en lugar de hacerlo secuencialmente a medida que se recibe.
Al establecer DataReader para usar SequentialAccess , es importante tener en cuenta la secuencia en la que se
tiene acceso a los campos devueltos. El comportamiento predeterminado de DataReader , que carga una fila
completa tan pronto como está disponible, le permite tener acceso a los campos devueltos en cualquier orden
hasta que se lea la siguiente fila. Sin embargo, al usar SequentialAccess, debe tener acceso a los campos
devueltos por El DataReader en orden. Por ejemplo, si la consulta devuelve tres columnas y la tercera es un BLOB,
debe devolver los valores de los campos primero y segundo antes de tener acceso a los datos BLOB del tercer
campo. Si trata de tener acceso al tercer campo antes que al primero o el segundo, puede que éstos dejen de estar
disponibles. Esto se debe a que SequentialAccess ha modificado el DataReader para devolver datos en
secuencia y los datos no están disponibles después de que DataReader lo haya leído más allá de él.
Al tener acceso a los datos en el campo BLOB, utilice los descriptores de acceso con tipo GetBytes o GetChars de
DataReader , que rellenan una matriz con datos. También puede usar GetString para los datos de caracteres; Sin
embargo. si desea conservar los recursos del sistema, es mejor que no cargue un valor BLOB completo en una sola
variable de cadena. En lugar de ello, puede especificar un tamaño determinado de búfer para los datos que se van a
devolver, así como la ubicación de comienzo para leer el primer byte o carácter de los datos devueltos. GetBytes y
GetChars long devolverán un valor, que representa el número de bytes o caracteres devueltos. Si pasa una
matriz null a GetBytes o GetChars , el valor long devuelto será el número total de bytes o caracteres en el BLOB.
También puede especificar un índice de la matriz como posición de comienzo para la lectura de datos.

Ejemplo
En el ejemplo siguiente se devuelve el identificador de publicador y el logotipo de la base de datos de ejemplo
pubs en Microsoft SQL Server. El identificador del publicador ( pub_id ) es un campo de caracteres, mientras que el
logotipo es una imagen de BLOB. Dado que el campo logo es un mapa de bits, el ejemplo devuelve datos binarios
mediante GetBytes . Tenga en cuenta que la necesidad de tener acceso a los datos de forma secuencial hace que en
la fila actual de datos se tenga acceso al identificador de publicador antes que al logotipo.
' Assumes that connection is a valid SqlConnection object.
Dim command As SqlCommand = New SqlCommand( _
"SELECT pub_id, logo FROM pub_info", connection)

' Writes the BLOB to a file (*.bmp).


Dim stream As FileStream
' Streams the binary data to the FileStream object.
Dim writer As BinaryWriter
' The size of the BLOB buffer.
Dim bufferSize As Integer = 100
' The BLOB byte() buffer to be filled by GetBytes.
Dim outByte(bufferSize - 1) As Byte
' The bytes returned from GetBytes.
Dim retval As Long
' The starting position in the BLOB output.
Dim startIndex As Long = 0

' The publisher id to use in the file name.


Dim pubID As String = ""

' Open the connection and read data into the DataReader.
connection.Open()
Dim reader As SqlDataReader = command.ExecuteReader(CommandBehavior.SequentialAccess)

Do While reader.Read()
' Get the publisher id, which must occur before getting the logo.
pubID = reader.GetString(0)

' Create a file to hold the output.


stream = New FileStream( _
"logo" & pubID & ".bmp", FileMode.OpenOrCreate, FileAccess.Write)
writer = New BinaryWriter(stream)

' Reset the starting byte for a new BLOB.


startIndex = 0

' Read bytes into outByte() and retain the number of bytes returned.
retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize)

' Continue while there are bytes beyond the size of the buffer.
Do While retval = bufferSize
writer.Write(outByte)
writer.Flush()

' Reposition start index to end of the last buffer and fill buffer.
startIndex += bufferSize
retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize)
Loop

' Write the remaining buffer.


writer.Write(outByte, 0 , retval - 1)
writer.Flush()

' Close the output file.


writer.Close()
stream.Close()
Loop

' Close the reader and the connection.


reader.Close()
connection.Close()
// Assumes that connection is a valid SqlConnection object.
SqlCommand command = new SqlCommand(
"SELECT pub_id, logo FROM pub_info", connection);

// Writes the BLOB to a file (*.bmp).


FileStream stream;
// Streams the BLOB to the FileStream object.
BinaryWriter writer;

// Size of the BLOB buffer.


int bufferSize = 100;
// The BLOB byte[] buffer to be filled by GetBytes.
byte[] outByte = new byte[bufferSize];
// The bytes returned from GetBytes.
long retval;
// The starting position in the BLOB output.
long startIndex = 0;

// The publisher id to use in the file name.


string pubID = "";

// Open the connection and read data into the DataReader.


connection.Open();
SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);

while (reader.Read())
{
// Get the publisher id, which must occur before getting the logo.
pubID = reader.GetString(0);

// Create a file to hold the output.


stream = new FileStream(
"logo" + pubID + ".bmp", FileMode.OpenOrCreate, FileAccess.Write);
writer = new BinaryWriter(stream);

// Reset the starting byte for the new BLOB.


startIndex = 0;

// Read bytes into outByte[] and retain the number of bytes returned.
retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize);

// Continue while there are bytes beyond the size of the buffer.
while (retval == bufferSize)
{
writer.Write(outByte);
writer.Flush();

// Reposition start index to end of last buffer and fill buffer.


startIndex += bufferSize;
retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize);
}

// Write the remaining buffer.


writer.Write(outByte, 0, (int)retval);
writer.Flush();

// Close the output file.


writer.Close();
stream.Close();
}

// Close the reader and the connection.


reader.Close();
connection.Close();
Consulte también
DATOS binarios y de gran valor de SQL Server
Información general de ADO.NET
Modificar datos con procedimientos almacenados
23/10/2019 • 5 minutes to read • Edit Online

Los procedimientos almacenados pueden aceptar datos como parámetros de entrada y pueden devolver datos
como parámetros de salida, conjuntos de resultados o valores de retorno. En el ejemplo siguiente se muestra
cómo ADO.NET envía y recibe parámetros de entrada, parámetros de salida y valores de retorno. El ejemplo
inserta un nuevo registro en una tabla cuya columna de clave principal es una columna de identidad en una base
de datos de SQL Server.

NOTE
Si está utilizando procedimientos almacenados de SQL Server para editar o eliminar datos con SqlDataAdapter, asegúrese de
que no utiliza SET NOCOUNT ON en la definición del procedimiento almacenado. Esto hace que el recuento de filas afectadas
vuelva a cero, lo que DataAdapter interpreta como un conflicto de simultaneidad. En este caso, se iniciará una
DBConcurrencyException.

Ejemplo
En el ejemplo se usa el siguiente procedimiento almacenado para insertar una nueva categoría en la tabla
Categories de Nor thwind . El procedimiento almacenado toma el valor de la columna Categor yName como
parámetro de entrada y usa la función SCOPE_IDENTITY () para recuperar el nuevo valor del campo de identidad,
Categor yID , y devolverlo en un parámetro de salida. La instrucción return utiliza la@ROWCOUNT función @
para devolver el número de filas insertadas.

CREATE PROCEDURE dbo.InsertCategory


@CategoryName nvarchar(15),
@Identity int OUT
AS
INSERT INTO Categories (CategoryName) VALUES(@CategoryName)
SET @Identity = SCOPE_IDENTITY()
RETURN @@ROWCOUNT

En el siguiente ejemplo de código se utiliza el anterior procedimiento almacenado InsertCategory como origen de
la propiedad InsertCommand de SqlDataAdapter. El parámetro de salida @Identity se reflejará en DataSet una
vez que se haya insertado el registro en la base de dados al llamar al método Update del SqlDataAdapter. El
código también recupera el valor devuelto.

NOTE
Al utilizar OleDbDataAdapter, debe especificar los parámetros con un ParameterDirection de ReturnValue antes que los
demás parámetros.

using System;
using System.Data;
using System.Data.SqlClient;

class Program
{
static void Main()
{
string connectionString = GetConnectionString();
ReturnIdentity(connectionString);
// Console.ReadLine();
}

private static void ReturnIdentity(string connectionString)


{
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create a SqlDataAdapter based on a SELECT query.
SqlDataAdapter adapter = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM dbo.Categories",
connection);

// Create a SqlCommand to execute the stored procedure.


adapter.InsertCommand = new SqlCommand("InsertCategory", connection);
adapter.InsertCommand.CommandType = CommandType.StoredProcedure;

// Create a parameter for the ReturnValue.


SqlParameter parameter = adapter.InsertCommand.Parameters.Add("@RowCount", SqlDbType.Int);
parameter.Direction = ParameterDirection.ReturnValue;

// Create an input parameter for the CategoryName.


// You do not need to specify direction for input parameters.
adapter.InsertCommand.Parameters.Add("@CategoryName", SqlDbType.NChar, 15, "CategoryName");

// Create an output parameter for the new identity value.


parameter = adapter.InsertCommand.Parameters.Add("@Identity", SqlDbType.Int, 0, "CategoryID");
parameter.Direction = ParameterDirection.Output;

// Create a DataTable and fill it.


DataTable categories = new DataTable();
adapter.Fill(categories);

// Add a new row.


DataRow categoryRow = categories.NewRow();
categoryRow["CategoryName"] = "New Beverages";
categories.Rows.Add(categoryRow);

// Update the database.


adapter.Update(categories);

// Retrieve the ReturnValue.


Int32 rowCount = (Int32)adapter.InsertCommand.Parameters["@RowCount"].Value;

Console.WriteLine("ReturnValue: {0}", rowCount.ToString());


Console.WriteLine("All Rows:");
foreach (DataRow row in categories.Rows)
{
Console.WriteLine(" {0}: {1}", row[0], row[1]);
}
}
}

static private string GetConnectionString()


{
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file.
return "Data Source=(local);Initial Catalog=Northwind;Integrated Security=true";
}
}

Option Explicit On
Option Strict On

Imports System.Data
Imports System.Data.SqlClient

Module Class1
Sub Main()
Dim connectionString As String = _
GetConnectionString()
ReturnIdentity(connectionString)
' Console.ReadLine()
End Sub

Private Sub ReturnIdentity(ByVal connectionString As String)


Using connection As SqlConnection = New SqlConnection( _
connectionString)

' Create a SqlDataAdapter based on a SELECT query.


Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CategoryID, CategoryName FROM dbo.Categories", _
connection)

' Create a SqlCommand to execute the stored procedure.


adapter.InsertCommand = New SqlCommand("dbo.InsertCategory", _
connection)
adapter.InsertCommand.CommandType = CommandType.StoredProcedure

' Create a parameter for the ReturnValue.


Dim parameter As SqlParameter = _
adapter.InsertCommand.Parameters.Add( _
"@RowCount", SqlDbType.Int)
parameter.Direction = ParameterDirection.ReturnValue

' Create an input parameter for the CategoryName.


' You do not need to specify direction for input parameters.
adapter.InsertCommand.Parameters.Add( _
"@CategoryName", SqlDbType.NChar, 15, "CategoryName")

' Create an output parameter for the new identity value.


parameter = adapter.InsertCommand.Parameters.Add( _
"@Identity", SqlDbType.Int, 0, "CategoryID")
parameter.Direction = ParameterDirection.Output

' Create a DataTable and fill it.


Dim categories As DataTable = New DataTable
adapter.Fill(categories)

' Add a new row.


Dim newRow As DataRow = categories.NewRow()
newRow("CategoryName") = "New Category"
categories.Rows.Add(newRow)

' Update the database.


adapter.Update(categories)

' Retrieve the ReturnValue.


Dim rowCount As Int32 = _
CInt(adapter.InsertCommand.Parameters("@RowCount").Value)

Console.WriteLine("ReturnValue: {0}", rowCount.ToString())


Console.WriteLine("All Rows:")
Dim row As DataRow
For Each row In categories.Rows
Console.WriteLine(" {0}: {1}", row(0), row(1))
Next
End Using
End Sub

Private Function GetConnectionString() As String


' To avoid storing the connection string in your code,
' you can retrieve it from a configuration file.
Return "Data Source=(local);Initial Catalog=Northwind;" _
& "Integrated Security=true;"
& "Integrated Security=true;"
End Function

End Module

Vea también
Recuperar y modificar datos en ADO.NET
Objetos DataAdapter y DataReader
Ejecución de un comando
Información general sobre ADO.NET
Recuperar información del esquema de la base de
datos
23/10/2019 • 4 minutes to read • Edit Online

La obtención de información de esquema de una base de datos se efectúa con el proceso de detección de
esquemas. La detección de esquemas permite a las aplicaciones solicitar que los proveedores administrados
busquen y devuelvan información sobre el esquema de la base de datos, también conocido como metadatos, de
una base de datos determinada. Los diferentes elementos del esquema de base de datos, como tablas, columnas y
procedimientos almacenados, se exponen a través de colecciones de esquemas. Cada colección de esquemas
contiene diversa información de esquema relativa al proveedor que se está utilizando.
Cada uno de los proveedores administrados de .NET Framework implementa el método GetSchema en la clase
Connection , y la información de esquema que se devuelve desde el método GetSchema tiene el
DataTableformato. El método GetSchema es un método sobrecargado que proporciona parámetros opcionales
para especificar la colección de esquemas que se va a devolver y restringir la cantidad de información devuelta.
Los proveedores de datos de .NET Framework para OLE DB, ODBC, Oracle y SqlClient proporcionan un método
GetSchemaTable que devuelve un DataTable que describe los metadatos de columna del DataReader .
Además el proveedor de datos .NET Framework para OLE DB también expone información de esquema mediante
el método GetOleDbSchemaTable del objeto OleDbConnection. Como argumentos, GetOleDbSchemaTable
toma un OleDbSchemaGuid que identifica la información de esquema que se va a devolver y una matriz de
restricciones en esas columnas devueltas. GetOleDbSchemaTable devuelve un DataTable rellenado con la
información de esquema solicitada.

En esta sección
GetSchema y colecciones de esquema
Describe el método GetSchema y cómo se puede utilizar para recuperar y restringir la información de esquema
de una base de datos.
Restricciones de esquema
Describe las restricciones de esquema que se pueden usar con GetSchema .
Colecciones de esquemas comunes
Describe todas las colecciones de esquemas comunes que admiten todos los proveedores administrados de .NET
Framework.
Colecciones de esquemas de SQL Server
Describe la colección de esquemas compatibles con el proveedor de datos .NET Framework para SQL Server.
Colecciones de esquemas de Oracle
Describe la colección de esquemas compatibles con el proveedor de datos .NET Framework para Oracle.
Colecciones de esquemas de ODBC
Describe las colecciones de esquemas para los controladores ODBC.
Colecciones de esquemas de OLE DB
Describe las colecciones de esquemas para los proveedores OLE DB.

Referencia
GetSchema
Describe el método GetSchema de la DbConnection clase.
GetSchema
Describe el método GetSchema de la OdbcConnection clase.
GetSchema
Describe el método GetSchema de la OleDbConnection clase.
GetSchema
Describe el método GetSchema de la OracleConnection clase.
GetSchema
Describe el método GetSchema de la SqlConnection clase.
GetSchemaTable
Describe el método GetSchemaTable de la DbDataReader clase.
GetSchemaTable
Describe el método GetSchemaTable de la OdbcDataReader clase.
GetSchemaTable
Describe el método GetSchemaTable de la OleDbDataReader clase.
GetSchemaTable
Describe el método GetSchemaTable de la OracleDataReader clase.
GetSchemaTable
Describe el método GetSchemaTable de la SqlDataReader clase.

Vea también
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
GetSchema y colecciones de esquema
21/03/2020 • 3 minutes to read • Edit Online

Las clases connection de cada uno de los proveedores administrados de .NET Framework implementan un
método GetSchema que se usa para recuperar DataTableinformación de esquema sobre la base de datos que está
conectada actualmente y la información de esquema devuelta desde el método GetSchema viene en forma de
archivo . El GetSchema método es un método sobrecargado que proporciona parámetros opcionales para
especificar la colección de esquemas que se va a devolver y restringir la cantidad de información devuelta.

Especificación de las colecciones de esquemas


El primer parámetro opcional del método GetSchema es el nombre de la colección que se especifica como una
cadena. Existen dos tipos de colecciones de esquemas: comunes, que son comunes a todos los proveedores, y
específicas, que son específicas de cada proveedor.
Puede consultar un proveedor administrado de .NET Framework para determinar la lista de colecciones de
esquemas compatibles llamando al método GetSchema sin argumentos o con el nombre de colección de
esquemas "MetaDataCollections". Esto devolverá una DataTable con una lista de colecciones de esquemas
admitidas, el número de restricciones que admite cada una y el número de partes de identificador que emplean.
Ejemplo de recuperación de colecciones de esquemas
En los ejemplos siguientes GetSchema se muestra cómo utilizar el método SqlConnection del proveedor de datos
de .NET Framework para la clase de SQL Server para recuperar información de esquema sobre todas las tablas
contenidas en la base de datos de ejemplo AdventureWorks:
Imports System.Data.SqlClient

Module Module1
Sub Main()
Dim connectionString As String = GetConnectionString()
Using connection As New SqlConnection(connectionString)
'Connect to the database then retrieve the schema information.
connection.Open()
Dim table As DataTable = connection.GetSchema("Tables")

' Display the contents of the table.


DisplayData(table)
Console.WriteLine("Press any key to continue.")
Console.ReadKey()
End Using
End Sub

Private Function GetConnectionString() As String


' To avoid storing the connection string in your code,
' you can retrieve it from a configuration file.
Return "Data Source=(local);Database=AdventureWorks;" _
& "Integrated Security=true;"
End Function

Private Sub DisplayData(ByVal table As DataTable)


For Each row As DataRow In table.Rows
For Each col As DataColumn In table.Columns
Console.WriteLine("{0} = {1}", col.ColumnName, row(col))
Next
Console.WriteLine("============================")
Next
End Sub
End Module
using System;
using System.Data;
using System.Data.SqlClient;

class Program
{
static void Main()
{
string connectionString = GetConnectionString();
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Connect to the database then retrieve the schema information.
connection.Open();
DataTable table = connection.GetSchema("Tables");

// Display the contents of the table.


DisplayData(table);
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
}

private static string GetConnectionString()


{
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file.
return "Data Source=(local);Database=AdventureWorks;" +
"Integrated Security=true;";
}

private static void DisplayData(System.Data.DataTable table)


{
foreach (System.Data.DataRow row in table.Rows)
{
foreach (System.Data.DataColumn col in table.Columns)
{
Console.WriteLine("{0} = {1}", col.ColumnName, row[col]);
}
Console.WriteLine("============================");
}
}
}

Consulte también
Recuperación de información del esquema de la base de datos
Información general de ADO.NET
Restricciones de esquema
21/03/2020 • 9 minutes to read • Edit Online

El segundo parámetro opcional del método GetSchema son las restricciones que se usan para limitar la cantidad
de información de esquema devuelta y se pasa al método GetSchema como una matriz de cadenas. La posición en
la matriz determina los valores que puede pasar, y es equivalente al número de restricciones.
Por ejemplo, en la tabla siguiente se describen las restricciones que admite la colección de esquemas "Tables"
utilizando el proveedor de datos .NET Framework para SQL Server. Las restricciones adicionales para las
colecciones de esquemas de SQL Server se muestran al final de este tema.

N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Name TABLE_NAME 3

TableType @TableType TABLE_TYPE 4

Especificación de los valores de restricción


Para utilizar una de las restricciones de la colección de esquemas "Tables", basta con crear una matriz de cadenas
con cuatro elementos y, después, colocar un valor en el elemento que coincida con el número de restricción. Por
ejemplo, para restringir las tablas devueltas por el método GetSchema solo a las tablas del esquema "Sales",
establezca el segundo elemento de la matriz en "Sales" antes de pasarlo al método GetSchema.

NOTE
Las colecciones con restricciones para SqlClient y OracleClient tienen una columna ParameterName adicional. La
columna de valor predeterminado de restricción sigue ahí para la compatibilidad con versiones anteriores, pero actualmente
se omite. Para reducir el riesgo de un ataque de inyección de SQL al especificar valores de restricción, es necesario utilizar
consultas parametrizadas en lugar de sustitución de cadenas.

NOTE
El número de elementos de la matriz debe ser menor o igual que el número de restricciones admitidas en la colección de
esquemas especificada o se iniciará una ArgumentException. Puede haber un número de restricciones inferior al máximo. Se
supone que las restricciones que faltan serán nulas (sin restricciones).

Puede consultar un proveedor administrado de .NET Framework para determinar la lista de restricciones admitidas
llamando al método GetSchema con el nombre de la colección de esquemas de restricciones, que es
"Restrictions". Esto devolverá una DataTable con una lista de los nombres de colecciones, los nombres de
restricciones, los valores predeterminados de restricción y los números de restricciones.
Ejemplo
En los ejemplos siguientes GetSchema se muestra cómo utilizar el método SqlConnection del proveedor de datos
de .NET Framework para la clase de SQL ServerSQL Server para recuperar información de esquema sobre todas las
tablas contenidas en la base de datos de ejemplo AdventureWorks y restringir la información devuelta solo a las
tablas del esquema "Sales":

Imports System.Data.SqlClient

Module Module1
Sub Main()
Dim connectionString As String = _
"Data Source=(local);Database=AdventureWorks;" & _
"Integrated Security=true;";

Dim restrictions(3) As String


Using connection As New SqlConnection(connectionString)
connection.Open()

'Specify the restrictions.


restrictions(1) = "Sales"
Dim table As DataTable = connection.GetSchema("Tables", _
restrictions)

' Display the contents of the table.


For Each row As DataRow In table.Rows
For Each col As DataColumn In table.Columns
Console.WriteLine("{0} = {1}", col.ColumnName, row(col))
Next
Console.WriteLine("============================")
Next
Console.WriteLine("Press any key to continue.")
Console.ReadKey()
End Using
End Sub
End Module
using System;
using System.Data;
using System.Data.SqlClient;

class Program
{
static void Main()
{
string connectionString =
"Data Source=(local);Database=AdventureWorks;" +
"Integrated Security=true;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
connection.Open();

// Specify the restrictions.


string[] restrictions = new string[4];
restrictions[1] = "Sales";
System.Data.DataTable table = connection.GetSchema(
"Tables", restrictions);

// Display the contents of the table.


foreach (System.Data.DataRow row in table.Rows)
{
foreach (System.Data.DataColumn col in table.Columns)
{
Console.WriteLine("{0} = {1}",
col.ColumnName, row[col]);
}
Console.WriteLine("============================");
}
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
}

private static string GetConnectionString()


{
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file.
return "Data Source=(local);Database=AdventureWorks;" +
"Integrated Security=true;";
}

private static void DisplayData(System.Data.DataTable table)


{
foreach (System.Data.DataRow row in table.Rows)
{
foreach (System.Data.DataColumn col in table.Columns)
{
Console.WriteLine("{0} = {1}", col.ColumnName, row[col]);
}
Console.WriteLine("============================");
}
}
}

Restricciones de esquema de SQL Server


En la tabla siguiente se muestran las restricciones de las colecciones de esquemas de SQL Server.
Usuarios
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

User_Name @Name name 1

Bases de datos
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Nombre @Name Nombre 1

Tablas
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Name TABLE_NAME 3

TableType @TableType TABLE_TYPE 4

Columnas
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Table TABLE_NAME 3

Columna @Column COLUMN_NAME 4

StructuredTypeMembers
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Table TABLE_NAME 3

Columna @Column COLUMN_NAME 4

Vistas
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Table TABLE_NAME 3

ViewColumns
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog VIEW_CATALOG 1

Propietario @Owner VIEW_SCHEMA 2

Tabla @Table VIEW_NAME 3

Columna @Column COLUMN_NAME 4

ProcedureParameters
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog SPECIFIC_CATALOG 1

Propietario @Owner SPECIFIC_SCHEMA 2

Nombre @Name SPECIFIC_NAME 3

Parámetro @Parameter PARAMETER_NAME 4

Procedimientos
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog SPECIFIC_CATALOG 1

Propietario @Owner SPECIFIC_SCHEMA 2

Nombre @Name SPECIFIC_NAME 3

Tipo @Type ROUTINE_TYPE 4

IndexColumns
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog db_name() 1


N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Propietario @Owner user_name() 2

Tabla @Table o.name 3

ConstraintName @ConstraintName x.name 4

Columna @Column c.name 5

Índices
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog db_name() 1

Propietario @Owner user_name() 2

Tabla @Table o.name 3

UserDefinedTypes
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

assembly_name @AssemblyName assemblies.name 1

udt_name @UDTName types.assembly_class 2

ForeignKeys
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog CONSTRAINT_CATALOG 1

Propietario @Owner CONSTRAINT_SCHEMA 2

Tabla @Table TABLE_NAME 3

Nombre @Name CONSTRAINT_NAME 4

Restricciones de esquema de SQL Server 2008


En la tabla siguiente se muestran las restricciones de las colecciones de esquemas de SQL Server 2008. Estas
restricciones son válidas a partir de la versión 3.5 SP1 de .NET Framework y SQL Server 2008. No se admiten en
versiones anteriores de .NET Framework y SQL Server.
ColumnSetColumns
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Table TABLE_NAME 3

AllColumns
N O M B RE DE L A VA LO R P REDET ERM IN A DO
REST RIC C IÓ N N O M B RE DE PA RÁ M ET RO DE L A REST RIC C IÓ N N ÚM ERO DE REST RIC C IO N ES

Catálogo @Catalog TABLE_CATALOG 1

Propietario @Owner TABLE_SCHEMA 2

Tabla @Table TABLE_NAME 3

Columna @Column COLUMN_NAME 4

Consulte también
Información general de ADO.NET
Colecciones de esquemas comunes
27/03/2020 • 21 minutes to read • Edit Online

Las colecciones de esquemas comunes son las colecciones de esquemas que implementa cada uno de los
proveedores administrados de .NET Framework. Puede consultar un proveedor administrado de .NET Framework
para determinar la lista de colecciones de esquemas compatibles llamando al método GetSchema sin argumentos
o con el nombre de colección de esquemas "MetaDataCollections". Esto devolverá una DataTable con una lista de
colecciones de esquemas admitidas, el número de restricciones que admite cada una y el número de partes de
identificador que emplean. Estas colecciones describen todas las columnas necesarias. Los proveedores pueden
agregar más columnas si lo desean. Por ejemplo, SqlClient y OracleClient agregan ParameterName a la
colección con restricciones.
Si un proveedor no puede determinar el valor de una columna necesaria, se devolverá NULL.
Para obtener más información sobre el uso de los métodos GetSchema, vea GetSchema y Coleccionesde
esquemas .

MetaDataCollections
Esta colección de esquemas expone información acerca de las colecciones de esquemas que admite el proveedor
administrado de .NET Framework que se utiliza actualmente para conectarse a la base de datos.

C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

CollectionName string El nombre de la colección que se va a


pasar al método GetSchema para
devolver la colección.

NumberOfRestrictions int El número de restricciones que se


pueden especificar para la colección.

NumberOfIdentifierParts int El número de partes del identificador


compuesto y nombre del objeto de
base de datos. Por ejemplo, en SQL
Server, sería 3 para las tablas y 4 para
las columnas. En Oracle, sería 2 para las
tablas y 3 para las columnas.

DataSourceInformation
Esta colección de esquemas expone información acerca del origen de datos al que actualmente está conectado el
proveedor administrado de .NET Framework.

C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N


C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

CompositeIdentifierSeparatorPattern string La expresión regular que va a hacer


corresponder los separadores
compuestos en un identificador
compuesto. Por ejemplo, "\". (para SQL
Server)@ \o "|." (en Oracle).

Un identificador compuesto suele ser lo


que se utiliza para un nombre
de@objeto de base de datos, por
ejemplo: pubs.dbo.authors o pubs
dbo.authors.

Para SQL Server, use\la expresión


regular " .". Para OracleClient,@ utilice
\"|.".

En ODBC, utilice
Catalog_name_seperator.

En OLE DB, use


DBLITERAL_CATALOG_SEPARATOR o
DBLITERAL_SCHEMA_SEPARATOR.

DataSourceProductName string El nombre del producto al que tiene


acceso el proveedor, por ejemplo,
"Oracle" o "SQLServer".

DataSourceProductVersion string Indica la versión del producto al que


tiene acceso el proveedor, en el formato
nativo de los orígenes de datos y no en
el formato de Microsoft.

En algunos casos,
DataSourceProductVersion y
DataSourceProductVersionNormalized
tendrán el mismo valor. En el caso de
OLE DB y ODBC, serán siempre iguales
dado que se asignan a la misma llamada
de función en la API nativa subyacente.
C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

DataSourceProductVersionNormalized string Una versión normalizada del origen de


datos, de forma que se puede comparar
con String.Compare() . Su formato es
coherente con todas las versiones del
proveedor para evitar que la versión 10
se clasifique entre la versión 1 y la
versión 2.

Por ejemplo, el proveedor de Oracle


utiliza un formato de
"nn.nn.nn.nn.nn.nn" para su versión
normalizada, lo que hace que un origen
de datos de Oracle 8i devuelva
"08.01.07.04.01". SQL ServerSQL Server
usa el formato típico de Microsoft
"nn.nn.nnnn".

En algunos casos,
DataSourceProductVersion y
DataSourceProductVersionNormalized
tendrán el mismo valor. En el caso de
OLE DB y ODBC, serán siempre iguales
dado que se asignan a la misma llamada
de función en la API nativa subyacente.

GroupByBehavior GroupByBehavior Especifica la relación entre las columnas


de una cláusula GROUP BY y las
columnas no agregadas de la lista de
selección.

IdentifierPattern string Expresión regular que crea una


correspondencia con un identificador y
con un valor de coincidencia del
identificador. Por ejemplo, "[A-Za-z0-
9_#$]".

IdentifierCase IdentifierCase Indica si los identificadores que no se


incluyen entre comillas se usan con
distinción de mayúsculas y minúsculas o
no.

OrderByColumnsInSelect bool Especifica si las columnas de una


cláusula ORDER BY deben estar en la
lista de selección. Un valor de true
indica que es necesario que estén en la
lista de selección; un valor de false
indica que no es necesario que estén en
la lista de selección.
C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

ParameterMarkerFormat string Una cadena de formato que representa


cómo dar formato a un parámetro.

Si el origen de datos admite parámetros


con nombre, el primer marcador de
posición de esta cadena debe estar
donde se debe dar formato al nombre
del parámetro.

Por ejemplo, si el origen de datos espera


que los parámetros se{0}nombren y
prefijen un ':' esto sería ": ". Cuando se
formatea con un nombre de parámetro
de "p1", la cadena resultante es ":p1".

Si el origen de datos espera que@los


parámetros tengan el prefijo '{0}', pero
los nombres ya@los incluyen,
esto@sería ' ', y el resultado de dar
formato a un parámetro denominado "
p1" simplemente sería " p1".

Para los orígenes de datos que no


esperan parámetros con nombre y
esperan el uso del carácter '?', la cadena
de formato se puede especificar como
simplemente '?', lo que omitiría el
nombre del parámetro. Para OLE DB
devolvemos '?'.

ParameterMarkerPattern string Una expresión regular que crea una


correspondencia con un marcador de
parámetro. Tendrá un valor de
correspondencia del nombre del
parámetro, si lo hay.

Por ejemplo, si los parámetros@con


nombre se admiten con un carácter de
entrada@' ' que se incluirá en el nombre
del parámetro, esto sería: "( [A-Za-z0-
9_$-]*)".

Sin embargo, si los parámetros con


nombre se admiten con un ':' como el
carácter de entrada principal y no*forma
parte del nombre del parámetro, esto
sería: ":([A-Za-z0-9_$-] )".

Por supuesto, si el origen de datos no


admite parámetros con nombre, esto
sería simplemente "?".
C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

ParameterNameMaxLength int La longitud máxima del nombre del


parámetro en caracteres. Visual Studio
espera que si se admiten nombres de
parámetros, el valor mínimo de la
longitud máxima sea 30 caracteres.

Si el origen de datos no admite


parámetros con nombre, esta propiedad
devuelve cero.

ParameterNamePattern string Una expresión regular que crea una


correspondencia con los nombres de
parámetros válidos. Según el origen de
datos, existen diferentes reglas respecto
a los caracteres que se pueden utilizar
en los nombres de parámetros.

Visual Studio espera que si se admiten


nombres de parámetros, los caracteres
"\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\p{N
d}" son el juego mínimo de caracteres
admitidos que son válidos en nombres
de parámetros.

QuotedIdentifierPattern string Una expresión regular que crea una


correspondencia con un identificador
incluido entre comillas y que tiene un
valor de correspondencia del propio
identificador sin las comillas. Por
ejemplo, si el origen de datos utiliza
comillas dobles para identificar
identificadores entrecomillados, esto
sería: "(([-|\ \"")\")".

QuotedIdentifierCase IdentifierCase Indica si los identificadores incluidos


entre comillas se tratan o no como con
diferenciación entre mayúsculas y
minúsculas.

StatementSeparatorPattern string Una expresión regular que crea una


correspondencia con el separador de
instrucciones.

StringLiteralPattern string Una expresión regular que crea una


correspondencia con un literal de
cadena y que tiene un valor de
correspondencia del propio literal. Por
ejemplo, si el origen de datos utiliza
comillas simples para identificar
cadenas, esto sería: "(((['']|'')*')"'

SupportedJoinOperators SupportedJoinOperators Especifica los tipos de instrucciones de


unión SQL que admite el origen de
datos.

DataTypes
Esta colección de esquemas expone información acerca de los tipos de datos que admite la base de datos a la que
está conectado actualmente el proveedor de datos de .NET Framework.

C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

TypeName string El nombre del tipo de datos específico


del proveedor.

ProviderDbType int El valor de tipo específico del proveedor


que se debe usar al especificar el tipo de
un parámetro. Por ejemplo,
SqlDbType.Money u OracleType.Blob.

ColumnSize long La longitud de una columna o


parámetro no numérico hace referencia
a la longitud máxima o a la longitud que
ha definido el proveedor para este tipo.

En datos de caracteres, es la longitud


máxima o definida en unidades por el
origen de datos. Oracle tiene el
concepto de especificar una longitud y,
a continuación, el tamaño de
almacenamiento real en algunos tipos
de datos de caracteres. Esto solo define
la longitud en unidades en Oracle.

En los tipos de datos de fecha y hora, es


la longitud de la representación de
cadena (suponiendo la precisión máxima
permitida del componente de segundos
decimales).

Si el tipo de datos es numérico, se


corresponde al límite superior de la
precisión máxima del tipo de datos.

CreateFormat string La cadena de formato que representa


cómo agregar esta columna a una
instrucción de definición de datos, como
CREATE TABLE. Cada elemento de la
matriz CreateParameter se debe
representar con un "marcador de
parámetro" en la cadena de formato.

Por ejemplo, el tipo de datos SQL


DECIMAL necesita una precisión y una
escala. En este caso, la cadena de{0}
{1}formato sería "DECIMAL( , )".
C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

CreateParameters string Los parámetros de creación que se


deben especificar al crear una columna
de este tipo de datos. Cada parámetro
de creación se muestra en la cadena,
separado por una coma en el orden en
que se suministran.

Por ejemplo, el tipo de datos SQL


DECIMAL necesita una precisión y una
escala. En este caso, los parámetros de
creación deben contener la cadena
"precisión, escala".

En un comando de texto para crear una


columna DECIMAL con una precisión de
10 y{0}{1}una escala de 2, el valor de la
columna CreateFormat podría ser
DECIMAL( , )" y la especificación de tipo
completa sería DECIMAL(10,2).

DataType string El nombre del tipo de datos de .NET


Framework.

IsAutoincrementable bool true: los valores de este tipo de datos


pueden ser de incremento automático.

false: los valores de este tipo de datos


podrían no ser de incremento
automático.

Tenga en cuenta que esto simplemente


indica si una columna de este tipo de
datos podría ser de incremento
automático, no que todas las columnas
de este tipo lo sean.

IsBestMatch bool true: el tipo de datos es la mejor


coincidencia entre todos los tipos de
datos del almacén de datos y el tipo de
datos de .NET Framework que indica el
valor de la columna DataType.

false: el tipo de datos no es la mejor


coincidencia.

En cada conjunto de filas en las que el


valor de la columna DataType sea el
mismo, la columna IsBestMatch solo se
establece en true en una fila.

IsCaseSensitive bool true: el tipo de datos es de tipo carácter


y distingue entre mayúsculas y
minúsculas.

false: el tipo de datos no es de tipo


carácter y no distingue entre
mayúsculas y minúsculas.
C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

IsFixedLength bool true: las columnas de este tipo de datos


creadas con el lenguaje de definición de
datos (DDL) serán de longitud fija.

false: las columnas de este tipo de datos


creadas con la DDL serán de longitud
variable.

DBNull.Value: no se sabe si el proveedor


asignará este campo con una columna
de longitud fija o variable.

IsFixedPrecisionScale bool true: el tipo de datos tiene una precisión


y escala fijas.

false: el tipo de datos no tiene una


precisión y escala fijas.

IsLong bool true: el tipo de datos contiene datos


muy largos; la definición de datos muy
largos es específica del proveedor.

false: el tipo de datos no contiene datos


muy largos.

IsNullable bool true: el tipo de datos acepta valores


NULL.

false: el tipo de datos no acepta valores


NULL.

DBNull.Value: no se sabe si el tipo de


datos acepta valores NULL.

IsSearchable bool true: el tipo de datos se puede utilizar


en una cláusula WHERE con cualquier
operador, excepto con el predicado LIKE.

false: el tipo de datos no se puede


utilizar en una cláusula WHERE con
ningún operador, excepto con el
predicado LIKE.

IsSearchableWithLike bool true: el tipo de datos se puede utilizar


con el predicado LIKE

false: el tipo de datos no se puede


utilizar con el predicado LIKE.

IsUnsigned bool true: el tipo de datos es sin signo.

false: el tipo de datos es con signo.

DBNull.Value: no es aplicable al tipo de


datos.
C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

MaximumScale short Si el indicador de tipos es un tipo


numérico, es el número máximo de
dígitos permitidos a la derecha del
separador decimal. De lo contrario, es
DBNull.Value.

MinimumScale short Si el indicador de tipos es un tipo


numérico, es el número mínimo de
dígitos permitidos a la derecha del
separador decimal. De lo contrario, es
DBNull.Value.

IsConcurrencyType bool true: la base de datos actualiza el tipo


de datos cada vez que cambia la fila y el
valor de la columna es diferente de
todos los valores anteriores.

false: la base de datos no actualiza el


tipo de datos cada vez que cambia la
fila.

DBNull.Value: la base de datos no


admite este tipo de datos.

IsLiteralSupported bool true: el tipo de datos se puede expresar


como un literal.

false: el tipo de datos no se puede


expresar como un literal.

LiteralPrefix string El prefijo aplicado a un literal dado.

LiteralSuffix string El sufijo aplicado a un literal dado.

NativeDataType String NativeDataType es una columna


específica de OLE DB para la exposición
del tipo de datos de OLE DB.

Restricciones
Esta colección de esquemas expone información acerca de las restricciones que admite el proveedor administrado
de .NET Framework que está actualmente conectado a la base de datos.

C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

CollectionName string El nombre de la colección a la que se


aplican estas restricciones.

RestrictionName string El nombre de la restricción en la


colección.

RestrictionDefault string ignorado.


C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

RestrictionNumber int La ubicación real de las restricciones de


colecciones en la que se encuentra esta
restricción en particular.

ReservedWords
Esta colección de esquemas expone información sobre las palabras que reserva la base de datos a la que está
conectado actualmente el proveedor de datos de .NET Framework.

C O L UM N N A M E DATAT Y P E DESC RIP C IÓ N

ReservedWord string Palabra reservada específica del


proveedor.

Vea también
Recuperar información del esquema de la base de datos
GetSchema y colecciones de esquema
Información general sobre ADO.NET
Colecciones de esquemas de SQL Server
23/10/2019 • 24 minutes to read • Edit Online

El proveedor de datos .NET Framework para SQL Server de Microsoft admite colecciones de esquemas
adicionales, además de las colecciones de esquemas comunes. Las colecciones de esquemas varían ligeramente
respecto de la versión de SQL Server que está utilizando. Para determinar la lista de colecciones de esquemas
admitidas, llame al método GetSchema sin argumentos o con el nombre de la colección de esquemas
"MetaDataCollections". Esto devolverá una DataTable con una lista de colecciones de esquemas admitidas, el
número de restricciones que admite cada una y el número de partes de identificador que emplean.

Bases de datos
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

database_name string Nombre de la base de datos.

DBID Int16 Id. de la base de datos.

create_date DateTime Fecha de creación de la base de datos.

Claves externas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

CONSTRAINT_CATALOG string Catálogo al que pertenece la restricción.

CONSTRAINT_SCHEMA string Esquema que contiene la restricción.

CONSTRAINT_NAME string Nombre.

TABLE_CATALOG string Nombre de la tabla de la que forma


parte la restricción.

TABLE_SCHEMA string Esquema que contiene la tabla.

TABLE_NAME string Nombre de la tabla

CONSTRAINT_TYPE string Tipo de restricción. Sólo se permite


"FOREIGN KEY".

IS_DEFERRABLE string Especifica si la restricción es aplazable.


Devuelve NO.

INITIALLY_DEFERRED string Especifica si la restricción es inicialmente


aplazable. Devuelve NO.

Índices
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

constraint_catalog string Catálogo al que pertenece el índice.

constraint_schema string Esquema que contiene el índice.

constraint_name string Nombre del índice.

table_catalog string Nombre de la tabla con la que está


asociado el índice.

table_schema string Esquema que contiene la tabla con la


que está asociado el índice.

table_name string Nombre de la tabla.

index_name string Nombre del índice.

Índices (SQL Server 2008)


Desde .NET Framework 3.5 Service Pack 1 y SQL Server 2008, se han agregado las columnas siguientes a la
colección de esquemas Indexes para admitir nuevas columnas de tipos espaciales, de secuencia de archivos y
dispersas. Estas columnas no se admiten en versiones anteriores de .NET Framework y SQL Server.

C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

type_desc string El tipo de índice debe ser uno de los


valores siguientes:

-MONTÓN
-AGRUPADO
-NONCLUSTERED
-XML
-ESPACIAL

IndexColumns
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

constraint_catalog string Catálogo al que pertenece el índice.

constraint_schema string Esquema que contiene el índice.

constraint_name string Nombre del índice.

table_catalog string Nombre de la tabla con la que está


asociado el índice.

table_schema string Esquema que contiene la tabla con la


que está asociado el índice.

table_name string Nombre de la tabla.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

column_name string Nombre de la columna con la que está


asociado el índice.

ordinal_position Int32 Posición del índice de columna.

KeyType Byte Tipo del objeto.

index_name string Nombre del índice.

Procedimientos
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

SPECIFIC_CATALOG string Nombre específico del catálogo.

SPECIFIC_SCHEMA string Nombre específico del esquema.

SPECIFIC_NAME string Nombre específico del catálogo.

ROUTINE_CATALOG string Catálogo al que pertenece el


procedimiento almacenado.

ROUTINE_SCHEMA string Esquema que contiene el procedimiento


almacenado.

ROUTINE_NAME string Nombre del procedimiento almacenado.

ROUTINE_TYPE string Devuelve PROCEDURE en el caso de los


procedimientos almacenados y
FUNCTION en el caso de las funciones.

CREATED DateTime Hora a la que se creó el procedimiento.

LAST_ALTERED DateTime La última vez que se modificó el


procedimiento.

Parámetros de procedimiento
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

SPECIFIC_CATALOG string Nombre de catálogo del procedimiento


del que forma parte este parámetro.

SPECIFIC_SCHEMA string Esquema que contiene el procedimiento


del que forma parte este parámetro.

SPECIFIC_NAME string Nombre del procedimiento del que


forma parte este parámetro.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

ORDINAL_POSITION Int32 Posición ordinal del parámetro que


empieza por 1. Para el valor devuelto de
un procedimiento, es un 0.

PARAMETER_MODE string Devuelve IN si es un parámetro de


entrada, OUT si es un parámetro de
salida e INOUT si es un parámetro de
entrada y salida.

IS_RESULT string Devuelve YES si indica que el resultado


del procedimiento es una función. De lo
contrario, devuelve NO.

AS_LOCATOR string Devuelve YES si se declara como


localizador. De lo contrario, devuelve
NO.

PARAMETER_NAME string Nombre del parámetro. NULL si


corresponde al valor devuelto de una
función.

DATA_TYPE string Tipo de datos suministrado por el


sistema.

CHARACTER_MAXIMUM_LENGTH Int32 Longitud máxima, en caracteres, de los


tipos de datos binarios o de caracteres.
De lo contrario, devuelve NULL.

CHARACTER_OCTET_LENGTH Int32 Longitud máxima, en bytes, de los tipos


de datos binarios o de caracteres. De lo
contrario, devuelve NULL.

COLLATION_CATALOG string Nombre de catálogo de la intercalación


del parámetro. Si no es uno de los tipos
de caracteres, devuelve NULL.

COLLATION_SCHEMA string Siempre devuelve NULL.

COLLATION_NAME string Nombre de la intercalación del


parámetro. Si no es uno de los tipos de
caracteres, devuelve NULL.

CHARACTER_SET_CATALOG string Nombre de catálogo del juego de


caracteres del parámetro. Si no es uno
de los tipos de caracteres, devuelve
NULL.

CHARACTER_SET_SCHEMA string Siempre devuelve NULL.

CHARACTER_SET_NAME string Nombre del juego de caracteres del


parámetro. Si no es uno de los tipos de
caracteres, devuelve NULL.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

NUMERIC_PRECISION Byte Precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_PRECISION_RADIX Int16 Base de precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_SCALE Int32 Escala de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

DATETIME_PRECISION Int16 Precisión en segundos decimales si el


tipo de parámetro es datetime o
smalldatetime. De lo contrario, devuelve
NULL.

INTERVAL_TYPE string NULL. Reservado por SQL Server para


uso futuro.

INTERVAL_PRECISION Int16 NULL. Reservado por SQL Server para


uso futuro.

Tablas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_CATALOG string Catálogo de la tabla.

TABLE_SCHEMA string Esquema que contiene la tabla.

TABLE_NAME string Nombre de la tabla.

TABLE_TYPE string Tipo de tabla. Puede ser VIEW o BASE


TABLE.

Columnas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_CATALOG string Catálogo de la tabla.

TABLE_SCHEMA string Esquema que contiene la tabla.

TABLE_NAME string Nombre de la tabla.

COLUMN_NAME string Nombre de columna.

ORDINAL_POSITION Int32 Número de identificación de la columna.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

COLUMN_DEFAULT string Valor predeterminado de la columna.

IS_NULLABLE string Capacidad de la columna de admitir


valores NULL. Si esta columna permite
NULL, devuelve YES. De lo contrario,
devuelve NO.

DATA_TYPE string Tipo de datos suministrado por el


sistema.

CHARACTER_MAXIMUM_LENGTH Int32 – Sql8, Int16 – Sql7 Longitud máxima, en caracteres, de los


datos binarios, datos de caracteres o
datos de texto e imágenes. De lo
contrario, devuelve NULL.

CHARACTER_OCTET_LENGTH Int32 – SQL8, Int16 – Sql7 Longitud máxima, en bytes, de los


datos binarios, datos de caracteres o
datos de texto e imágenes. De lo
contrario, devuelve NULL.

NUMERIC_PRECISION Byte sin signo Precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_PRECISION_RADIX Int16 Base de precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_SCALE Int32 Escala de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

DATETIME_PRECISION Int16 Código de subtipo para los tipos de


datos datetime y de intervalo SQL-92.
Para otros tipos de datos, devuelve
NULL.

CHARACTER_SET_CATALOG string Devuelve Master, para indicar la base


de datos en la que se encuentra el
juego de caracteres, si la columna es del
tipo de datos de caracteres o de texto.
De lo contrario, devuelve NULL.

CHARACTER_SET_SCHEMA string Siempre devuelve NULL.

CHARACTER_SET_NAME string Devuelve el nombre único del juego de


caracteres si esta columna es del tipo
de datos de caracteres o de texto. De lo
contrario, devuelve NULL.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

COLLATION_CATALOG string Devuelve Master, para indicar la base


de datos en la que se define la
intercalación, si la columna es del tipo
de datos de caracteres o de texto. De lo
contrario, esta columna es NULL.

Columns (SQL Server 2008)


A partir de .NET Framework versión 3.5 Service Pack 1 y SQL Server 2008, se han agregado las columnas
siguientes a la colección de esquemas Columns para admitir nuevas columnas de tipos espaciales, de secuencias
de archivos y dispersas. Estas columnas no se admiten en versiones anteriores de .NET Framework y SQL Server.

C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

IS_FILESTREAM string YES si la columna tiene el atributo


FILESTREAM.

NO si la columna no tiene el atributo


FILESTREAM.

IS_SPARSE string YES si la columna es una columna


dispersa.

NO si la columna no es una columna


dispersa.

IS_COLUMN_SET string YES si la columna es de conjunto de


columnas.

NO si la columna no es de conjunto de
columnas.

AllColumns (SQL Server 2008)


A partir de .NET Framework versión 3.5 Service Pack 1 y SQL Server 2008, se ha agregado la colección de
esquemas AllColumns para admitir columnas dispersas. AllColumns no se admite en versiones anteriores de .NET
Framework y SQL Server.
AllColumns tiene las mismas restricciones y el mismo esquema DataTable resultante que la colección de esquemas
Columns. La única diferencia es que AllColumns contiene columnas de conjunto de columnas que no se incluyen
en la colección de esquemas Columns. Estas columnas se describen en la siguiente tabla.

C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_CATALOG string Catálogo de la tabla.

TABLE_SCHEMA string Esquema que contiene la tabla.

TABLE_NAME string Nombre de la tabla.

COLUMN_NAME string Nombre de columna.

ORDINAL_POSITION Int32 Número de identificación de la columna.

COLUMN_DEFAULT string Valor predeterminado de la columna.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

IS_NULLABLE string Capacidad de la columna de admitir


valores NULL. Si esta columna permite
NULL, devuelve YES. De lo contrario,
devuelve NO.

DATA_TYPE string Tipo de datos suministrado por el


sistema.

CHARACTER_MAXIMUM_LENGTH Int32 Longitud máxima, en caracteres, de los


datos binarios, datos de caracteres o
datos de texto e imágenes. De lo
contrario, devuelve NULL.

CHARACTER_OCTET_LENGTH Int32 Longitud máxima, en bytes, de los


datos binarios, datos de caracteres o
datos de texto e imágenes. De lo
contrario, devuelve NULL.

NUMERIC_PRECISION Byte sin signo Precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_PRECISION_RADIX Int16 Base de precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_SCALE Int32 Escala de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

DATETIME_PRECISION Int16 Código de subtipo para los tipos de


datos datetime y de intervalo SQL-92.
Para otros tipos de datos, devuelve
NULL.

CHARACTER_SET_CATALOG string Devuelve Master, para indicar la base


de datos en la que se encuentra el
juego de caracteres, si la columna es del
tipo de datos de caracteres o de texto.
De lo contrario, devuelve NULL.

CHARACTER_SET_SCHEMA string Siempre devuelve NULL.

CHARACTER_SET_NAME string Devuelve el nombre único del juego de


caracteres si esta columna es del tipo
de datos de caracteres o de texto. De lo
contrario, devuelve NULL.

COLLATION_CATALOG string Devuelve Master, para indicar la base


de datos en la que se define la
intercalación, si la columna es del tipo
de datos de caracteres o de texto. De lo
contrario, esta columna es NULL.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

IS_FILESTREAM string YES si la columna tiene el atributo


FILESTREAM.

NO si la columna no tiene el atributo


FILESTREAM.

IS_SPARSE string YES si la columna es una columna


dispersa.

NO si la columna no es una columna


dispersa.

IS_COLUMN_SET string YES si la columna es de conjunto de


columnas.

NO si la columna no es de conjunto de
columnas.

ColumnSetColumns (SQL Server 2008)


A partir de .NET Framework versión 3.5 Service Pack 1 y SQL Server 2008, se ha agregado la colección de
esquemas ColumnSetColumns para admitir columnas dispersas. ColumnSetColumns no se admite en versiones
anteriores de .NET Framework y SQL Server. La colección de esquemas ColumnSetColumns devuelve el esquema
de todas las columnas de un conjunto de columnas. Estas columnas se describen en la siguiente tabla.

C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_CATALOG string Catálogo de la tabla.

TABLE_SCHEMA string Esquema que contiene la tabla.

TABLE_NAME string Nombre de la tabla.

COLUMN_NAME string Nombre de columna.

ORDINAL_POSITION Int32 Número de identificación de la columna.

COLUMN_DEFAULT string Valor predeterminado de la columna.

IS_NULLABLE string Capacidad de la columna de admitir


valores NULL. Si esta columna permite
NULL, devuelve YES. De lo contrario,
devuelve NO.

DATA_TYPE string Tipo de datos suministrado por el


sistema.

CHARACTER_MAXIMUM_LENGTH Int32 Longitud máxima, en caracteres, de los


datos binarios, datos de caracteres o
datos de texto e imágenes. De lo
contrario, devuelve NULL.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

CHARACTER_OCTET_LENGTH Int32 Longitud máxima, en bytes, de los


datos binarios, datos de caracteres o
datos de texto e imágenes. De lo
contrario, devuelve NULL.

NUMERIC_PRECISION Byte sin signo Precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_PRECISION_RADIX Int16 Base de precisión de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

NUMERIC_SCALE Int32 Escala de datos numéricos


aproximados, datos numéricos exactos,
datos enteros o datos monetarios. De
lo contrario, devuelve NULL.

DATETIME_PRECISION Int16 Código de subtipo para los tipos de


datos datetime y de intervalo SQL-92.
Para otros tipos de datos, devuelve
NULL.

CHARACTER_SET_CATALOG string Devuelve Master, para indicar la base


de datos en la que se encuentra el
juego de caracteres, si la columna es del
tipo de datos de caracteres o de texto.
De lo contrario, devuelve NULL.

CHARACTER_SET_SCHEMA string Siempre devuelve NULL.

CHARACTER_SET_NAME string Devuelve el nombre único del juego de


caracteres si esta columna es del tipo
de datos de caracteres o de texto. De lo
contrario, devuelve NULL.

COLLATION_CATALOG string Devuelve Master, para indicar la base


de datos en la que se define la
intercalación, si la columna es del tipo
de datos de caracteres o de texto. De lo
contrario, esta columna es NULL.

IS_FILESTREAM string YES si la columna tiene el atributo


FILESTREAM.

NO si la columna no tiene el atributo


FILESTREAM.

IS_SPARSE string YES si la columna es una columna


dispersa.

NO si la columna no es una columna


dispersa.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

IS_COLUMN_SET string YES si la columna es de conjunto de


columnas.

NO si la columna no es de conjunto de
columnas.

Usuarios
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

uid Int16 Id. de usuario, único en esta base de


datos. 1 es el propietario de la base de
datos.

user_name string Nombre de usuario o nombre del


grupo, único en esta base de datos.

createdate DateTime Fecha en que se agregó la cuenta.

updatedate DateTime Fecha en que se modificó la cuenta por


última vez.

Vistas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_CATALOG string Catálogo de la vista.

TABLE_SCHEMA string Esquema que contiene la vista.

TABLE_NAME string Nombre de la vista.

CHECK_OPTION string Tipo de WITH CHECK OPTION. Es


CASCADE si la vista original se creó
mediante WITH CHECK OPTION. De lo
contrario, se devuelve NONE.

IS_UPDATABLE string Especifica si la vista se puede actualizar.


Siempre devuelve NO.

ViewColumns
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

VIEW_CATALOG string Catálogo de la vista.

VIEW_SCHEMA string Esquema que contiene la vista.

VIEW_NAME string Nombre de la vista.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_CATALOG string Catálogo de la tabla asociada con esta


vista.

TABLE_SCHEMA string Esquema que contiene la tabla asociada


con esta vista.

TABLE_NAME string Nombre de la tabla asociada con esta


vista. Tabla base.

COLUMN_NAME string Nombre de columna.

UserDefinedTypes
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

assembly_name string Nombre de archivo del ensamblado.

udt_name string Nombre de clase del ensamblado.

version_major Object Número de versión principal.

version_minor Object Número de versión secundaria.

version_build Object Número de compilación.

version_revision Object Número de revisión.

culture_info Object La referencia cultural asociada con este


tipo definido por el usuario.

public_key Object La clave pública que utiliza este


ensamblado.

is_fixed_length Boolean Especifica si la longitud del tipo es


siempre igual que max_length.

max_length Int16 Longitud máxima del tipo en bytes.

Create_Date DateTime Fecha en que se creó o registró el


ensamblado.

Permission_set_desc string Nombre descriptivo del conjunto de


permisos y nivel de seguridad del
ensamblado.

Vea también
Recuperación de información del esquema de la base de datos
Información general sobre ADO.NET
Colecciones de esquemas de Oracle
23/10/2019 • 30 minutes to read • Edit Online

El proveedor de datos Microsoft .NET Framework para Oracle admite, además de las colecciones de esquemas
comunes, las siguientes colecciones de esquemas específicas:
Columnas
Índices
IndexColumns
Procedimientos
Secuencias
Sinónimos
Tablas
Usuarios
Vistas
Funciones
Paquetes
PackageBodies
Argumentos
UniqueKeys
PrimaryKeys
ForeignKeys
ForeignKeyColumns
ProcedureParameters

Columnas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario de la tabla, vista o clúster.

TABLE_NAME string Nombre de la tabla, vista o clúster.

COLUMN_NAME string Nombre de columna.

ID Decimal Número de secuencia de la columna


que se crea.

DATATYPE string Tipo de datos de la columna.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

LENGTH Decimal Longitud de la columna en bytes.

PRECISION Decimal Precisión decimal del tipo de datos


NUMBER; precisión binaria del tipo de
datos FLOAT; NULL para todos los
demás tipos de datos.

SCALE Decimal Dígitos a la derecha del separador


decimal en un número.

NULLABLE string Especifica si una columna permite


valores NULL. El valor es N si hay una
restricción NOT NULL en la columna o si
la columna forma parte de una
restricción PRIMARY KEY.

Índices
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del índice

INDEX_NAME string Nombre del índice.

INDEX_TYPE string Tipo de índice (NORMAL, BITMAP,


FUNCTION-BASED NORMAL,
FUNCTION-BASED BITMAP o
DOMAIN).

TABLE_OWNER string Propietario del objeto indizado.

TABLE_NAME string Nombre del objeto indizado.

TABLE_TYPE string Tipo del objeto indizado (por ejemplo,


TABLE, CLUSTER).

UNIQUENESS string Si el índice es UNIQUE o NONUNIQUE.

COMPRESSION string Si el índice está ENABLED o DISABLED.

PREFIX_LENGTH Decimal Número de columnas en el prefijo de la


clave de compresión.

TABLESPACE_NAME string Nombre del espacio de tabla que


contiene el índice.

INI_TRANS Decimal Número inicial de transacciones.

MAX_TRANS Decimal Número máximo de transacciones.

INITIAL_EXTENT Decimal Tamaño de la extensión inicial.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

NEXT_EXTENT Decimal Tamaño de las extensiones secundarias.

MIN_EXTENTS Decimal Número mínimo de extensiones


permitidas en el segmento.

MAX_EXTENTS Decimal Número máximo de extensiones


permitidas en el segmento.

PCT_INCREASE Decimal Porcentaje de aumento en el tamaño de


la extensión.

PCT_THRESHOLD Decimal Porcentaje de umbral del espacio de


bloque permitido por cada entrada de
índice.

INCLUDE_COLUMN Decimal Id. de columna de la última columna


que se incluirá en el índice de clave
principal (sin desbordamiento) de la
tabla organizada por índice. Esta
columna corresponde a la columna
COLUMN_ID de las vistas de diccionario
de datos *_TAB_COLUMNS.

FREELISTS Decimal Número de listas de liberaciones de


procesos asignadas a este segmento.

FREELIST_GROUPS Decimal Número de grupos de lista de


liberaciones asignados a este segmento.

PCT_FREE Decimal Porcentaje mínimo de espacio


disponible en un bloque.

LOGGING string Información de registro.

BLEVEL Decimal Nivel de b*-Tree: profundidad del índice


desde su bloque raíz a sus bloques hoja.
Una profundidad de 0 indica que el
bloque raíz y el bloque hoja son iguales.

LEAF_BLOCKS Decimal Número de bloques hoja del índice

DISTINCT_KEYS Decimal Número de valores indizados distintos.


En el caso de los índices que exigen las
restricciones UNIQUE y PRIMARY KEY,
este valor es igual al número de filas de
la tabla (USER_TABLES.NUM_ROWS).

AVG_LEAF_BLOCKS_PER_KEY Decimal Número medio de bloques hoja en el


que cada valor distinto en el índice
aparece redondeado al entero más
próximo. En los índices que exigen
restricciones UNIQUE y PRIMARY KEY,
este valor es siempre 1.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

AVG_DATA_BLOCKS_PER_KEY Decimal Número medio de bloques de datos en


la tabla a los que apunta un valor
distinto en el índice redondeado al
entero más cercano. Esta estadística es
el número medio de bloques de datos
que contienen filas con un valor
determinado para las columnas
indizadas.

CLUSTERING_FACTOR Decimal Indica la cantidad de orden de las filas


de la tabla según los valores del índice.

STATUS string Si un índice no particionado es VALID o


UNUSABLE.

NUM_ROWS Decimal Número de filas en el índice.

SAMPLE_SIZE Decimal Tamaño de la muestra utilizada para


analizar el índice.

LAST_ANALYZED DateTime Fecha de análisis más reciente de este


índice.

DEGREE string Número de subprocesos por instancia


para examinar el índice.

INSTANCES string Número de instancias a través de las


que se van a examinar los índices.

PARTITIONED string Indica si este índice tiene particiones ( |


sí no).

TEMPORARY string Si el índice se encuentra en una tabla


temporal.

GENERATED string Indica si el nombre del índice es


generado por el sistema|(Y N).

SECONDARY string Si el índice es un objeto secundario


creado por el método ODCIIndexCreate
del cartucho de datos Oracle9i (Y|N).

BUFFER_POOL string Nombre del grupo de búferes


predeterminado que se va a utilizar en
los bloques de índice.

USER_STATS string Si el usuario introdujo directamente las


estadísticas.

DURATION string Indica la duración de una tabla


temporal. 1) SYS $ SESSION: las filas se
conservan mientras dure la sesión, 2)
SYS $ TRANSACTION: las filas se
eliminan después de COMMIT, 3) null
para la tabla permanente.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

PCT_DIRECT_ACCESS Decimal En un índice secundario de una tabla


organizada por índice, el porcentaje de
filas con estimación VALID

ITYP_OWNER string En un índice de dominio, el propietario


del tipo de índice.

ITYP_NAME string En un índice de dominio, el nombre del


tipo de índice.

PARAMETERS string En un índice de dominio, la cadena de


parámetros.

GLOBAL_STATS string En índices particionados, indica si se


recopilaron las estadísticas mediante el
análisis del índice como un todo (YES) o
si se estimaron a partir de las
estadísticas de las particiones y
subparticiones del índice subyacente
(NO).

DOMIDX_STATUS string Refleja el estado del índice de dominio.


NULL: el índice especificado no es un
índice de dominio. VALID: el índice es un
índice de dominio válido.
IDXTYP_INVLD: el tipo de índice de este
índice de dominio no es válido.

DOMIDX_OPSTATUS string Refleja el estado de una operación que


se realizó en un índice de dominio:
NULL: el índice especificado no es un
índice de dominio. VALID: la operación
transcurrió sin errores. FAILED: la
operación produjo un error.

FUNCIDX_STATUS string Indica el estado de un índice basado en


función: NULL: este no es un índice
basado en función, ENABLED: el índice
basado en función está habilitado,
Disabled: el índice basado en función
está deshabilitado.

JOIN_INDEX string Indica si es o no un índice de


combinación.

IndexColumns
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

INDEX_OWNER string Propietario del índice.

INDEX_NAME string Nombre del índice.

TABLE_OWNER string Propietario de la tabla o clúster.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TABLE_NAME string Nombre de la tabla o clúster.

COLUMN_NAME string Nombre o atributo de columna de la


columna de tipo de objeto.

COLUMN_POSITION Decimal Posición de la columna o atributo en el


índice.

COLUMN_LENGTH Decimal Longitud indizada de la columna.

CHAR_LENGTH Decimal Longitud máxima del punto de código


de la columna.

DESCEND string Si la columna se ha clasificado en orden


descendente.

Procedimientos
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del objeto.

OBJECT_NAME string Nombre del objeto.

SUBOBJECT_NAME string Nombre del subobjeto (por ejemplo,


partición).

OBJECT_ID Decimal Número del objeto de diccionario del


objeto.

DATA_OBJECT_ID Decimal Número del objeto de diccionario del


segmento que contiene el objeto.

LAST_DDL_TIME DateTime Marca de tiempo de la última


modificación del objeto resultante de un
comando DDL (incluye concesiones y
revocaciones).

TIMESTAMP string Marca de tiempo de la especificación del


objeto (datos de caracteres).

STATUS string Estado del objeto (VALID, INVALID o


N/A).

TEMPORARY string Si el objeto es temporal (la sesión actual


solo podrá ver los datos colocados en el
objeto en sí).

GENERATED string ¿El nombre de este objeto fue generado


por el sistema? (Y | N).
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

SECONDARY string Indica si se trata de un objeto


secundario creado por el método
ODCIIndexCreate del cartucho de datos
Oracle9i | (Y N).

CREATED DateTime Fecha en que se creó el objeto.

Secuencias
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

SEQUENCE_OWNER string Nombre del propietario de la secuencia.

SEQUENCE_NAME string Nombre de la secuencia.

MIN_VALUE Decimal Valor mínimo de la secuencia.

MAX_VALUE Decimal Valor máximo de la secuencia.

INCREMENT_BY Decimal Valor en el que se incrementa la


secuencia.

CYCLE_FLAG string ¿Salta la secuencia al alcanzar el límite?

ORDER_FLAG string ¿Los números de secuencia se generan


en orden?

CACHE_SIZE Decimal Número de números de secuencia para


almacenar en caché.

LAST_NUMBER Decimal Último número de secuencia escrito en


el disco. Si una secuencia utiliza el
almacenamiento en caché, el número
escrito en el disco es el último número
colocado en la caché de secuencias. Este
número es probable que sea mayor que
el último número de secuencia que se
ha utilizado.

Sinónimos
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del sinónimo.

SYNONYM_NAME string Nombre del sinónimo.

TABLE_OWNER string Propietario del objeto al que hace


referencia el sinónimo.

TABLE_NAME string Nombre del objeto al que hace


referencia el sinónimo.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

DB_LINK string Nombre del vínculo de base de datos al


que se hace referencia, si lo hay.

Tablas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario de la tabla.

TABLE_NAME string Nombre de la tabla.

TYPE string Tipo de tabla.

Usuarios
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

NAME string Nombre del usuario.

ID Decimal Número de id. del usuario.

CREATEDATE DateTime Fecha de creación del usuario.

Vistas
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario de la vista.

VIEW_NAME string Nombre de la vista.

TEXT_LENGTH Decimal Longitud del texto de la vista.

TEXT string Texto de la vista.

TYPE_TEXT_LENGTH Decimal Longitud de la cláusula de tipo de la


vista con establecimiento de tipos.

TYPE_TEXT string Cláusula de tipo de la vista con


establecimiento de tipos.

OID_TEXT_LENGTH Decimal Longitud de la cláusula WITH OID de la


vista con establecimiento de tipos.

OID_TEXT string Cláusula WITH OID de la vista con


establecimiento de tipos.

VIEW_TYPE_OWNER string Propietario del tipo de la vista si ésta es


una vista con establecimiento de tipos.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

VIEW_TYPE string Tipo de la vista si esta es una vista con


establecimiento de tipos.

SUPERVIEW_NAME string Nombre de la supervista.

Funciones
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del objeto.

OBJECT_NAME string Nombre del objeto.

SUBOBJECT_NAME string Nombre del subobjeto (por ejemplo,


partición).

OBJECT_ID Decimal Número del objeto de diccionario del


objeto.

DATA_OBJECT_ID Decimal Número del objeto de diccionario del


segmento que contiene el objeto.

OBJECT_TYPE string Tipo del objeto.

CREATED DateTime Fecha en que se creó el objeto.

LAST_DDL_TIME DateTime Marca de tiempo de la última


modificación del objeto resultante de un
comando DDL (incluye concesiones y
revocaciones).

TIMESTAMP string Marca de tiempo de la especificación del


objeto (datos de caracteres).

STATUS string Estado del objeto (VALID, INVALID o


N/A).

TEMPORARY string Si el objeto es temporal (la sesión actual


solo podrá ver los datos colocados en el
objeto en sí).

GENERATED string ¿El nombre de este objeto fue generado


por el sistema? (Y | N).

SECONDARY string Indica si se trata de un objeto


secundario creado por el método
ODCIIndexCreate del cartucho de datos
Oracle9i | (Y N).

Paquetes
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del objeto.

OBJECT_NAME string Nombre del objeto.

SUBOBJECT_NAME string Nombre del subobjeto (por ejemplo,


partición).

OBJECT_ID Decimal Número del objeto de diccionario del


objeto.

DATA_OBJECT_ID Decimal Número del objeto de diccionario del


segmento que contiene el objeto.

LAST_DDL_TIME DateTime Marca de tiempo de la última


modificación del objeto resultante de un
comando DDL (incluye concesiones y
revocaciones).

TIMESTAMP string Marca de tiempo de la especificación del


objeto (datos de caracteres).

STATUS string Estado del objeto (VALID, INVALID o


N/A).

TEMPORARY string Si el objeto es temporal (la sesión actual


solo podrá ver los datos colocados en el
objeto en sí).

GENERATED string ¿El nombre de este objeto fue generado


por el sistema? (Y | N).

SECONDARY string Indica si se trata de un objeto


secundario creado por el método
ODCIIndexCreate del cartucho de datos
Oracle9i | (Y N).

CREATED DateTime Fecha en que se creó el objeto.

PackageBodies
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del objeto.

OBJECT_NAME string Nombre del objeto.

SUBOBJECT_NAME string Nombre del subobjeto (por ejemplo,


partición).

OBJECT_ID Decimal Número del objeto de diccionario del


objeto.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

DATA_OBJECT_ID Decimal Número del objeto de diccionario del


segmento que contiene el objeto.

LAST_DDL_TIME DateTime Marca de tiempo de la última


modificación del objeto resultante de un
comando DDL (incluye concesiones y
revocaciones).

TIMESTAMP string Marca de tiempo de la especificación del


objeto (datos de caracteres).

STATUS string Estado del objeto (VALID, INVALID o


N/A).

TEMPORARY string Si el objeto es temporal (la sesión actual


solo podrá ver los datos colocados en el
objeto en sí).

GENERATED string ¿El nombre de este objeto fue generado


por el sistema? (Y | N).

SECONDARY string Indica si se trata de un objeto


secundario creado por el método
ODCIIndexCreate del cartucho de datos
Oracle9i | (Y N).

CREATED DateTime Fecha en que se creó el objeto.

Argumentos
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Nombre del propietario del objeto.

PACKAGE_NAME string Nombre del paquete.

OBJECT_NAME string Nombre del procedimiento o función.

ARGUMENT_NAME string Nombre del argumento.

POSITION Decimal Posición en la lista de argumentos, o


NULL para el valor devuelto de la
función.

SEQUENCE Decimal Secuencia de argumentos, incluidos


todos los niveles de anidamiento.

DEFAULT_VALUE string Valor predeterminado del argumento.

DEFAULT_LENGTH Decimal Longitud del valor predeterminado del


argumento.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

IN_OUT string Dirección del argumento (IN, OUT o


IN/OUT).

DATA_LENGTH Decimal Longitud de la columna en bytes.

DATA_PRECISION Decimal Longitud en dígitos decimales


(NUMBER) o dígitos binarios (FLOAT).

DATA_SCALE Decimal Dígitos a la derecha del separador


decimal en un número.

DATA_TYPE string Tipo de datos del argumento.

UniqueKeys
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario de la definición de


restricción.

CONSTRAINT_NAME string Nombre de la definición de restricción.

TABLE_NAME string Nombre asociado con la tabla (o vista)


con la definición de restricción.

SEARCH_CONDITION string Texto de la condición de búsqueda de


una restricción de comprobación.

R_OWNER string Propietario de la tabla a la que se hace


referencia en una restricción referencial.

R_CONSTRAINT_NAME string Nombre de la definición de restricción


única de la tabla a la que se hace
referencia.

DELETE_RULE string Regla de eliminación de una restricción


referencial (CASCADE o NO ACTION).

STATUS string Estado de aplicación de la restricción


(ENABLED o DISABLED).

DEFERRABLE string Si la restricción es aplazable.

VALIDATED string Si todos los datos respetan la restricción


(VALIDATED o NOT VALIDATED).

GENERATED string Si el nombre de la restricción es un


nombre generado por el sistema o por
el usuario.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

BAD string Un valor YES indica que esta restricción


especifica un siglo de manera ambigua.
Para evitar los errores derivados de esta
ambigüedad, vuelva a escribir la
restricción mediante la función TO_DATE
con un año de cuatro cifras.

RELY string Si una restricción habilitada se exige o


no se exige.

LAST_CHANGE DateTime La última vez que se habilitó o


deshabilitó la restricción.

INDEX_OWNER string Nombre del usuario propietario del


índice.

INDEX_NAME string Nombre del índice

PrimaryKeys
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario de la definición de


restricción.

CONSTRAINT_NAME string Nombre de la definición de restricción.

TABLE_NAME string Nombre asociado con la tabla (o vista)


con la definición de restricción.

SEARCH_CONDITION string Texto de la condición de búsqueda de


una restricción de comprobación.

R_OWNER string Propietario de la tabla a la que se hace


referencia en una restricción referencial.

R_CONSTRAINT_NAME string Nombre de la definición de restricción


única de la tabla a la que se hace
referencia.

DELETE_RULE string Regla de eliminación de una restricción


referencial (CASCADE o NO ACTION).

STATUS string Estado de aplicación de la restricción


(ENABLED o DISABLED).

DEFERRABLE string Si la restricción es aplazable.

VALIDATED string Si todos los datos respetan la restricción


(VALIDATED o NOT VALIDATED).
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

GENERATED string Si el nombre de la restricción es un


nombre generado por el sistema o por
el usuario.

BAD string Un valor YES indica que esta restricción


especifica un siglo de manera ambigua.
Para evitar los errores derivados de esta
ambigüedad, vuelva a escribir la
restricción mediante la función TO_DATE
con un año de cuatro cifras.

RELY string Si una restricción habilitada se exige o


no se exige.

LAST_CHANGE DateTime La última vez que se habilitó o


deshabilitó la restricción.

INDEX_OWNER string Nombre del usuario propietario del


índice.

INDEX_NAME string Nombre del índice.

ForeignKeys
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

PRIMARY_KEY_CONSTRAINT_NAME string Nombre de la definición de restricción.

PRIMARY_KEY_OWNER string Propietario de la definición de


restricción.

PRIMARY_KEY_TABLE_NAME string Nombre asociado con la tabla (o vista)


con la definición de restricción.

FOREIGN_KEY_OWNER string Propietario de la definición de


restricción.

FOREIGN_KEY_CONSTRAINT_NAME string Nombre de la definición de restricción.

FOREIGN_KEY_TABLE_NAME string Nombre asociado con la tabla (o vista)


con la definición de restricción.

SEARCH_CONDITION string Texto de la condición de búsqueda de


una restricción de comprobación.

R_OWNER string Propietario de la tabla a la que se hace


referencia en una restricción referencial.

R_CONSTRAINT_NAME string Nombre de la definición de restricción


única de la tabla a la que se hace
referencia.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

DELETE_RULE string Regla de eliminación de una restricción


referencial (CASCADE o NO ACTION).

STATUS string Estado de aplicación de la restricción


(ENABLED o DISABLED).

VALIDATED string Si todos los datos respetan la restricción


(VALIDATED o NOT VALIDATED).

GENERATED string Si el nombre de la restricción es un


nombre generado por el sistema o por
el usuario.

RELY string Si una restricción habilitada se exige o


no se exige.

LAST_CHANGE DateTime La última vez que se habilitó o


deshabilitó la restricción.

INDEX_OWNER string Nombre del usuario propietario del


índice.

INDEX_NAME string Nombre del índice.

ForeignKeyColumns
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario de la definición de


restricción.

CONSTRAINT_NAME string Nombre de la definición de restricción.

TABLE_NAME string Nombre de la tabla con la definición de


restricción.

COLUMN_NAME string Nombre de la columna o atributo de la


columna de tipo de objeto especificada
en la definición de restricción.

POSITION Decimal Posición original de la columna o


atributo en la definición del objeto.

ProcedureParameters
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

OWNER string Propietario del objeto.

OBJECT_NAME string Nombre del procedimiento o función.


C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

PACKAGE_NAME string Nombre del procedimiento o función.

OBJECT_ID Decimal Número de objeto del objeto.

OVERLOAD string Identificador único de sobrecarga.

ARGUMENT_NAME string Nombre del argumento.

POSITION Decimal Posición en la lista de argumentos, o


NULL para un valor devuelto de la
función.

SEQUENCE Decimal Secuencia de argumentos, incluidos


todos los niveles de anidamiento.

DATA_LEVEL Decimal Profundidad de anidamiento del


argumento en los tipos compuestos

DATA_TYPE string Tipo de datos del argumento.

DEFAULT_VALUE string Valor predeterminado del argumento.

DEFAULT_LENGTH Decimal Longitud del valor predeterminado del


argumento.

IN_OUT string Dirección del argumento (IN, OUT o


IN/OUT).

DATA_LENGTH Decimal Longitud de la columna (en bytes).

DATA_PRECISION Decimal Longitud en dígitos decimales


(NUMBER) o dígitos binarios (FLOAT).

DATA_SCALE Decimal Dígitos a la derecha del separador


decimal en un número.

RADIX Decimal Base de argumento de un número.

CHARACTER_SET_NAME string Nombre del juego de caracteres del


argumento.

TYPE_OWNER string Propietario del tipo del argumento.

TYPE_NAME string Nombre del tipo del argumento. Si el


tipo es un tipo local de paquete (es
decir, se declara en una especificación de
paquete), entonces esta columna
muestra el nombre del paquete.

TYPE_SUBNAME string Solo es relevante para los tipos locales


de paquete. Muestra el nombre del tipo
declarado en el paquete identificado en
la columna TYPE_NAME.
C O L UM N A M E DATAT Y P E DESC RIP C IÓ N

TYPE_LINK string Solo es relevante para los tipos locales


de paquete cuando el paquete
identificado en la columna TYPE_NAME
es un paquete remoto. Esta columna
muestra el vínculo de base de datos
utilizado para hacer referencia al
paquete remoto.

PLS_TYPE string En argumentos numéricos, el nombre


del tipo PL/SQL del argumento. De lo
contrario, es NULL.

CHAR_LENGTH Decimal Límite de caracteres de los tipos de


datos de cadena.

CHAR_USED string Indica si el límite de bytes (B) o el límite


de caracteres (C) es oficial para la
cadena.

Vea también
Información general sobre ADO.NET
Colecciones de esquemas de ODBC
23/10/2019 • 5 minutes to read • Edit Online

En esta sección se describe la compatibilidad de las colecciones de esquemas con los controladores ODBC de
Microsoft SQL Server, Oracle y Microsoft Jet.

Controlador ODBC para Microsoft SQL Server


El controlador ODBC de Microsoft SQL Server admite las siguientes colecciones de esquemas específicas además
de las colecciones de esquemas comunes:
Tablas
Índices
Columnas
Procedimientos
ProcedureColumns
ProcedureParameters
Vistas
Tablas y vistas
C O L UM N A M E DATAT Y P E

TABLE_CAT string

TABLE_SCHEM string

TABLE_NAME string

TABLE_TYPE string

REMARKS string

Índices
C O L UM N A M E DATAT Y P E

TABLE_CAT string

TABLE_SCHEM string

TABLE_NAME string

NON_UNIQUE Int16

INDEX_QUALIFIER string
C O L UM N A M E DATAT Y P E

INDEX_NAME string

TYPE Int16

ORDINAL_POSITION Int16

COLUMN_NAME string

ASC_OR_DESC string

CARDINALITY Int32

PAGES Int32

FILTER_CONDITION string

SS_TYPE_SCHEMA string

SS_DATA_TYPE Byte

Columnas
C O L UM N A M E DATAT Y P E

TABLE_CAT string

TABLE_SCHEM string

TABLE_NAME string

COLUMN_NAME string

DATA_TYPE Int16

TYPE_NAME string

COLUMN_SIZE Int32

BUFFER_LENGTH Int32

DECIMAL_DIGITS Int16

NUM_PREC_RADIX Int16

NULLABLE Int16

REMARKS string

COLUMN_DEF string
C O L UM N A M E DATAT Y P E

SQL_DATA_TYPE Int16

SQL_DATETIME_SUB Int16

CHAR_OCTET_LENGTH Int32

ORDINAL_POSITION Int32

IS_NULLABLE string

SS_TYPE_CATALOG string

SS_TYPE_SCHEMA string

SS_DATA_TYPE Byte

Procedimientos
C O L UM N A M E DATAT Y P E

PROCEDURE_CAT string

PROCEDURE_SCHEM string

PROCEDURE_NAME string

NUM_INPUT_PARAMS Int32

NUM_OUTPUT_PARAMS Int32

NUM_RESULT_SETS Int32

REMARKS string

PROCEDURE_TYPE Int16

ProcedureColumns
C O L UM N A M E DATAT Y P E

PROCEDURE_CAT string

PROCEDURE_SCHEM string

PROCEDURE_NAME string

COLUMN_NAME string

COLUMN_TYPE Int16

DATA_TYPE Int16
C O L UM N A M E DATAT Y P E

TYPE_NAME string

COLUMN_SIZE Int32

BUFFER_LENGTH Int32

DECIMAL_DIGITS Int16

NUM_PREC_RADIX Int16

NULLABLE Int16

REMARKS string

COLUMN_DEF string

SQL_DATA_TYPE Int16

SQL_DATETIME_SUB Int16

CHAR_OCTET_LENGTH Int32

ORDINAL_POSITION Int32

IS_NULLABLE string

SS_TYPE_CATALOG string

SS_TYPE_SCHEMA string

SS_DATA_TYPE Byte

ProcedureParameters
C O L UM N A M E DATAT Y P E

PROCEDURE_CAT string

PROCEDURE_SCHEM string

PROCEDURE_NAME string

COLUMN_NAME string

COLUMN_TYPE Int16

DATA_TYPE Int16

TYPE_NAME string
C O L UM N A M E DATAT Y P E

COLUMN_SIZE Int32

BUFFER_LENGTH Int32

DECIMAL_DIGITS Int16

NUM_PREC_RADIX Int16

NULLABLE Int16

REMARKS string

COLUMN_DEF string

SQL_DATA_TYPE Int16

SQL_DATETIME_SUB Int16

CHAR_OCTET_LENGTH Int32

ORDINAL_POSITION Int32

IS_NULLABLE string

SS_TYPE_CATALOG string

SS_TYPE_SCHEMA string

SS_DATA_TYPE Byte

Controlador ODBC para Oracle de Microsoft


El controlador ODBC de Oracle Microsoft SQL Server admite las siguientes colecciones de esquemas específicas
además de las colecciones de esquemas comunes:
Tablas
Columnas
Procedimientos
ProcedureColumns
ProcedureParameters
Vistas
Índices
Tablas y vistas
C O L UM N A M E DATAT Y P E

TABLE_QUALIFIER string

TABLE_OWNER string

TABLE_NAME string

TABLE_TYPE string

REMARKS string

Columnas
C O L UM N A M E DATAT Y P E

TABLE_QUALIFIER string

TABLE_OWNER string

TABLE_NAME string

COLUMN_NAME string

DATA_TYPE Int16

TYPE_NAME string

PRECISION Int32

LENGTH Int32

SCALE Int16

RADIX Int16

NULLABLE Int16

REMARKS string

ORDINAL_POSITION Int32

Procedimientos
C O L UM N A M E DATAT Y P E

PROCEDURE_QUALIFIER string

PROCEDURE_OWNER string

PROCEDURE_NAME string

NUM_INPUT_PARAMS Int16
C O L UM N A M E DATAT Y P E

NUM_OUTPUT_PARAMS Int16

NUM_RESULT_SETS Int16

REMARKS string

PROCEDURE_TYPE Int16

ProcedureColumns
C O L UM N A M E DATAT Y P E

PROCEDURE_QUALIFIER string

PROCEDURE_OWNER string

PROCEDURE_NAME string

COLUMN_NAME string

COLUMN_TYPE Int16

DATA_TYPE Int16

TYPE_NAME string

PRECISION Int32

LENGTH Int32

SCALE Int16

RADIX Int16

NULLABLE Int16

REMARKS string

OVERLOAD Int32

ORDINAL_POSITION Int32

Controlador ODBC de Microsoft para Jet


El controlador ODBC de Microsoft para Jet admite, además de las colecciones de esquemas comunes, las siguientes
colecciones de esquemas específicas:
Tablas
Índices
Columnas
Procedimientos
ProcedureColumns
ProcedureParameters
Vistas
Tablas y vistas
C O L UM N A M E DATAT Y P E

TABLE_QUALIFIER string

TABLE_OWNER string

TABLE_NAME string

TABLE_TYPE string

REMARKS string

Columnas
C O L UM N A M E DATAT Y P E

TABLE_QUALIFIER string

TABLE_OWNER string

TABLE_NAME string

COLUMN_NAME string

DATA_TYPE Int16

TYPE_NAME string

PRECISION Int32

LENGTH Int32

SCALE Int16

RADIX Int16

NULLABLE Int16

REMARKS string

ORDINAL_POSITION Int32

Procedimientos
C O L UM N A M E DATAT Y P E

PROCEDURE_QUALIFIER string

PROCEDURE_OWNER string

PROCEDURE_NAME string

NUM_INPUT_PARAMS Int16

NUM_OUTPUT_PARAMS Int16

NUM_RESULT_SETS Int16

REMARKS string

PROCEDURE_TYPE Int16

ProcedureColumns
C O L UM N A M E DATAT Y P E

PROCEDURE_QUALIFIER string

PROCEDURE_OWNER string

PROCEDURE_NAME string

COLUMN_NAME string

COLUMN_TYPE Int16

DATA_TYPE Int16

TYPE_NAME string

PRECISION Int32

LENGTH Int32

SCALE Int16

RADIX Int16

NULLABLE Int16

REMARKS string

OVERLOAD Int32

ORDINAL_POSITION Int32

ProcedureParameters
C O L UM N A M E DATAT Y P E

PROCEDURE_CAT string

PROCEDURE_SCHEM string

PROCEDURE_NAME string

COLUMN_NAME string

COLUMN_TYPE Int16

DATA_TYPE Int16

TYPE_NAME string

COLUMN_SIZE Int32

BUFFER_LENGTH Int32

DECIMAL_DIGITS Int16

NUM_PREC_RADIX Int16

NULLABLE Int16

REMARKS string

COLUMN_DEF string

SQL_DATA_TYPE Int16

SQL_DATETIME_SUB Int16

CHAR_OCTET_LENGTH Int32

ORDINAL_POSITION Int32

IS_NULLABLE string

Vea también
Información general sobre ADO.NET
Colecciones de esquemas de OLE DB
23/10/2019 • 7 minutes to read • Edit Online

En esta sección se describe la compatibilidad de las colecciones de esquemas con los proveedores OLE DB de
Microsoft SQL Server, Oracle y Microsoft Jet.

Proveedor OLE DB para Microsoft SQL Server


El controlador de OLE DB de Microsoft SQL Server admite las siguientes colecciones de esquemas específicas
además de las colecciones de esquemas comunes:
Tablas
Columnas
Procedimientos
ProcedureParameters
Catálogo
Índices
Tablas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

TABLE_TYPE string

TABLE_GUID Guid

DESCRIPTION string

TABLE_PROPID Int64

DATE_CREATED DateTime

DATE_MODIFIED DateTime

Columnas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string
C O L UM N A M E DATAT Y P E

TABLE_NAME string

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

ORDINAL_POSITION Int64

COLUMN_HASDEFAULT Boolean

COLUMN_DEFAULT string

COLUMN_FLAGS Int64

IS_NULLABLE Boolean

DATA_TYPE Int32

TYPE_GUID Guid

CHARACTER_MAXIMUM_LENGTH Int64

CHARACTER_OCTET_LENGTH Int64

NUMERIC_PRECISION Int32

NUMERIC_SCALE Int16

DATETIME_PRECISION Int64

CHARACTER_SET_CATALOG string

CHARACTER_SET_SCHEMA string

CHARACTER_SET_NAME string

COLLATION_CATALOG string

COLLATION_SCHEMA string

COLLATION_NAME string

DOMAIN_CATALOG string

DOMAIN_SCHEMA string

DOMAIN_NAME string
C O L UM N A M E DATAT Y P E

DESCRIPTION string

COLUMN_LCID Int32

COLUMN_COMPFLAGS Int32

COLUMN_SORTID Int32

COLUMN_TDSCOLLATION Byte[]

IS_COMPUTED Boolean

Procedimientos
C O L UM N A M E DATAT Y P E

PROCEDURE_CATALOG string

PROCEDURE_SCHEMA string

PROCEDURE_NAME string

PROCEDURE_TYPE Int16

PROCEDURE_DEFINITION string

DESCRIPTION string

DATE_CREATED DateTime

DATE_MODIFIED DateTime

ProcedureParameters
C O L UM N A M E DATAT Y P E

PROCEDURE_CATALOG string

PROCEDURE_SCHEMA string

PROCEDURE_NAME string

PARAMETER_NAME string

ORDINAL_POSITION Int32

PARAMETER_TYPE Int32

PARAMETER_HASDEFAULT Boolean

PARAMETER_DEFAULT string
C O L UM N A M E DATAT Y P E

IS_NULLABLE Boolean

DATA_TYPE Int32

CHARACTER_MAXIMUM_LENGTH Int64

CHARACTER_OCTET_LENGTH Int64

NUMERIC_PRECISION Int32

NUMERIC_SCALE Int16

DESCRIPTION string

TYPE_NAME string

LOCAL_TYPE_NAME string

Catálogo
C O L UM N A M E DATAT Y P E

CATALOG_NAME string

DESCRIPTION string

Índices
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

INDEX_CATALOG string

INDEX_SCHEMA string

INDEX_NAME string

PRIMARY_KEY Boolean

UNIQUE Boolean

CLUSTERED Boolean

TYPE Int32

FILL_FACTOR Int32
C O L UM N A M E DATAT Y P E

INITIAL_SIZE Int32

NULLS Int32

SORT_BOOKMARKS Boolean

AUTO_UPDATE Boolean

NULL_COLLATION Int32

ORDINAL_POSITION Int64

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

COLLATION Int16

CARDINALITY Decimal

PAGES Int32

FILTER_CONDITION string

INTEGRATED Boolean

Proveedor OLE DB de Microsoft para Oracle


El controlador OLE DB de Microsoft para Oracle admite, además de las colecciones de esquemas comunes, las
siguientes colecciones de esquemas específicas:
Tablas
Columnas
Procedimientos
ProcedureColumns
ProcedureParameters
Vistas
Índices
Tablas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string
C O L UM N A M E DATAT Y P E

TABLE_SCHEMA string

TABLE_NAME string

TABLE_TYPE string

TABLE_GUID Guid

DESCRIPTION string

TABLE_PROPID Int64

DATE_CREATED DateTime

DATE_MODIFIED DateTime

Columnas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

ORDINAL_POSITION Int64

COLUMN_HASDEFAULT Boolean

COLUMN_DEFAULT string

COLUMN_FLAGS Int64

IS_NULLABLE Boolean

DATA_TYPE Int32

TYPE_GUID Guid

CHARACTER_MAXIMUM_LENGTH Int64

CHARACTER_OCTET_LENGTH Int64
C O L UM N A M E DATAT Y P E

NUMERIC_PRECISION Int32

NUMERIC_SCALE Int16

DATETIME_PRECISION Int64

CHARACTER_SET_CATALOG string

CHARACTER_SET_SCHEMA string

CHARACTER_SET_NAME string

COLLATION_CATALOG string

COLLATION_SCHEMA string

COLLATION_NAME string

DOMAIN_CATALOG string

DOMAIN_SCHEMA string

DOMAIN_NAME string

DESCRIPTION string

Procedimientos
C O L UM N A M E DATAT Y P E

PROCEDURE_CATALOG string

PROCEDURE_SCHEMA string

PROCEDURE_NAME string

PROCEDURE_TYPE Int16

PROCEDURE_DEFINITION string

DESCRIPTION string

DATE_CREATED DateTime

DATE_MODIFIED DateTime

ProcedureColumns
C O L UM N A M E DATAT Y P E

PROCEDURE_CATALOG string
C O L UM N A M E DATAT Y P E

PROCEDURE_SCHEMA string

PROCEDURE_NAME string

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

ROWSET_NUMBER Int64

ORDINAL_POSITION Int64

IS_NULLABLE Boolean

DATA_TYPE Int32

TYPE_GUID Guid

CHARACTER_MAXIMUM_LENGTH Int64

CHARACTER_OCTET_LENGTH Int64

NUMERIC_PRECISION Int32

NUMERIC_SCALE Int16

DESCRIPTION string

OVERLOAD Int16

Vistas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

VIEW_DEFINITION string

CHECK_OPTION Boolean

IS_UPDATABLE Boolean

DESCRIPTION string
C O L UM N A M E DATAT Y P E

DATE_CREATED DateTime

DATE_MODIFIED DateTime

Índices
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

INDEX_CATALOG string

INDEX_SCHEMA string

INDEX_NAME string

PRIMARY_KEY Boolean

UNIQUE Boolean

CLUSTERED Boolean

TYPE Int32

FILL_FACTOR Int32

INITIAL_SIZE Int32

NULLS Int32

SORT_BOOKMARKS Boolean

AUTO_UPDATE Boolean

NULL_COLLATION Int32

ORDINAL_POSITION Int64

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

COLLATION Int16
C O L UM N A M E DATAT Y P E

CARDINALITY Decimal

PAGES Int32

FILTER_CONDITION string

INTEGRATED Boolean

Proveedor OLE DB de Microsoft para Jet


El controlador OLE DB de Microsoft para Jet admite, además de las colecciones de esquemas comunes, las
siguientes colecciones de esquemas específicas:
Tablas
Columnas
Procedimientos
Vistas
Índices
Tablas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

TABLE_TYPE string

TABLE_GUID Guid

DESCRIPTION string

TABLE_PROPID Int64

DATE_CREATED DateTime

DATE_MODIFIED DateTime

Columnas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string
C O L UM N A M E DATAT Y P E

TABLE_NAME string

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

ORDINAL_POSITION Int64

COLUMN_HASDEFAULT Boolean

COLUMN_DEFAULT string

COLUMN_FLAGS Int64

IS_NULLABLE Boolean

DATA_TYPE Int32

TYPE_GUID Guid

CHARACTER_MAXIMUM_LENGTH Int64

CHARACTER_OCTET_LENGTH Int64

NUMERIC_PRECISION Int32

NUMERIC_SCALE Int16

DATETIME_PRECISION Int64

CHARACTER_SET_CATALOG string

CHARACTER_SET_SCHEMA string

CHARACTER_SET_NAME string

COLLATION_CATALOG string

COLLATION_SCHEMA string

COLLATION_NAME string

DOMAIN_CATALOG string

DOMAIN_SCHEMA string

DOMAIN_NAME string
C O L UM N A M E DATAT Y P E

DESCRIPTION string

Procedimientos
C O L UM N A M E DATAT Y P E

PROCEDURE_CATALOG string

PROCEDURE_SCHEMA string

PROCEDURE_NAME string

PROCEDURE_TYPE Int16

PROCEDURE_DEFINITION string

DESCRIPTION string

DATE_CREATED DateTime

DATE_MODIFIED DateTime

Vistas
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string

TABLE_NAME string

VIEW_DEFINITION string

CHECK_OPTION Boolean

IS_UPDATABLE Boolean

DESCRIPTION string

DATE_CREATED DateTime

DATE_MODIFIED DateTime

Índices
C O L UM N A M E DATAT Y P E

TABLE_CATALOG string

TABLE_SCHEMA string
C O L UM N A M E DATAT Y P E

TABLE_NAME string

INDEX_CATALOG string

INDEX_SCHEMA string

INDEX_NAME string

PRIMARY_KEY Boolean

UNIQUE Boolean

CLUSTERED Boolean

TYPE Int32

FILL_FACTOR Int32

INITIAL_SIZE Int32

NULLS Int32

SORT_BOOKMARKS Boolean

AUTO_UPDATE Boolean

NULL_COLLATION Int32

ORDINAL_POSITION Int64

COLUMN_NAME string

COLUMN_GUID Guid

COLUMN_PROPID Int64

COLLATION Int16

CARDINALITY Decimal

PAGES Int32

FILTER_CONDITION string

INTEGRATED Boolean

Vea también
Información general sobre ADO.NET
Objetos DbProviderFactory
23/10/2019 • 2 minutes to read • Edit Online

El espacio de nombres System.Data.Common proporciona clases para la creación de instancias DbProviderFactory


que permiten trabajar con orígenes de datos específicos. Cuando crea una instancia DbProviderFactory y le pasa
información acerca del proveedor de datos, la instancia DbProviderFactory puede determinar el objeto
fuertemente tipado correcto que debe devolver en función de la información que se ha proporcionado.
Comenzando con la versión 4 de .NET Framework , los proveedores de datos como System.Data.Odbc,
System.Data.OleDb, System.Data.SqlClient y System.Data.OracleClient ya no aparecen en el archivo
machine.config; sin embargo, sí aparecerán los proveedores personalizados.

En esta sección
Información general sobre el modelo de fábrica
Proporciona una introducción al patrón de diseño de generadores y a la interfaz de programación.
Obtención de un objeto DbProviderFactory
Muestra cómo enumerar los proveedores de datos instalados y cómo crear una clase DbConnection a partir de
DbProviderFactory .

DbConnection, DbCommand y DbException


Muestra cómo crear objetos DbCommand y DbDataReader, y cómo controlar los errores de datos con
DbException.
Modificación de datos con un objeto DbDataAdapter
Muestra cómo usar un objeto DbCommandBuilder con DbDataAdapter para recuperar y modificar datos.

Vea también
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
Información general sobre el modelo de fábrica
08/01/2020 • 2 minutes to read • Edit Online

ADO.NET 2.0 incorporó nuevas clases base en el espacio de nombres System.Data.Common. No se pueden crear
instancias directamente de las clases base debido a que son abstractas. Entre ellas se incluyen DbConnection,
DbCommand y DbDataAdapter, y las utilizan los proveedores de datos .NET Framework como
System.Data.SqlClient y System.Data.OleDb. El aumento de clases base simplifica la agregación de funcionalidades
a los proveedores de datos .NET Framework sin necesidad de crear nuevas interfaces.
ADO.NET 2.0 incorporó también clases base abstractas, con permiten al desarrollador escribir código de acceso a
datos genérico que no dependa de un proveedor de datos concreto.

Patrón de diseño de generador


El modelo de programación para la escritura de código independiente del proveedor se basa en el uso del patrón
de diseño de generador utiliza una única API para tener acceso a las bases de datos de varios proveedores. Este
patrón tiene un nombre muy apropiado, dado que exige el uso de un objeto especializado solamente para crear
otros objetos, de forma muy parecida a una fábrica del mundo real. Para obtener una descripción más detallada del
patrón de diseño de generador, vea escribir código genérico de acceso a datos en ASP.NET 2,0 y ADO.NET 2,0.
A partir de ADO.NET 2.0, la clase DbProviderFactories proporciona métodos static (o Shared en Visual Basic)
para crear una instancia de DbProviderFactory. La instancia devuelve luego un objeto fuertemente tipado basado
en la información sobre el proveedor y la cadena de conexión suministrada en tiempo de ejecución.

Vea también
Obtención de un objeto DbProviderFactory
DbConnection, DbCommand y DbException
Modificación de datos con un objeto DbDataAdapter
Información general sobre ADO.NET
Obtener un objeto DbProviderFactory
21/03/2020 • 9 minutes to read • Edit Online

El proceso de obtención de DbProviderFactory implica pasar información sobre un proveedor de datos a la clase
DbProviderFactories. En función de esta información, el método GetFactory crea un generador del proveedor
fuertemente tipado. Por ejemplo, para crear SqlClientFactory, se puede pasar a GetFactory una cadena con el
nombre de proveedor especificado como "System.Data.SqlClient". La otra sobrecarga de GetFactory toma
DataRow. Una vez creado el generador del proveedor, se pueden utilizar sus métodos para crear objetos
adicionales. Entre los métodos de SqlClientFactory se incluyen CreateConnection, CreateCommand y
CreateDataAdapter.

NOTE
Las clases OracleClientFactory, OdbcFactory y OleDbFactory de .NET Framework también proporcionan una funcionalidad
similar.

Registrar DbProviderFactories
Cada proveedor de datos de .NET Framework que admite una clase basada en fábrica registra información de
configuración en la sección DbProviderFactories del archivo machine.config del equipo local. El siguiente
fragmento del archivo de configuración muestra la sintaxis y formato de System.Data.SqlClient.

<system.data>
<DbProviderFactories>
<add name="SqlClient Data Provider"
invariant="System.Data.SqlClient"
description=".Net Framework Data Provider for SqlServer"
type="System.Data.SqlClient.SqlClientFactory, System.Data,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
/>
</DbProviderFactories>
</system.data>

El atributo invariable identifica el proveedor de datos subyacente. Esta sintaxis de nomenclatura en tres partes
también se utiliza al crear un nuevo generador y para identificar al proveedor en un archivo de configuración de la
aplicación, de manera que el nombre de proveedor, junto con sus cadenas de conexión asociadas, se puedan
recuperar en tiempo de conexión.

Recuperar información del proveedor


Se puede recuperar información acerca de todos los proveedores de datos instalados en el equipo local utilizando
el método GetFactoryClasses. Devuelve un DataTable dbProviderFactories con nombre que contiene las
columnas descritas en la tabla siguiente.

ÍN DIC E DE C O L UM N A N O M B RE DE L A C O L UM N A SA L IDA DE E JEM P LO DESC RIP C IÓ N

0 Nombre Proveedor de datos Nombre legible del


SqlClient proveedor de datos
ÍN DIC E DE C O L UM N A N O M B RE DE L A C O L UM N A SA L IDA DE E JEM P LO DESC RIP C IÓ N

1 Descripción Proveedor de datos .NET Descripción legible del


Framework para SqlServer proveedor de datos

2 InvariantName System.Data.SqlClient Nombre que se puede


utilizar mediante
programación para hacer
referencia al proveedor de
datos

3 AssemblyQualifiedName System.Data.SqlClient.SqlClie Nombre completo de la


ntFactory, System.Data, clase de generador, el cual
Version=2.0.0.0, tiene información suficiente
Culture=neutral, para crear la instancia del
PublicKeyToken=b77a5c561 objeto
934e089

Este se puede utilizar para permitir a un usuario seleccionar un DataRow en tiempo de ejecución. El
DataTable
DataRow seleccionado se puede pasar al método GetFactory para crear un DbProviderFactory fuertemente tipado.
Un DataRow seleccionado se puede pasar al método GetFactory para crear el objeto DbProviderFactory deseado.

Enumerar las clases de generador del proveedor instaladas


Este ejemplo muestra cómo el método GetFactoryClasses devuelve un DataTable con información sobre los
proveedores instalados. El código recorre en iteración cada fila de DataTable y muestra información de todos los
proveedores instalados en la ventana de la consola.

// This example assumes a reference to System.Data.Common.


static DataTable GetProviderFactoryClasses()
{
// Retrieve the installed providers and factories.
DataTable table = DbProviderFactories.GetFactoryClasses();

// Display each row and column value.


foreach (DataRow row in table.Rows)
{
foreach (DataColumn column in table.Columns)
{
Console.WriteLine(row[column]);
}
}
return table;
}
' This example assumes a reference to System.Data.Common.
Private Shared Function GetProviderFactoryClasses() As DataTable

' Retrieve the installed providers and factories.


Dim table As DataTable = DbProviderFactories.GetFactoryClasses()

' Display each row and column value.


Dim row As DataRow
Dim column As DataColumn
For Each row In table.Rows
For Each column In table.Columns
Console.WriteLine(row(column))
Next
Next

Return table
End Function

Utilizar archivos de configuración de la aplicación para almacenar


información del generador
El patrón de diseño utilizado para trabajar con fábricas implica almacenar información de proveedor y cadena de
conexión en un archivo de configuración de aplicación, como app.config para una aplicación de Windows y
web.config para una aplicación de ASP.NET.
El siguiente fragmento de archivo de configuración muestra cómo guardar dos cadenas de conexión, una
denominada "NorthwindSQL", para la conexión a la base de datos Northwind de SQL Server, y la otra denominada
"NorthwindAccess", para una conexión a la base de datos Northwind de Access/Jet. El nombre invariable se
utiliza para el atributo providerName.

<configuration>
<connectionStrings>
<clear/>
<add name="NorthwindSQL"
providerName="System.Data.SqlClient"
connectionString=
"Data Source=MSSQL1;Initial Catalog=Northwind;Integrated Security=true"
/>

<add name="NorthwindAccess"
providerName="System.Data.OleDb"
connectionString=
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Data\Northwind.mdb;"
/>
</connectionStrings>
</configuration>

Recuperar una cadena de conexión por el nombre de proveedor


Para crear un generador del proveedor, debe proporcionar una cadena de conexión así como el nombre del
proveedor. En este ejemplo se muestra cómo recuperar una cadena de conexión de un archivo de configuración de
aplicación pasando el nombre del proveedor en el formato invariable "System.Data.ProviderName". El código
recorre en iteración ConnectionStringSettingsCollection. Devuelve correctamente ProviderName; en caso
contrario, null ( Nothing en Visual Basic). Si existen varias entradas para un proveedor, se devuelve la primera
que se encuentra. Para obtener más información y ejemplos de recuperación de cadenas de conexión de archivos
de configuración, vea Cadenas de conexión y archivosde configuración .
NOTE
Se necesita una referencia a System.Configuration.dll para que se ejecute el código.

// Retrieve a connection string by specifying the providerName.


// Assumes one connection string per provider in the config file.
static string GetConnectionStringByProvider(string providerName)
{
// Return null on failure.
string returnValue = null;

// Get the collection of connection strings.


ConnectionStringSettingsCollection settings =
ConfigurationManager.ConnectionStrings;

// Walk through the collection and return the first


// connection string matching the providerName.
if (settings != null)
{
foreach (ConnectionStringSettings cs in settings)
{
if (cs.ProviderName == providerName)
returnValue = cs.ConnectionString;
break;
}
}
return returnValue;
}

' Retrieve a connection string by specifying the providerName.


' Assumes one connection string per provider in the config file.
Private Shared Function GetConnectionStringByProvider( _
ByVal providerName As String) As String

'Return Nothing on failure.


Dim returnValue As String = Nothing

' Get the collection of connection strings.


Dim settings As ConnectionStringSettingsCollection = _
ConfigurationManager.ConnectionStrings

' Walk through the collection and return the first


' connection string matching the providerName.
If Not settings Is Nothing Then
For Each cs As ConnectionStringSettings In settings
If cs.ProviderName = providerName Then
returnValue = cs.ConnectionString
Exit For
End If
Next
End If

Return returnValue
End Function

Crear DbProviderFactory y DbConnection


En este ejemplo se DbProviderFactory muestra DbConnection cómo crear un y object pasándole el nombre del
proveedor en el formato "System.Data.ProviderName" y una cadena de conexión. Se devuelve correctamente un
objeto DbConnection ; null ( Nothing en Visual Basic), en caso de producirse un error.
El código obtiene DbProviderFactory llamando a GetFactory. Entonces, el método CreateConnection crea el objeto
DbConnection y la propiedad ConnectionString se establece en la cadena de conexión.

// Given a provider name and connection string,


// create the DbProviderFactory and DbConnection.
// Returns a DbConnection on success; null on failure.
static DbConnection CreateDbConnection(
string providerName, string connectionString)
{
// Assume failure.
DbConnection connection = null;

// Create the DbProviderFactory and DbConnection.


if (connectionString != null)
{
try
{
DbProviderFactory factory =
DbProviderFactories.GetFactory(providerName);

connection = factory.CreateConnection();
connection.ConnectionString = connectionString;
}
catch (Exception ex)
{
// Set the connection to null if it was created.
if (connection != null)
{
connection = null;
}
Console.WriteLine(ex.Message);
}
}
// Return the connection.
return connection;
}
' Given a provider, create a DbProviderFactory and DbConnection.
' Returns a DbConnection on success; Nothing on failure.
Private Shared Function CreateDbConnection( _
ByVal providerName As String, ByVal connectionString As String) _
As DbConnection

' Assume failure.


Dim connection As DbConnection = Nothing

' Create the DbProviderFactory and DbConnection.


If Not connectionString Is Nothing Then
Try
Dim factory As DbProviderFactory = _
DbProviderFactories.GetFactory(providerName)

connection = factory.CreateConnection()
connection.ConnectionString = connectionString

Catch ex As Exception
' Set the connection to Nothing if it was created.
If Not connection Is Nothing Then
connection = Nothing
End If
Console.WriteLine(ex.Message)
End Try
End If

' Return the connection.


Return connection
End Function

Consulte también
Objetos DbProviderFactory
Cadenas de conexión
Utilizar las clases Configuration
Información general de ADO.NET
DbConnection, DbCommand y DbException
23/10/2019 • 5 minutes to read • Edit Online

Después de crear un objeto DbProviderFactory y un objeto DbConnection, puede trabajar con comandos y
lectores de datos para recuperar datos del origen de datos.

Ejemplo de recuperación de datos


En este ejemplo se utiliza un objeto DbConnection como argumento. Se crea un objeto DbCommand para
seleccionar datos de la tabla Categories mediante el establecimiento de CommandText en una instrucción SELECT
de SQL. El código asume que la tabla Categories existe en el origen de datos. Se abre la conexión y los datos se
recuperan mediante DbDataReader.

// Takes a DbConnection and creates a DbCommand to retrieve data


// from the Categories table by executing a DbDataReader.
static void DbCommandSelect(DbConnection connection)
{
string queryString =
"SELECT CategoryID, CategoryName FROM Categories";

// Check for valid DbConnection.


if (connection != null)
{
using (connection)
{
try
{
// Create the command.
DbCommand command = connection.CreateCommand();
command.CommandText = queryString;
command.CommandType = CommandType.Text;

// Open the connection.


connection.Open();

// Retrieve the data.


DbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("{0}. {1}", reader[0], reader[1]);
}
}

catch (Exception ex)


{
Console.WriteLine("Exception.Message: {0}", ex.Message);
}
}
}
else
{
Console.WriteLine("Failed: DbConnection is null.");
}
}
' Takes a DbConnection and creates a DbCommand to retrieve data
' from the Categories table by executing a DbDataReader.
Private Shared Sub DbCommandSelect(ByVal connection As DbConnection)

Dim queryString As String = _


"SELECT CategoryID, CategoryName FROM Categories"

' Check for valid DbConnection.


If Not connection Is Nothing Then
Using connection
Try
' Create the command.
Dim command As DbCommand = connection.CreateCommand()
command.CommandText = queryString
command.CommandType = CommandType.Text

' Open the connection.


connection.Open()

' Retrieve the data.


Dim reader As DbDataReader = command.ExecuteReader()
Do While reader.Read()
Console.WriteLine("{0}. {1}", reader(0), reader(1))
Loop

Catch ex As Exception
Console.WriteLine("Exception.Message: {0}", ex.Message)
End Try
End Using
Else
Console.WriteLine("Failed: DbConnection is Nothing.")
End If
End Sub

Ejemplo de ejecución de un comando


En este ejemplo se utiliza un objeto DbConnection como argumento. Si el objeto DbConnection es válido, se abre la
conexión y, a continuación, se crea y se ejecuta un objeto DbCommand. CommandText se establece en una
instrucción INSERT de SQL que realiza una inserción en la tabla Categories de la base de datos Northwind. El
código a sume que la base de datos Northwind existe en el origen de datos y que la sintaxis de SQL usada en la
instrucción INSERT es válida en el proveedor especificado. El bloque de código DbException controla los errores
que se producen en el origen de datos y el bloque Exception controla todas las demás excepciones.
// Takes a DbConnection, creates and executes a DbCommand.
// Assumes SQL INSERT syntax is supported by provider.
static void ExecuteDbCommand(DbConnection connection)
{
// Check for valid DbConnection object.
if (connection != null)
{
using (connection)
{
try
{
// Open the connection.
connection.Open();

// Create and execute the DbCommand.


DbCommand command = connection.CreateCommand();
command.CommandText =
"INSERT INTO Categories (CategoryName) VALUES ('Low Carb')";
int rows = command.ExecuteNonQuery();

// Display number of rows inserted.


Console.WriteLine("Inserted {0} rows.", rows);
}
// Handle data errors.
catch (DbException exDb)
{
Console.WriteLine("DbException.GetType: {0}", exDb.GetType());
Console.WriteLine("DbException.Source: {0}", exDb.Source);
Console.WriteLine("DbException.ErrorCode: {0}", exDb.ErrorCode);
Console.WriteLine("DbException.Message: {0}", exDb.Message);
}
// Handle all other exceptions.
catch (Exception ex)
{
Console.WriteLine("Exception.Message: {0}", ex.Message);
}
}
}
else
{
Console.WriteLine("Failed: DbConnection is null.");
}
}
' Takes a DbConnection and executes an INSERT statement.
' Assumes SQL INSERT syntax is supported by provider.
Private Shared Sub ExecuteDbCommand(ByVal connection As DbConnection)

' Check for valid DbConnection object.


If Not connection Is Nothing Then
Using connection
Try
' Open the connection.
connection.Open()

' Create and execute the DbCommand.


Dim command As DbCommand = connection.CreateCommand()
command.CommandText = _
"INSERT INTO Categories (CategoryName) VALUES ('Low Carb')"
Dim rows As Integer = command.ExecuteNonQuery()

' Display number of rows inserted.


Console.WriteLine("Inserted {0} rows.", rows)

' Handle data errors.


Catch exDb As DbException
Console.WriteLine("DbException.GetType: {0}", exDb.GetType())
Console.WriteLine("DbException.Source: {0}", exDb.Source)
Console.WriteLine("DbException.ErrorCode: {0}", exDb.ErrorCode)
Console.WriteLine("DbException.Message: {0}", exDb.Message)

' Handle all other exceptions.


Catch ex As Exception
Console.WriteLine("Exception.Message: {0}", ex.Message)
End Try
End Using
Else
Console.WriteLine("Failed: DbConnection is Nothing.")
End If
End Sub

Controlar errores de datos con DbException


La clase DbException es la clase base para todas las excepciones que se producen por cuenta del origen de datos.
Puede usarla en el código de control de excepciones para controlar las excepciones que inician diferentes
proveedores sin necesidad de hacer referencia a una clase de excepción específica. En el fragmento de código
siguiente se muestra cómo utilizar DbExceptionpara mostrar la información de error que devuelve el origen de
datos mediante las propiedades GetType, Source, ErrorCode y Message. El resultado mostrará el tipo de error, el
origen que indica el nombre de proveedor, un código de error y el mensaje asociado al error.

Try
' Do work here.
Catch ex As DbException
' Display information about the exception.
Console.WriteLine("GetType: {0}", ex.GetType())
Console.WriteLine("Source: {0}", ex.Source)
Console.WriteLine("ErrorCode: {0}", ex.ErrorCode)
Console.WriteLine("Message: {0}", ex.Message)
Finally
' Perform cleanup here.
End Try
try
{
// Do work here.
}
catch (DbException ex)
{
// Display information about the exception.
Console.WriteLine("GetType: {0}", ex.GetType());
Console.WriteLine("Source: {0}", ex.Source);
Console.WriteLine("ErrorCode: {0}", ex.ErrorCode);
Console.WriteLine("Message: {0}", ex.Message);
}
finally
{
// Perform cleanup here.
}

Vea también
Objetos DbProviderFactory
Obtención de un objeto DbProviderFactory
Modificación de datos con un objeto DbDataAdapter
Información general sobre ADO.NET
Modificar datos con un objeto DbDataAdapter
23/10/2019 • 7 minutes to read • Edit Online

El método CreateDataAdapter de un objeto DbProviderFactory proporciona un objeto DbDataAdapter


fuertemente tipado en el proveedor de datos subyacente especificado en el momento de crear el generador. Se
puede utilizar DbCommandBuilder para crear comandos para insertar, actualizar y eliminar datos desde DataSet a
un origen de datos.

Recuperación de datos con un DbDataAdapter


Este ejemplo muestra cómo crear un DbDataAdapter fuertemente tipado basado en el nombre de proveedor y la
cadena de conexión. El código utiliza el método CreateConnection de DbProviderFactory para crear DbConnection.
A continuación, el código utiliza el método CreateCommand para crear un DbCommand para seleccionar datos
estableciendo sus propiedades CommandText y Connection . Finalmente, el código crear un objeto DbDataAdapter
mediante el método CreateDataAdapter y establece su propiedad SelectCommand . El método Fill de
DbDataAdapter carga los datos en DataTable.
static void CreateDataAdapter(string providerName, string connectionString)
{
try
{
// Create the DbProviderFactory and DbConnection.
DbProviderFactory factory =
DbProviderFactories.GetFactory(providerName);

DbConnection connection = factory.CreateConnection();


connection.ConnectionString = connectionString;

using (connection)
{
// Define the query.
string queryString =
"SELECT CategoryName FROM Categories";

// Create the DbCommand.


DbCommand command = factory.CreateCommand();
command.CommandText = queryString;
command.Connection = connection;

// Create the DbDataAdapter.


DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = command;

// Fill the DataTable.


DataTable table = new DataTable();
adapter.Fill(table);

// Display each row and column value.


foreach (DataRow row in table.Rows)
{
foreach (DataColumn column in table.Columns)
{
Console.WriteLine(row[column]);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Shared Sub CreateDataAdapter(ByVal providerName As String, _
ByVal connectionString As String)

' Create the DbProviderFactory and DbConnection.


Try
Dim factory As DbProviderFactory = _
DbProviderFactories.GetFactory(providerName)

Dim connection As DbConnection = _


factory.CreateConnection()
connection.ConnectionString = connectionString
Using connection

' Define the query.


Dim queryString As String = _
"SELECT CategoryName FROM Categories"

'Create the DbCommand.


Dim command As DbCommand = _
factory.CreateCommand()
command.CommandText = queryString
command.Connection = connection

' Create the DbDataAdapter.


Dim adapter As DbDataAdapter = _
factory.CreateDataAdapter()
adapter.SelectCommand = command

' Fill the DataTable


Dim table As New DataTable
adapter.Fill(table)

'Display each row and column value.


Dim row As DataRow
Dim column As DataColumn
For Each row In table.Rows
For Each column In table.Columns
Console.WriteLine(row(column))
Next
Next
End Using

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub

Modificar datos con un objeto DbDataAdapter


Este ejemplo muestra cómo modificar datos en DataTable utilizando DbDataAdapter mediante
DbCommandBuilder para generar los comandos necesarios para actualizar datos en el origen de datos. El
SelectCommand de DbDataAdapter se configura para recuperar el CustomerID y el CompanyName desde la tabla
Customers. El método GetInsertCommand se utiliza para establecer la propiedad InsertCommand, el método
GetUpdateCommand para establecer la propiedad UpdateCommand y el método GetDeleteCommand para
establecer la propiedad DeleteCommand. El código agrega una nueva fila a la tabla Customers y actualiza el
origen de datos. A continuación, el código localiza la fila agregada buscando en CustomerID, que es la clave
principal definida para la tabla Customers. Cambia el CompanyName y actualiza el origen de datos. Finalmente, el
código elimina la fila.

static void CreateDataAdapter(string providerName, string connectionString)


{
try
{
{
// Create the DbProviderFactory and DbConnection.
DbProviderFactory factory =
DbProviderFactories.GetFactory(providerName);

DbConnection connection = factory.CreateConnection();


connection.ConnectionString = connectionString;

using (connection)
{
// Define the query.
string queryString =
"SELECT CustomerID, CompanyName FROM Customers";

// Create the select command.


DbCommand command = factory.CreateCommand();
command.CommandText = queryString;
command.Connection = connection;

// Create the DbDataAdapter.


DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = command;

// Create the DbCommandBuilder.


DbCommandBuilder builder = factory.CreateCommandBuilder();
builder.DataAdapter = adapter;

// Get the insert, update and delete commands.


adapter.InsertCommand = builder.GetInsertCommand();
adapter.UpdateCommand = builder.GetUpdateCommand();
adapter.DeleteCommand = builder.GetDeleteCommand();

// Display the CommandText for each command.


Console.WriteLine("InsertCommand: {0}",
adapter.InsertCommand.CommandText);
Console.WriteLine("UpdateCommand: {0}",
adapter.UpdateCommand.CommandText);
Console.WriteLine("DeleteCommand: {0}",
adapter.DeleteCommand.CommandText);

// Fill the DataTable.


DataTable table = new DataTable();
adapter.Fill(table);

// Insert a new row.


DataRow newRow = table.NewRow();
newRow["CustomerID"] = "XYZZZ";
newRow["CompanyName"] = "XYZ Company";
table.Rows.Add(newRow);

adapter.Update(table);

// Display rows after insert.


Console.WriteLine();
Console.WriteLine("----List All Rows-----");
foreach (DataRow row in table.Rows)
{
Console.WriteLine("{0} {1}", row[0], row[1]);
}
Console.WriteLine("----After Insert-----");

// Edit an existing row.


DataRow[] editRow = table.Select("CustomerID = 'XYZZZ'");
editRow[0]["CompanyName"] = "XYZ Corporation";

adapter.Update(table);

// Display rows after update.


Console.WriteLine();
foreach (DataRow row in table.Rows)
foreach (DataRow row in table.Rows)
{
Console.WriteLine("{0} {1}", row[0], row[1]);
}
Console.WriteLine("----After Update-----");

// Delete a row.
DataRow[] deleteRow = table.Select("CustomerID = 'XYZZZ'");
foreach (DataRow row in deleteRow)
{
row.Delete();
}

adapter.Update(table);

// Display rows after delete.


Console.WriteLine();
foreach (DataRow row in table.Rows)
{
Console.WriteLine("{0} {1}", row[0], row[1]);
}
Console.WriteLine("----After Delete-----");
Console.WriteLine("Customer XYZZZ was deleted.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

Shared Sub CreateDataAdapter(ByVal providerName As String, _


ByVal connectionString As String)

' Create the DbProviderFactory and DbConnection.


Try
Dim factory As DbProviderFactory = _
DbProviderFactories.GetFactory(providerName)

Dim connection As DbConnection = _


factory.CreateConnection()
connection.ConnectionString = connectionString

Using connection
' Define the query.
Dim queryString As String = _
"SELECT CustomerID, CompanyName FROM Customers"

'Create the select command.


Dim command As DbCommand = _
factory.CreateCommand()
command.CommandText = queryString
command.Connection = connection

' Create the DbDataAdapter.


Dim adapter As DbDataAdapter = _
factory.CreateDataAdapter()
adapter.SelectCommand = command

' Create the DbCommandBuilder.


Dim builder As DbCommandBuilder = _
factory.CreateCommandBuilder()
builder.DataAdapter = adapter

' Get the insert, update and delete commands.


adapter.InsertCommand = builder.GetInsertCommand()
adapter.UpdateCommand = builder.GetUpdateCommand()
adapter.DeleteCommand = builder.GetDeleteCommand()
adapter.DeleteCommand = builder.GetDeleteCommand()

' Display the CommandText for each command.


Console.WriteLine("InsertCommand: {0}", _
adapter.InsertCommand.CommandText)
Console.WriteLine("UpdateCommand: {0}", _
adapter.UpdateCommand.CommandText)
Console.WriteLine("DeleteCommand: {0}", _
adapter.DeleteCommand.CommandText)

' Fill the DataTable


Dim table As New DataTable
adapter.Fill(table)

' Insert a new row.


Dim newRow As DataRow = table.NewRow
newRow("CustomerID") = "XYZZZ"
newRow("CompanyName") = "XYZ Company"
table.Rows.Add(newRow)

adapter.Update(table)

' Display rows after insert.


Console.WriteLine()
Console.WriteLine("----List All Rows-----")
Dim row As DataRow
For Each row In table.Rows
Console.WriteLine("{0} {1}", row(0), row(1))
Next
Console.WriteLine("----After Insert-----")

' Edit an existing row.


Dim editRow() As DataRow = _
table.Select("CustomerID = 'XYZZZ'")
editRow(0)("CompanyName") = "XYZ Corporation"

adapter.Update(table)

' Display rows after update.


Console.WriteLine()
For Each row In table.Rows
Console.WriteLine("{0} {1}", row(0), row(1))
Next
Console.WriteLine("----After Update-----")

' Delete a row.


Dim deleteRow() As DataRow = _
table.Select("CustomerID = 'XYZZZ'")
For Each row In deleteRow
row.Delete()
Next

adapter.Update(table)
table.AcceptChanges()

' Display each row and column value after delete.


Console.WriteLine()
For Each row In table.Rows
Console.WriteLine("{0} {1}", row(0), row(1))
Next
Console.WriteLine("----After Delete-----")
Console.WriteLine("Customer XYZZZ was deleted.")
End Using

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Control de parámetros
Los proveedores de datos .NET Framework administran la asignación de nombres y la especificación de
parámetros y marcadores de posición de parámetros de diferente forma. Esta sintaxis se adapta a un origen de
datos específico, como se describe en la tabla siguiente.

P RO VEEDO R DE DATO S SIN TA XIS DE N O M EN C L AT URA DE PA RÁ M ET RO S

SqlClient Usa parámetros con nombre, con el formato @


nombreDeParámetro.

OracleClient Usa parámetros con nombre, con el formato :


nombreDeParámetro (o nombreDeParámetro).

OleDb Usa marcadores de parámetro de posición, indicados por un


signo de interrogación ( ? ).

Odbc Usa marcadores de parámetro de posición, indicados por un


signo de interrogación ( ? ).

El modelo de generador no es útil para la creación de objetos DbCommand y DbDataAdapter con parámetros. Es
necesario bifurcar en el código para crear parámetros que se adapten al proveedor de datos.

IMPORTANT
Por razones de seguridad, no es recomendable evitar totalmente parámetros específicos del proveedor mediante el uso de
concatenación de cadenas para construir instrucciones SQL directas. El uso de cadenas de concatenación en lugar de
parámetros expone a la aplicación a ataques por inyección de SQL.

Vea también
Objetos DbProviderFactory
Obtención de un objeto DbProviderFactory
DbConnection, DbCommand y DbException
Información general sobre ADO.NET
Traza de datos en ADO.NET
05/02/2020 • 6 minutes to read • Edit Online

ADO.NET incluye la funcionalidad de seguimiento de datos integrada que admiten los proveedores de datos de
.NET para SQL Server, Oracle, OLE DB y ODBC, así como los DataSetde ADO.NET y los protocolos de red de SQL
Server.
Las llamadas API de acceso a datos de traza ayudan a diagnosticar los siguientes problemas:
Falta de coincidencia de esquemas entre el programa cliente y la base de datos.
Inoperabilidad de la base de datos o problemas con las bibliotecas de red.
SQL incorrecto, tanto si está incluido en el código como si lo ha generado una aplicación.
Lógica de programación incorrecta.
Problemas derivados de la interacción entre varios componentes de ADO.NET o entre ADO.NET y sus
propios componentes.
Para garantizar la compatibilidad entre diferentes tecnologías de traza, éste es ampliable, de manera que un
programador puede realizar un seguimiento de un problema en cualquier nivel de la pila de la aplicación. A pesar
de que el seguimiento no es una característica exclusiva de ADO.NET, los proveedores de Microsoft aprovechan la
ventaja de el seguimiento generalizada y las API de instrumental.
Para obtener más información sobre cómo establecer y configurar el seguimiento administrado en ADO.NET, vea
seguimiento de acceso a datos.

Tener acceso a información de diagnóstico en el registro de eventos


extendidos
En el proveedor de datos de .NET Framework para SQL Server, el seguimiento de acceso a datos (seguimiento de
acceso a datos) se ha actualizado para facilitar la correlación de eventos de cliente con información de diagnóstico,
como errores de conexión, desde el búfer de anillo de Conectividad del servidor y la información de rendimiento
de la aplicación en el registro de eventos extendidos. Para obtener información sobre cómo leer el registro de
eventos extendidos, vea ver datos de sesiones de eventos.
Para las operaciones de conexión, ADO.NET enviará un identificador de conexión de cliente. Si se produce un error
en la conexión, puede tener acceso al búfer de anillo de conectividad (solución de problemas de conectividad en
SQL Server 2008 con el búfer de anillo de conectividad) y buscar el campo de ClientConnectionID y obtener
información de diagnóstico sobre el error de conexión. Los identificadores de conexión de cliente inician sesión el
búfer de anillo solo si se produce un error. (Si se produce un error en una conexión antes de enviar el paquete de
inicio de sesión previo, no se generará un identificador de conexión de cliente). El identificador de conexión de
cliente es un GUID de 16 bytes. También puede buscar el identificador de la conexión de cliente en la salida de
destino de los eventos extendidos si la acción client_connection_id se agregó a los eventos en una sesión de
eventos extendidos. Puede habilitar el seguimiento de acceso a datos y volver a ejecutar el comando de conexión y
observar el campo ClientConnectionID en el seguimiento de acceso a datos, si necesita ayuda adicional de
diagnóstico del controlador del cliente.
Puede obtener el identificador de la conexión de cliente mediante programación con la propiedad
SqlConnection.ClientConnectionID .

ClientConnectionID está disponible para un objeto SqlConnection que establezca correctamente una conexión. Si
se produce un error en un intento de conexión, ClientConnectionID puede estar disponible a través de
SqlException.ToString .

ADO.NET también envía un identificador de actividad específico del subproceso El ID. de actividad se captura en las
sesiones de eventos extendidos si las sesiones se inician con la opción de TRACK_CAUSALITY habilitada. Para los
problemas de rendimiento con una conexión activa, puede obtener el identificador de actividad del seguimiento de
acceso a datos del cliente (campo ActivityID ) y después buscar el identificador de actividad en la salida de los
eventos extendidos. El identificador de actividad en los eventos extendidos es un GUID de 16 bytes (no es el
mismo GUID que el identificador de conexión de cliente) anexado con un número de secuencia de cuatro bytes. El
número de secuencia representa el orden de una solicitud dentro de un subproceso e indica el orden relativo del
lote y las instrucciones RPC para el subproceso. ActivityID se envía actualmente opcionalmente para
instrucciones por lotes de SQL y solicitudes RPC cuando el seguimiento de acceso a datos está habilitado y el
decimoctavo bit de la palabra de configuración de seguimiento de acceso a datos está activado.
A continuación se muestra un ejemplo que utiliza Transact-SQL para iniciar una sesión de Extended Events que se
almacenará en un búfer de anillo y registrará el ID. de actividad enviado desde un cliente en operaciones RPC y por
lotes.

create event session MySession on server


add event connectivity_ring_buffer_recorded,
add event sql_statement_starting (action (client_connection_id)),
add event sql_statement_completed (action (client_connection_id)),
add event rpc_starting (action (client_connection_id)),
add event rpc_completed (action (client_connection_id))
add target ring_buffer with (track_causality=on)

Vea también
Network Tracing in the .NET Framework (Seguimiento de red en .NET Framework)
Seguimiento e instrumentación de aplicaciones
Información general sobre ADO.NET
Contadores de rendimiento de ADO.NET
21/03/2020 • 12 minutes to read • Edit Online

ADO.NET 2.0 incorporó la compatibilidad expandida para los contadores de rendimiento que incluye la
compatibilidad tanto con System.Data.SqlClient como con System.Data.OracleClient. Los contadores de
rendimiento System.Data.SqlClient que estaban disponibles en las versiones anteriores de ADO.NET están en
desuso y se han sustituido por los nuevos contadores de rendimiento que se describen aquí. Puede utilizar los
contadores de rendimiento de ADO.NET para supervisar el estado de su aplicación y los recursos de conexión que
emplea. Los contadores de rendimiento se pueden controlar con el Monitor de rendimiento de Windows pero
también se puede tener acceso a ellos mediante programación usando la clase PerformanceCounter del espacio
de nombres System.Diagnostics.

Contadores de rendimiento disponibles


Actualmente hay disponibles 14 contadores de rendimiento para System.Data.SqlClient y
System.Data.OracleClient, tal y como se describe en la siguiente tabla. Tenga en cuenta que los nombres de los
contadores individuales no se localizan en las versiones regionales de Microsoft .NET Framework.

C O N TA DO R DE REN DIM IEN TO DESC RIP C IÓ N

HardConnectsPerSecond El número de conexiones por segundo que se establecen con


un servidor de bases de datos.

HardDisconnectsPerSecond El número de desconexiones por segundo que se producen


con un servidor de bases de datos.

NumberOfActiveConnectionPoolGroups El número de conjuntos de grupos de conexiones únicas que


están activos. Este contador depende del número de cadenas
de conexión única que haya en el AppDomain.

NumberOfActiveConnectionPools El número total de grupos de conexiones.

NumberOfActiveConnections El número de conexiones activas que se están utilizando


actualmente. Nota: Este contador de rendimiento no está
habilitado de forma predeterminada. Para habilitar este
contador de rendimiento, consulte Activación de contadores
no predeterminados.

NumberOfFreeConnections El número de conexiones que se pueden utilizar en los grupos


de conexiones. Nota: Este contador de rendimiento no está
habilitado de forma predeterminada. Para habilitar este
contador de rendimiento, consulte Activación de contadores
no predeterminados.

NumberOfInactiveConnectionPoolGroups El número de conjuntos de grupos de conexiones únicas que


están marcados para ser eliminados. Este contador depende
del número de cadenas de conexión única que haya en el
AppDomain.

NumberOfInactiveConnectionPools El número de grupos de conexiones inactivas que no han


tenido ninguna actividad recientemente y que están a la
espera de ser eliminadas.
C O N TA DO R DE REN DIM IEN TO DESC RIP C IÓ N

NumberOfNonPooledConnections El número de conexiones activas que no están agrupadas.

NumberOfPooledConnections El número de conexiones activas que administra la


infraestructura de agrupación de conexiones.

NumberOfReclaimedConnections El número de conexiones que se han reclamado a través de la


recolección de elementos no utilizados si la aplicación no
llamó a Close o Dispose . Si no se cierran o eliminan
explícitamente las conexiones, el rendimiento se verá
perjudicado.

NumberOfStasisConnections El número de conexiones que están actualmente en espera de


que se finalice una acción y que por lo tanto no pueden ser
utilizadas por la aplicación.

SoftConnectsPerSecond El número de conexiones activas que se están extrayendo del


grupo de conexiones. Nota: Este contador de rendimiento no
está habilitado de forma predeterminada. Para habilitar este
contador de rendimiento, consulte Activación de contadores
no predeterminados.

SoftDisconnectsPerSecond El número de conexiones activas que se devuelven al grupo


de conexiones. Nota: Este contador de rendimiento no está
habilitado de forma predeterminada. Para habilitar este
contador de rendimiento, consulte Activación de contadores
no predeterminados.

Conjuntos de grupos de conexiones y grupos de conexiones


Si utiliza la autenticación de Windows (seguridad integrada) debe supervisar los contadores de rendimiento
NumberOfActiveConnectionPoolGroups y NumberOfActiveConnectionPools . El motivo es que los conjuntos de grupos
de conexiones se corresponden con cadenas de conexión única. Si se usa seguridad integrada, los grupos de
conexiones se asignan a cadenas de conexión y además crean grupos diferentes para cada identidad de Windows.
Por ejemplo, si Alfredo y Julia, los dos dentro del mismo AppDomain, utilizan la cadena de conexión
"Data Source=MySqlServer;Integrated Security=true" , se crea un conjunto de grupos de conexiones para la cadena
de conexión y dos grupos adicionales, uno para Alfredo y otro para Julia. Si John y Martha usan una cadena de
"Data Source=MySqlServer;User Id=lowPrivUser;Password=Strong?Password" conexión con un inicio de sesión de SQL
Server idéntico, , solo se crea un único grupo para la identidad lowPrivUser.
Activar contadores desactivados de forma predeterminada
Los contadores de rendimiento NumberOfFreeConnections , NumberOfActiveConnections , SoftDisconnectsPerSecond y
SoftConnectsPerSecond están desactivados de forma predeterminada. Agregue la siguiente información al archivo
de configuración de la aplicación para habilitarlos:

<system.diagnostics>
<switches>
<add name="ConnectionPoolPerformanceCounterDetail"
value="4"/>
</switches>
</system.diagnostics>

Recuperar los valores de los contadores de rendimiento


La siguiente aplicación de consola muestra cómo recuperar valores de los contadores de rendimiento en su
aplicación. Las conexiones deben estar abiertas y activas para que se devuelva información de todos los
contadores de rendimiento de ADO.NET.

NOTE
En este ejemplo se utiliza la base de datos AdventureWorks de ejemplo incluida con SQL Server. Las cadenas de conexión
que se incluyen en el código de ejemplo presuponen que la base de datos está instalada y disponible en el equipo local con
el nombre de instancia SqlExpress y que se han creado inicios de sesión de SQL Server que coinciden con los
proporcionados en las cadenas de conexión. Quizá deba habilitar inicios de sesión de SQL Server si su servidor se ha
configurado usando la configuración de seguridad predeterminada, que solo admite la autenticación de Windows. Modifique
las cadenas de conexión según sea necesario para su entorno.

Ejemplo

Option Explicit On
Option Strict On

Imports System.Data.SqlClient
Imports System.Diagnostics
Imports System.Runtime.InteropServices

Class Program

Private PerfCounters(9) As PerformanceCounter


Private connection As SqlConnection = New SqlConnection

Public Shared Sub Main()


Dim prog As Program = New Program
' Open a connection and create the performance counters.
prog.connection.ConnectionString = _
GetIntegratedSecurityConnectionString()
prog.SetUpPerformanceCounters()
Console.WriteLine("Available Performance Counters:")

' Create the connections and display the results.


prog.CreateConnections()
Console.WriteLine("Press Enter to finish.")
Console.ReadLine()
End Sub

Private Sub CreateConnections()


' List the Performance counters.
WritePerformanceCounters()

' Create 4 connections and display counter information.


Dim connection1 As SqlConnection = New SqlConnection( _
GetIntegratedSecurityConnectionString)
connection1.Open()
Console.WriteLine("Opened the 1st Connection:")
WritePerformanceCounters()

Dim connection2 As SqlConnection = New SqlConnection( _


GetSqlConnectionStringDifferent)
connection2.Open()
Console.WriteLine("Opened the 2nd Connection:")
WritePerformanceCounters()

Console.WriteLine("Opened the 3rd Connection:")


Dim connection3 As SqlConnection = New SqlConnection( _
GetSqlConnectionString)
connection3.Open()
WritePerformanceCounters()

Dim connection4 As SqlConnection = New SqlConnection( _


GetSqlConnectionString)
GetSqlConnectionString)
connection4.Open()
Console.WriteLine("Opened the 4th Connection:")
WritePerformanceCounters()

connection1.Close()
Console.WriteLine("Closed the 1st Connection:")
WritePerformanceCounters()

connection2.Close()
Console.WriteLine("Closed the 2nd Connection:")
WritePerformanceCounters()

connection3.Close()
Console.WriteLine("Closed the 3rd Connection:")
WritePerformanceCounters()

connection4.Close()
Console.WriteLine("Closed the 4th Connection:")
WritePerformanceCounters()
End Sub

Private Enum ADO_Net_Performance_Counters


NumberOfActiveConnectionPools
NumberOfReclaimedConnections
HardConnectsPerSecond
HardDisconnectsPerSecond
NumberOfActiveConnectionPoolGroups
NumberOfInactiveConnectionPoolGroups
NumberOfInactiveConnectionPools
NumberOfNonPooledConnections
NumberOfPooledConnections
NumberOfStasisConnections
' The following performance counters are more expensive to track.
' Enable ConnectionPoolPerformanceCounterDetail in your config file.
' SoftConnectsPerSecond
' SoftDisconnectsPerSecond
' NumberOfActiveConnections
' NumberOfFreeConnections
End Enum

Private Sub SetUpPerformanceCounters()


connection.Close()
Me.PerfCounters(9) = New PerformanceCounter()

Dim instanceName As String = GetInstanceName()


Dim apc As Type = GetType(ADO_Net_Performance_Counters)
Dim i As Integer = 0
Dim s As String = ""
For Each s In [Enum].GetNames(apc)
Me.PerfCounters(i) = New PerformanceCounter()
Me.PerfCounters(i).CategoryName = ".NET Data Provider for SqlServer"
Me.PerfCounters(i).CounterName = s
Me.PerfCounters(i).InstanceName = instanceName
i = (i + 1)
Next
End Sub

Private Declare Function GetCurrentProcessId Lib "kernel32.dll" () As Integer

Private Function GetInstanceName() As String


'This works for Winforms apps.
Dim instanceName As String = _
System.Reflection.Assembly.GetEntryAssembly.GetName.Name

' Must replace special characters like (, ), #, /, \\


Dim instanceName2 As String = _
AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[") _
.Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")
'For ASP.NET applications your instanceName will be your CurrentDomain's
'FriendlyName. Replace the line above that sets the instanceName with this:
'instanceName = AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[") _
' .Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")

Dim pid As String = GetCurrentProcessId.ToString


instanceName = (instanceName + ("[" & (pid & "]")))
Console.WriteLine("Instance Name: {0}", instanceName)
Console.WriteLine("---------------------------")
Return instanceName
End Function

Private Sub WritePerformanceCounters()


Console.WriteLine("---------------------------")
For Each p As PerformanceCounter In Me.PerfCounters
Console.WriteLine("{0} = {1}", p.CounterName, p.NextValue)
Next
Console.WriteLine("---------------------------")
End Sub

Private Shared Function GetIntegratedSecurityConnectionString() As String


' To avoid storing the connection string in your code,
' you can retrieve it from a configuration file.
Return ("Data Source=.\SqlExpress;Integrated Security=True;" &
"Initial Catalog=AdventureWorks")
End Function

Private Shared Function GetSqlConnectionString() As String


' To avoid storing the connection string in your code,
' you can retrieve it from a configuration file.
Return ("Data Source=.\SqlExpress;User Id=LowPriv;Password=Data!05;" &
"Initial Catalog=AdventureWorks")
End Function

Private Shared Function GetSqlConnectionStringDifferent() As String


' To avoid storing the connection string in your code,
' you can retrieve it from a configuration file.
Return ("Initial Catalog=AdventureWorks;Data Source=.\SqlExpress;" & _
"User Id=LowPriv;Password=Data!05;")
End Function
End Class

using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
PerformanceCounter[] PerfCounters = new PerformanceCounter[10];
SqlConnection connection = new SqlConnection();

static void Main()


{
Program prog = new Program();
// Open a connection and create the performance counters.
prog.connection.ConnectionString =
GetIntegratedSecurityConnectionString();
prog.SetUpPerformanceCounters();
Console.WriteLine("Available Performance Counters:");

// Create the connections and display the results.


prog.CreateConnections();
Console.WriteLine("Press Enter to finish.");
Console.ReadLine();
}
private void CreateConnections()
{
// List the Performance counters.
WritePerformanceCounters();

// Create 4 connections and display counter information.


SqlConnection connection1 = new SqlConnection(
GetIntegratedSecurityConnectionString());
connection1.Open();
Console.WriteLine("Opened the 1st Connection:");
WritePerformanceCounters();

SqlConnection connection2 = new SqlConnection(


GetSqlConnectionStringDifferent());
connection2.Open();
Console.WriteLine("Opened the 2nd Connection:");
WritePerformanceCounters();

SqlConnection connection3 = new SqlConnection(


GetSqlConnectionString());
connection3.Open();
Console.WriteLine("Opened the 3rd Connection:");
WritePerformanceCounters();

SqlConnection connection4 = new SqlConnection(


GetSqlConnectionString());
connection4.Open();
Console.WriteLine("Opened the 4th Connection:");
WritePerformanceCounters();

connection1.Close();
Console.WriteLine("Closed the 1st Connection:");
WritePerformanceCounters();

connection2.Close();
Console.WriteLine("Closed the 2nd Connection:");
WritePerformanceCounters();

connection3.Close();
Console.WriteLine("Closed the 3rd Connection:");
WritePerformanceCounters();

connection4.Close();
Console.WriteLine("Closed the 4th Connection:");
WritePerformanceCounters();
}

private enum ADO_Net_Performance_Counters


{
NumberOfActiveConnectionPools,
NumberOfReclaimedConnections,
HardConnectsPerSecond,
HardDisconnectsPerSecond,
NumberOfActiveConnectionPoolGroups,
NumberOfInactiveConnectionPoolGroups,
NumberOfInactiveConnectionPools,
NumberOfNonPooledConnections,
NumberOfPooledConnections,
NumberOfStasisConnections
// The following performance counters are more expensive to track.
// Enable ConnectionPoolPerformanceCounterDetail in your config file.
// SoftConnectsPerSecond
// SoftDisconnectsPerSecond
// NumberOfActiveConnections
// NumberOfFreeConnections
}

private void SetUpPerformanceCounters()


{
{
connection.Close();
this.PerfCounters = new PerformanceCounter[10];
string instanceName = GetInstanceName();
Type apc = typeof(ADO_Net_Performance_Counters);
int i = 0;
foreach (string s in Enum.GetNames(apc))
{
this.PerfCounters[i] = new PerformanceCounter();
this.PerfCounters[i].CategoryName = ".NET Data Provider for SqlServer";
this.PerfCounters[i].CounterName = s;
this.PerfCounters[i].InstanceName = instanceName;
i++;
}
}

[DllImport("kernel32.dll", SetLastError = true)]


static extern int GetCurrentProcessId();

private string GetInstanceName()


{
//This works for Winforms apps.
string instanceName =
System.Reflection.Assembly.GetEntryAssembly().GetName().Name;

// Must replace special characters like (, ), #, /, \\


string instanceName2 =
AppDomain.CurrentDomain.FriendlyName.ToString().Replace('(', '[')
.Replace(')', ']').Replace('#', '_').Replace('/', '_').Replace('\\', '_');

// For ASP.NET applications your instanceName will be your CurrentDomain's


// FriendlyName. Replace the line above that sets the instanceName with this:
// instanceName = AppDomain.CurrentDomain.FriendlyName.ToString().Replace('(','[')
// .Replace(')',']').Replace('#','_').Replace('/','_').Replace('\\','_');

string pid = GetCurrentProcessId().ToString();


instanceName = instanceName + "[" + pid + "]";
Console.WriteLine("Instance Name: {0}", instanceName);
Console.WriteLine("---------------------------");
return instanceName;
}

private void WritePerformanceCounters()


{
Console.WriteLine("---------------------------");
foreach (PerformanceCounter p in this.PerfCounters)
{
Console.WriteLine("{0} = {1}", p.CounterName, p.NextValue());
}
Console.WriteLine("---------------------------");
}

private static string GetIntegratedSecurityConnectionString()


{
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file.
return @"Data Source=.\SqlExpress;Integrated Security=True;" +
"Initial Catalog=AdventureWorks";
}
private static string GetSqlConnectionString()
{
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file.
return @"Data Source=.\SqlExpress;User Id=LowPriv;Password=Data!05;" +
"Initial Catalog=AdventureWorks";
}

private static string GetSqlConnectionStringDifferent()


{
// To avoid storing the connection string in your code,
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file.
return @"Initial Catalog=AdventureWorks;Data Source=.\SqlExpress;" +
"User Id=LowPriv;Password=Data!05;";
}
}

Consulte también
Conectarse a un origen de datos
Agrupación de conexiones de OLE DB, ODBC y Oracle
Contadores de rendimiento para ASP.NET
Generación de perfiles en tiempo de ejecución
Introducción a la supervisión de los umbrales de rendimiento
Información general de ADO.NET
Programación asincrónica
15/01/2020 • 20 minutes to read • Edit Online

En este tema se describe la compatibilidad con la programación asincrónica en el proveedor de datos de .NET
Framework para SQL Server (SqlClient), incluidas las mejoras realizadas para admitir la funcionalidad de
programación asincrónica que se presentó en .NET Framework 4,5.

Programación asincrónica heredada


Antes de .NET Framework 4,5, la programación asincrónica con SqlClient se realizaba con los siguientes métodos y
la propiedad de conexión Asynchronous Processing=true :
1. SqlCommand.BeginExecuteNonQuery
2. SqlCommand.BeginExecuteReader
3. SqlCommand.BeginExecuteXmlReader
Esta funcionalidad permanece en SqlClient en .NET Framework 4,5.

TIP
A partir de la .NET Framework 4,5, estos métodos heredados ya no requieren Asynchronous Processing=true en la
cadena de conexión.

Características de programación asincrónica agregadas en .NET


Framework 4,5
La nueva característica de programación asincrónica proporciona una técnica sencilla para crear código
asincrónico.
Para obtener más información acerca de la característica de programación asincrónica que se presentó en .NET
Framework 4,5, consulte:
Programación asincrónica en C#
Programación asincrónica con Async y Await (Visual Basic)
Usar los nuevos métodos Async de SqlDataReader en .NET 4,5 (parte 1)
Usar los nuevos métodos Async de SqlDataReader en .NET 4,5 (parte 2)
Cuando la interfaz de usuario no responde o el servidor no escala, es probable que necesite que el código sea más
asincrónico. La escritura de código asincrónico ha implicado tradicionalmente la instalación de una devolución de
llamada (también denominada continuación) para expresar la lógica que tiene lugar después de que la operación
asincrónica finalice. Esto complica la estructura del código asincrónico con respecto al código sincrónico.
Ahora puede llamar a métodos asincrónicos sin usar devoluciones de llamada y sin dividir el código en varios
métodos o expresiones lambda.
El modificador async especifica que un método es asincrónico. Al llamar a un método async , se devuelve una
tarea. Cuando se aplica el operador de await a una tarea, el método actual finaliza inmediatamente. Cuando la
tarea finaliza, la ejecución se reanuda en el mismo método.
WARNING
Las llamadas asincrónicas no se admiten si una aplicación también usa la palabra clave de cadena de conexión
Context Connection .

Al llamar a un método async no se asigna ningún subproceso adicional. Puede usar el subproceso existente de
finalización de E/S momentáneamente al final.
Los métodos siguientes se agregaron en .NET Framework 4,5 para admitir la programación asincrónica:
DbConnection.OpenAsync
DbCommand.ExecuteDbDataReaderAsync
DbCommand.ExecuteNonQueryAsync
DbCommand.ExecuteReaderAsync
DbCommand.ExecuteScalarAsync
GetFieldValueAsync
IsDBNullAsync
DbDataReader.NextResultAsync
DbDataReader.ReadAsync
SqlConnection.OpenAsync
SqlCommand.ExecuteNonQueryAsync
SqlCommand.ExecuteReaderAsync
SqlCommand.ExecuteScalarAsync
SqlCommand.ExecuteXmlReaderAsync
SqlDataReader.NextResultAsync
SqlDataReader.ReadAsync
SqlBulkCopy.WriteToServerAsync
Se agregaron otros miembros asincrónicos para admitir la compatibilidad con el streaming de SqlClient.

TIP
Los nuevos métodos asincrónicos no requieren Asynchronous Processing=true en la cadena de conexión.

Apertura de conexiones sincrónicas a asincrónicas


Puede actualizar una aplicación existente para usar la nueva característica asincrónica. Por ejemplo, suponga que
una aplicación tiene un algoritmo de conexión sincrónico y bloquea el subproceso de la interfaz de usuario cada
vez que se conecta a la base de datos y, una vez que está conectada, la aplicación llama a un procedimiento
almacenado que señala otros usuarios en vez de al que acaba de iniciar sesión.
using SqlConnection conn = new SqlConnection("…");
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("StoredProcedure_Logon", conn))
{
cmd.ExecuteNonQuery();
}
}

Cuando se convierte para usar la nueva funcionalidad asincrónica, el programa es así:

using System;
using System.Data.SqlClient;
using System.Threading.Tasks;

class A {

static async Task<int> Method(SqlConnection conn, SqlCommand cmd) {


await conn.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return 1;
}

public static void Main() {


using (SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind;
Integrated Security=SSPI")) {
SqlCommand command = new SqlCommand("select top 2 * from orders", conn);

int result = A.Method(conn, command).Result;

SqlDataReader reader = command.ExecuteReader();


while (reader.Read())
Console.WriteLine(reader[0]);
}
}
}

Agregar la nueva característica asincrónica en una aplicación existente (combinación de patrones antiguos y
nuevos)
También es posible agregar la nueva función asincrónica (SqlConnection::OpenAsync) sin modificar la lógica
asincrónica existente. Por ejemplo, si una aplicación usa actualmente:

AsyncCallback productList = new AsyncCallback(ProductList);


SqlConnection conn = new SqlConnection("…");
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM [Current Product List]", conn);
IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);

Puede empezar a usar el nuevo patrón asincrónico sin cambiar sustancialmente el algoritmo existente.
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;

class A {
static void ProductList(IAsyncResult result) { }

public static void Main() {


// AsyncCallback productList = new AsyncCallback(ProductList);
// SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated
Security=SSPI");
// conn.Open();
// SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn);
// IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);

AsyncCallback productList = new AsyncCallback(ProductList);


SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated
Security=SSPI");
conn.OpenAsync().ContinueWith((task) => {
SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn);
IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}

Usar el modelo de proveedor base y la nueva característica asincrónica


Puede que tenga que crear una herramienta que pueda conectarse a bases de datos diferentes y ejecutar
consultas. Puede usar el modelo de proveedor base y la nueva característica asincrónica.
El Controlador de transacciones distribuidas de Microsoft (MSDTC) debe estar habilitado en el servidor para usar
transacciones distribuidas. Para obtener información sobre cómo habilitar MSDTC, consulte Cómo habilitar MSDTC
en un servidor Web.
using System;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;

class A {
static async Task PerformDBOperationsUsingProviderModel(string connectionString, string providerName) {
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
using (DbConnection connection = factory.CreateConnection()) {
connection.ConnectionString = connectionString;
await connection.OpenAsync();

DbCommand command = connection.CreateCommand();


command.CommandText = "SELECT * FROM AUTHORS";

using (DbDataReader reader = await command.ExecuteReaderAsync()) {


while (await reader.ReadAsync()) {
for (int i = 0; i < reader.FieldCount; i++) {
// Process each column as appropriate
object obj = await reader.GetFieldValueAsync<object>(i);
Console.WriteLine(obj);
}
}
}
}
}

public static void Main()


{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
// replace these with your own values
builder.DataSource = "your_server";
builder.InitialCatalog = "pubs";
builder.IntegratedSecurity = true;
string provider = "System.Data.SqlClient";

Task task = PerformDBOperationsUsingProviderModel(builder.ConnectionString, provider);


task.Wait();
}
}

Usar transacciones SQL y la nueva característica asincrónica


using System;
using System.Data.SqlClient;
using System.Threading.Tasks;

class Program {
static void Main() {
string connectionString =
"Persist Security Info=False;Integrated Security=SSPI;database=Northwind;server=(local)";
Task task = ExecuteSqlTransaction(connectionString);
task.Wait();
}

static async Task ExecuteSqlTransaction(string connectionString) {


using (SqlConnection connection = new SqlConnection(connectionString)) {
await connection.OpenAsync();

SqlCommand command = connection.CreateCommand();


SqlTransaction transaction = null;

// Start a local transaction.


transaction = await Task.Run<SqlTransaction>(
() => connection.BeginTransaction("SampleTransaction")
);

// Must assign both transaction object and connection


// to Command object for a pending local transaction
command.Connection = connection;
command.Transaction = transaction;

try {
command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (555, 'Description')";
await command.ExecuteNonQueryAsync();

command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (556, 'Description')";
await command.ExecuteNonQueryAsync();

// Attempt to commit the transaction.


await Task.Run(() => transaction.Commit());
Console.WriteLine("Both records are written to database.");
}
catch (Exception ex) {
Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);

// Attempt to roll back the transaction.


try {
transaction.Rollback();
}
catch (Exception ex2) {
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
}

Usar transacciones SQL y la nueva característica asincrónica


En una aplicación empresarial, puede que necesite agregar transacciones distribuidas en algunos escenarios, para
habilitar transacciones entre varios servidores de bases de datos. Puede usar el espacio de nombres
System.Transactions e inscribir una transacción distribuida, del siguiente modo:

using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Transactions;

class Program {
public static void Main()
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
// replace these with your own values
builder.DataSource = "your_server";
builder.InitialCatalog = "your_data_source";
builder.IntegratedSecurity = true;

Task task = ExecuteDistributedTransaction(builder.ConnectionString, builder.ConnectionString);


task.Wait();
}

static async Task ExecuteDistributedTransaction(string connectionString1, string connectionString2) {


using (SqlConnection connection1 = new SqlConnection(connectionString1))
using (SqlConnection connection2 = new SqlConnection(connectionString2)) {
using (CommittableTransaction transaction = new CommittableTransaction()) {
await connection1.OpenAsync();
connection1.EnlistTransaction(transaction);

await connection2.OpenAsync();
connection2.EnlistTransaction(transaction);

try {
SqlCommand command1 = connection1.CreateCommand();
command1.CommandText = "Insert into RegionTable1 (RegionID, RegionDescription) VALUES (100,
'Description')";
await command1.ExecuteNonQueryAsync();

SqlCommand command2 = connection2.CreateCommand();


command2.CommandText = "Insert into RegionTable2 (RegionID, RegionDescription) VALUES (100,
'Description')";
await command2.ExecuteNonQueryAsync();

transaction.Commit();
}
catch (Exception ex) {
Console.WriteLine("Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);

try {
transaction.Rollback();
}
catch (Exception ex2) {
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
}
}

Cancelar una operación asincrónica


Puede cancelar una solicitud asincrónica mediante CancellationToken.
using System;
using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;

namespace Samples {
class CancellationSample {
public static void Main(string[] args) {
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(2000); // give up after 2 seconds
try {
Task result = CancellingAsynchronousOperations(source.Token);
result.Wait();
}
catch (AggregateException exception) {
if (exception.InnerException is SqlException) {
Console.WriteLine("Operation canceled");
}
else {
throw;
}
}
}

static async Task CancellingAsynchronousOperations(CancellationToken cancellationToken) {


using (SqlConnection connection = new SqlConnection("Server=(local);Integrated Security=true")) {
await connection.OpenAsync(cancellationToken);

SqlCommand command = new SqlCommand("WAITFOR DELAY '00:10:00'", connection);


await command.ExecuteNonQueryAsync(cancellationToken);
}
}
}
}

Operaciones asincrónicas con SqlBulkCopy


Las capacidades asincrónicas también se agregaron a System.Data.SqlClient.SqlBulkCopy con
SqlBulkCopy.WriteToServerAsync.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SqlBulkCopyAsyncCodeSample {
class Program {
static string selectStatement = "SELECT * FROM [pubs].[dbo].[titles]";
static string createDestTableStatement =
@"CREATE TABLE {0} (
[title_id] [varchar](6) NOT NULL,
[title] [varchar](80) NOT NULL,
[type] [char](12) NOT NULL,
[pub_id] [char](4) NULL,
[price] [money] NULL,
[advance] [money] NULL,
[royalty] [int] NULL,
[ytd_sales] [int] NULL,
[notes] [varchar](200) NULL,
[pubdate] [datetime] NOT NULL)";
// Replace the connection string if needed, for instance to connect to SQL Express: @"Server=
(local)\SQLEXPRESS;Database=Demo;Integrated Security=true"
// static string connectionString = @"Server=(localdb)\V11.0;Database=Demo";
static string connectionString = @"Server=(local);Database=Demo;Integrated Security=true";

// static string odbcConnectionString = @"Driver={SQL Server};Server=


(localdb)\V11.0;UID=oledb;Pwd=1Password!;Database=Demo";
static string odbcConnectionString = @"Driver={SQL Server};Server=(local);Database=Demo;Integrated
Security=true";

// static string marsConnectionString = @"Server=


(localdb)\V11.0;Database=Demo;MultipleActiveResultSets=true;";
static string marsConnectionString = @"Server=
(local);Database=Demo;MultipleActiveResultSets=true;Integrated Security=true";

// Replace the Server name with your actual sql azure server name and User ID/Password
static string azureConnectionString = @"Server=SqlAzure;User
ID=myUserID;Password=myPassword;Database=Demo";

static void Main(string[] args) {


SynchronousSqlBulkCopy();
AsyncSqlBulkCopy().Wait();
MixSyncAsyncSqlBulkCopy().Wait();
AsyncSqlBulkCopyNotifyAfter().Wait();
AsyncSqlBulkCopyDataRows().Wait();
// AsyncSqlBulkCopySqlServerToSqlAzure().Wait();
// AsyncSqlBulkCopyCancel().Wait();
AsyncSqlBulkCopyMARS().Wait();
}

// 3.1.1 Synchronous bulk copy in .NET 4.5


private static void SynchronousSqlBulkCopy() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
conn.Open();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);

string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";


cmd.CommandText = string.Format(createDestTableStatement, temptable);
cmd.ExecuteNonQuery();

using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {


bcp.DestinationTableName = temptable;
bcp.WriteToServer(dt);
}
}
}

// 3.1.2 Asynchronous bulk copy in .NET 4.5


private static async Task AsyncSqlBulkCopy() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);

string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";


cmd.CommandText = string.Format(createDestTableStatement, temptable);
await cmd.ExecuteNonQueryAsync();

using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {


bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(dt);
}
}
}
}
}

// 3.2 Add new Async.NET capabilities in an existing application (Mixing synchronous and asynchronous
calls)
private static async Task MixSyncAsyncSqlBulkCopy() {
using (OdbcConnection odbcconn = new OdbcConnection(odbcConnectionString)) {
odbcconn.Open();
using (OdbcCommand odbccmd = new OdbcCommand(selectStatement, odbcconn)) {
using (OdbcDataReader odbcreader = odbccmd.ExecuteReader()) {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
string temptable = "temptable";//"[#" + Guid.NewGuid().ToString("N") + "]";
SqlCommand createCmd = new SqlCommand(string.Format(createDestTableStatement, temptable),
conn);
await createCmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(odbcreader);
}
}
}
}
}
}

// 3.3 Using the NotifyAfter property


private static async Task AsyncSqlBulkCopyNotifyAfter() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);

string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";


cmd.CommandText = string.Format(createDestTableStatement, temptable);
await cmd.ExecuteNonQueryAsync();

using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {


bcp.DestinationTableName = temptable;
bcp.NotifyAfter = 5;
bcp.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
await bcp.WriteToServerAsync(dt);
}
}
}
}

private static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e) {


Console.WriteLine("Copied {0} so far...", e.RowsCopied);
}

// 3.4 Using the new SqlBulkCopy Async.NET capabilities with DataRow[]


private static async Task AsyncSqlBulkCopyDataRows() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
DataRow[] rows = dt.Select();

string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";


cmd.CommandText = string.Format(createDestTableStatement, temptable);
await cmd.ExecuteNonQueryAsync();

using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {


using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(rows);
}
}
}
}

// 3.5 Copying data from SQL Server to SQL Azure in .NET 4.5
//private static async Task AsyncSqlBulkCopySqlServerToSqlAzure() {
// using (SqlConnection srcConn = new SqlConnection(connectionString))
// using (SqlConnection destConn = new SqlConnection(azureConnectionString)) {
// await srcConn.OpenAsync();
// await destConn.OpenAsync();
// using (SqlCommand srcCmd = new SqlCommand(selectStatement, srcConn)) {
// using (SqlDataReader reader = await srcCmd.ExecuteReaderAsync()) {
// string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
// using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement,
temptable), destConn)) {
// await destCmd.ExecuteNonQueryAsync();
// using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) {
// bcp.DestinationTableName = temptable;
// await bcp.WriteToServerAsync(reader);
// }
// }
// }
// }
// }
//}

// 3.6 Cancelling an Asynchronous Operation to SQL Azure


//private static async Task AsyncSqlBulkCopyCancel() {
// CancellationTokenSource cts = new CancellationTokenSource();
// using (SqlConnection srcConn = new SqlConnection(connectionString))
// using (SqlConnection destConn = new SqlConnection(azureConnectionString)) {
// await srcConn.OpenAsync(cts.Token);
// await destConn.OpenAsync(cts.Token);
// using (SqlCommand srcCmd = new SqlCommand(selectStatement, srcConn)) {
// using (SqlDataReader reader = await srcCmd.ExecuteReaderAsync(cts.Token)) {
// string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
// using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement,
temptable), destConn)) {
// await destCmd.ExecuteNonQueryAsync(cts.Token);
// using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) {
// bcp.DestinationTableName = temptable;
// await bcp.WriteToServerAsync(reader, cts.Token);
// //Cancel Async SqlBulCopy Operation after 200 ms
// cts.CancelAfter(200);
// }
// }
// }
// }
// }
//}

// 3.7 Using Async.Net and MARS


private static async Task AsyncSqlBulkCopyMARS() {
using (SqlConnection marsConn = new SqlConnection(marsConnectionString)) {
await marsConn.OpenAsync();

SqlCommand titlesCmd = new SqlCommand("SELECT * FROM [pubs].[dbo].[titles]", marsConn);


SqlCommand authorsCmd = new SqlCommand("SELECT * FROM [pubs].[dbo].[authors]", marsConn);
//With MARS we can have multiple active results sets on the same connection
using (SqlDataReader titlesReader = await titlesCmd.ExecuteReaderAsync())
using (SqlDataReader authorsReader = await authorsCmd.ExecuteReaderAsync()) {
await authorsReader.ReadAsync();

string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";


using (SqlConnection destConn = new SqlConnection(connectionString)) {
await destConn.OpenAsync();
await destConn.OpenAsync();
using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement,
temptable), destConn)) {
await destCmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(titlesReader);
}
}
}
}
}
}
}
}

Usar varios comandos de forma asincrónica con MARS


En el ejemplo se abre una única conexión a la base de datos AdventureWorks . Al utilizar un objeto SqlCommand,
se crea un objeto SqlDataReader. A medida que se utiliza el lector, se abre un segundo SqlDataReader, que utiliza
datos del primer SqlDataReader como entrada a la cláusula WHERE del segundo lector.

NOTE
En el siguiente ejemplo se usa la base de datos de ejemplo AdventureWorks que se incluye con SQL Server. La cadena de
conexión proporcionada en el código de ejemplo asume que la base de datos está instalada y disponible en el equipo local.
Modifique la cadena de conexión según sea necesario para su entorno.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;

class Class1 {
static void Main() {
Task task = MultipleCommands();
task.Wait();
}

static async Task MultipleCommands() {


// By default, MARS is disabled when connecting to a MARS-enabled.
// It must be enabled in the connection string.
string connectionString = GetConnectionString();

int vendorID;
SqlDataReader productReader = null;
string vendorSQL =
"SELECT VendorId, Name FROM Purchasing.Vendor";
string productSQL =
"SELECT Production.Product.Name FROM Production.Product " +
"INNER JOIN Purchasing.ProductVendor " +
"ON Production.Product.ProductID = " +
"Purchasing.ProductVendor.ProductID " +
"WHERE Purchasing.ProductVendor.VendorID = @VendorId";

using (SqlConnection awConnection =


new SqlConnection(connectionString)) {
SqlCommand vendorCmd = new SqlCommand(vendorSQL, awConnection);
SqlCommand productCmd =
new SqlCommand(productSQL, awConnection);

productCmd.Parameters.Add("@VendorId", SqlDbType.Int);

await awConnection.OpenAsync();
using (SqlDataReader vendorReader = await vendorCmd.ExecuteReaderAsync()) {
while (await vendorReader.ReadAsync()) {
Console.WriteLine(vendorReader["Name"]);

vendorID = (int)vendorReader["VendorId"];

productCmd.Parameters["@VendorId"].Value = vendorID;
// The following line of code requires a MARS-enabled connection.
productReader = await productCmd.ExecuteReaderAsync();
using (productReader) {
while (await productReader.ReadAsync()) {
Console.WriteLine(" " +
productReader["Name"].ToString());
}
}
}
}
}
}

private static string GetConnectionString() {


// To avoid storing the connection string in your code, you can retrieve it from a configuration file.
return "Data Source=(local);Integrated Security=SSPI;Initial
Catalog=AdventureWorks;MultipleActiveResultSets=True";
}
}

Leer y actualizar datos de forma asincrónica con MARS


MARS permite que se utilice una conexión para las operaciones de lectura y de lenguaje de manipulación de datos
(DML) con más de una operación pendiente. Esta característica elimina la necesidad de que las aplicaciones
solucionen los errores de falta de disponibilidad de las conexiones. Además, MARS puede reemplazar al uso de
cursores en el servidor, que generalmente consume más recursos. Finalmente, dado que es posible realizar varias
operaciones con una sola conexión, pueden compartir el mismo contexto de transacción, lo que elimina la
necesidad de usar los procedimientos almacenados del sistema sp_getbindtoken y sp_bindsession .
La siguiente aplicación de consola demuestra cómo utilizar dos objetos SqlDataReader con tres objetos
SqlCommand y un solo objeto SqlConnection teniendo MARS habilitado. El primer objeto de comando recupera
una lista de proveedores cuya clasificación crediticia es 5. El segundo objeto de comando utiliza el id. de proveedor
proporcionado a partir de un SqlDataReader para cargar el segundo SqlDataReader con todos los productos de
ese proveedor en particular. El segundo SqlDataReader visita cada registro de producto. Para determinar cuál debe
ser la nueva OnOrderQty , se realiza un cálculo. Luego, se usa el tercer objeto de comando para actualizar la tabla
ProductVendor con el nuevo valor. Todo este proceso tiene lugar en una única transacción, que al final se
revierte.

NOTE
En el siguiente ejemplo se usa la base de datos de ejemplo AdventureWorks que se incluye con SQL Server. La cadena de
conexión proporcionada en el código de ejemplo asume que la base de datos está instalada y disponible en el equipo local.
Modifique la cadena de conexión según sea necesario para su entorno.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;

class Program {
static void Main() {
Task task = ReadingAndUpdatingData();
task.Wait();
}

static async Task ReadingAndUpdatingData() {


// By default, MARS is disabled when connecting to a MARS-enabled host.
// It must be enabled in the connection string.
string connectionString = GetConnectionString();

SqlTransaction updateTx = null;


SqlCommand vendorCmd = null;
SqlCommand prodVendCmd = null;
SqlCommand updateCmd = null;

SqlDataReader prodVendReader = null;

int vendorID = 0;
int productID = 0;
int minOrderQty = 0;
int maxOrderQty = 0;
int onOrderQty = 0;
int recordsUpdated = 0;
int totalRecordsUpdated = 0;

string vendorSQL =
"SELECT VendorID, Name FROM Purchasing.Vendor " +
"WHERE CreditRating = 5";
string prodVendSQL =
"SELECT ProductID, MaxOrderQty, MinOrderQty, OnOrderQty " +
"FROM Purchasing.ProductVendor " +
"WHERE VendorID = @VendorID";
"WHERE VendorID = @VendorID";
string updateSQL =
"UPDATE Purchasing.ProductVendor " +
"SET OnOrderQty = @OrderQty " +
"WHERE ProductID = @ProductID AND VendorID = @VendorID";

using (SqlConnection awConnection =


new SqlConnection(connectionString)) {
await awConnection.OpenAsync();
updateTx = await Task.Run(() => awConnection.BeginTransaction());

vendorCmd = new SqlCommand(vendorSQL, awConnection);


vendorCmd.Transaction = updateTx;

prodVendCmd = new SqlCommand(prodVendSQL, awConnection);


prodVendCmd.Transaction = updateTx;
prodVendCmd.Parameters.Add("@VendorId", SqlDbType.Int);

updateCmd = new SqlCommand(updateSQL, awConnection);


updateCmd.Transaction = updateTx;
updateCmd.Parameters.Add("@OrderQty", SqlDbType.Int);
updateCmd.Parameters.Add("@ProductID", SqlDbType.Int);
updateCmd.Parameters.Add("@VendorID", SqlDbType.Int);

using (SqlDataReader vendorReader = await vendorCmd.ExecuteReaderAsync()) {


while (await vendorReader.ReadAsync()) {
Console.WriteLine(vendorReader["Name"]);

vendorID = (int)vendorReader["VendorID"];
prodVendCmd.Parameters["@VendorID"].Value = vendorID;
prodVendReader = await prodVendCmd.ExecuteReaderAsync();

using (prodVendReader) {
while (await prodVendReader.ReadAsync()) {
productID = (int)prodVendReader["ProductID"];

if (prodVendReader["OnOrderQty"] == DBNull.Value) {
minOrderQty = (int)prodVendReader["MinOrderQty"];
onOrderQty = minOrderQty;
}
else {
maxOrderQty = (int)prodVendReader["MaxOrderQty"];
onOrderQty = (int)(maxOrderQty / 2);
}

updateCmd.Parameters["@OrderQty"].Value = onOrderQty;
updateCmd.Parameters["@ProductID"].Value = productID;
updateCmd.Parameters["@VendorID"].Value = vendorID;

recordsUpdated = await updateCmd.ExecuteNonQueryAsync();


totalRecordsUpdated += recordsUpdated;
}
}
}
}
Console.WriteLine("Total Records Updated: ", totalRecordsUpdated.ToString());
await Task.Run(() => updateTx.Rollback());
Console.WriteLine("Transaction Rolled Back");
}
}

private static string GetConnectionString() {


// To avoid storing the connection string in your code, you can retrieve it from a configuration file.
return "Data Source=(local);Integrated Security=SSPI;Initial
Catalog=AdventureWorks;MultipleActiveResultSets=True";
}
}
Vea también
Recuperar y modificar datos en ADO.NET
Compatibilidad de transmisión de datos de SqlClient
23/10/2019 • 15 minutes to read • Edit Online

La compatibilidad de streaming entre SQL Server y una aplicación (novedad en .NET Framework 4,5) admite datos
no estructurados en el servidor (documentos, imágenes y archivos multimedia). Una base de datos de SQL Server
puede almacenar objetos binarios grandes (blobs), pero la recuperación de blobs puede utilizar mucha memoria.
La compatibilidad con streaming hacia y desde SQL Server simplifica la escritura de aplicaciones que transmiten
datos, sin tener que cargar completamente los datos en la memoria, lo que da lugar a menos excepciones de
desbordamiento de memoria.
La compatibilidad con streaming también permitirá que las aplicaciones de nivel intermedio escalen mejor,
especialmente en escenarios donde los objetos comerciales establezcan conexión con SQL Azure para enviar,
recuperar y manipular BLOB grandes.

WARNING
Las llamadas asincrónicas no se admiten si una aplicación también usa la palabra clave de cadena de conexión
Context Connection .

Los miembros agregados para admitir streaming se usan para recuperar datos de consultas y para pasar parámetros a
consultas y procedimientos almacenados. La característica de streaming está dirigida a escenarios OLTP y de migración de
datos básicos, y es aplicable en entornos de migración de datos locales y remotos.

Compatibilidad con streaming desde SQL Server


La compatibilidad con streaming desde SQL Server introduce una DbDataReader nueva funcionalidad en
SqlDataReader y en las clases para Streamobtener XmlReaderobjetos, TextReader y y reaccionar ante ellos. Estas
clases se usan para recuperar datos de consultas. Como resultado, la compatibilidad con streaming desde SQL
Server aborda escenarios de OLTP y se aplica a entornos locales y fuera de las instalaciones.
Se han agregado los siguientes miembros SqlDataReader a para habilitar la compatibilidad con la transmisión por
secuencias desde SQL Server:
1. IsDBNullAsync
2. SqlDataReader.GetFieldValue
3. GetFieldValueAsync
4. GetStream
5. GetTextReader
6. GetXmlReader
Se han agregado los siguientes miembros DbDataReader a para habilitar la compatibilidad con la transmisión por
secuencias desde SQL Server:
1. GetFieldValue
2. GetStream
3. GetTextReader
Compatibilidad de streaming con SQL Server
La compatibilidad de streaming con SQL Server introduce una SqlParameter nueva funcionalidad en la clase para
que pueda XmlReaderaceptar Streamy reaccionar a los objetos, y TextReader . SqlParameter se usa para pasar
parámetros a consultas y procedimientos almacenados.
Al desechar un objeto SqlCommand o llamar a Cancel se cancela cualquier operación de streaming. Si una
aplicación envía CancellationToken, la cancelación no se puede garantizar.
Los siguientes tipos SqlDbType aceptarán Value de Stream:
Binar y
VarBinar y
Los siguientes tipos SqlDbType aceptarán Value de TextReader:
Char
NChar
NVarChar
Xml
El tipo XML SqlDbType aceptará un Value de XmlReader.
SqlValue puede aceptar valores de tipo XmlReader, TextReader y Stream.
Los objetos XmlReader, TextReader y Stream se transferirán al valor definido por Size.

Ejemplo: streaming desde SQL Server


Use el siguiente Transact-SQL para crear la base de datos de ejemplo:

CREATE DATABASE [Demo]


GO
USE [Demo]
GO
CREATE TABLE [Streams] (
[id] INT PRIMARY KEY IDENTITY(1, 1),
[textdata] NVARCHAR(MAX),
[bindata] VARBINARY(MAX),
[xmldata] XML)
GO
INSERT INTO [Streams] (textdata, bindata, xmldata) VALUES (N'This is a test', 0x48656C6C6F,
N'<test>value</test>')
INSERT INTO [Streams] (textdata, bindata, xmldata) VALUES (N'Hello, World!', 0x54657374696E67,
N'<test>value2</test>')
INSERT INTO [Streams] (textdata, bindata, xmldata) VALUES (N'Another row', 0x666F6F626172, N'<fff>bbb</fff>
<fff>bbc</fff>')
GO

El ejemplo muestra cómo hacer lo siguiente:


Evitar bloquear un subproceso de interfaz de usuario al proporcionar una manera asincrónica para
recuperar archivos grandes.
Transfiera un archivo de texto grande desde SQL Server en .NET Framework 4,5.
Transfiera un archivo XML de gran tamaño desde SQL Server en .NET Framework 4,5.
Recuperar datos de SQL Server.
Transfiera archivos de gran tamaño (BLOB) de una SQL Server base de datos a otra sin quedarse sin
memoria.

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Threading.Tasks;
using System.Xml;

namespace StreamingFromServer {
class Program {
// Replace the connection string if needed, for instance to connect to SQL Express: @"Server=
(local)\SQLEXPRESS;Database=Demo;Integrated Security=true"
private const string connectionString = @"Server=(localdb)\V11.0;Database=Demo";

static void Main(string[] args) {


CopyBinaryValueToFile().Wait();
PrintTextValues().Wait();
PrintXmlValues().Wait();
PrintXmlValuesViaNVarChar().Wait();

Console.WriteLine("Done");
}

// Application retrieving a large BLOB from SQL Server in .NET 4.5 using the new asynchronous
capability
private static async Task CopyBinaryValueToFile() {
string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"binarydata.bin");

using (SqlConnection connection = new SqlConnection(connectionString)) {


await connection.OpenAsync();
using (SqlCommand command = new SqlCommand("SELECT [bindata] FROM [Streams] WHERE [id]=@id",
connection)) {
command.Parameters.AddWithValue("id", 1);

// The reader needs to be executed with the SequentialAccess behavior to enable network
streaming
// Otherwise ReadAsync will buffer the entire BLOB into memory which can cause scalability
issues or even OutOfMemoryExceptions
using (SqlDataReader reader = await
command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) {
if (await reader.ReadAsync()) {
if (!(await reader.IsDBNullAsync(0))) {
using (FileStream file = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
using (Stream data = reader.GetStream(0)) {

// Asynchronously copy the stream from the server to the file we just created
await data.CopyToAsync(file);
}
}
}
}
}
}
}
}

// Application transferring a large Text File from SQL Server in .NET 4.5
private static async Task PrintTextValues() {
using (SqlConnection connection = new SqlConnection(connectionString)) {
await connection.OpenAsync();
using (SqlCommand command = new SqlCommand("SELECT [id], [textdata] FROM [Streams]", connection))
{

// The reader needs to be executed with the SequentialAccess behavior to enable network
// The reader needs to be executed with the SequentialAccess behavior to enable network
streaming
// Otherwise ReadAsync will buffer the entire text document into memory which can cause
scalability issues or even OutOfMemoryExceptions
using (SqlDataReader reader = await
command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) {
while (await reader.ReadAsync()) {
Console.Write("{0}: ", reader.GetInt32(0));

if (await reader.IsDBNullAsync(1)) {
Console.Write("(NULL)");
}
else {
char[] buffer = new char[4096];
int charsRead = 0;
using (TextReader data = reader.GetTextReader(1)) {
do {
// Grab each chunk of text and write it to the console
// If you are writing to a TextWriter you should use WriteAsync or
WriteLineAsync
charsRead = await data.ReadAsync(buffer, 0, buffer.Length);
Console.Write(buffer, 0, charsRead);
} while (charsRead > 0);
}
}

Console.WriteLine();
}
}
}
}
}

// Application transferring a large Xml Document from SQL Server in .NET 4.5
private static async Task PrintXmlValues() {
using (SqlConnection connection = new SqlConnection(connectionString)) {
await connection.OpenAsync();
using (SqlCommand command = new SqlCommand("SELECT [id], [xmldata] FROM [Streams]", connection))
{

// The reader needs to be executed with the SequentialAccess behavior to enable network
streaming
// Otherwise ReadAsync will buffer the entire Xml Document into memory which can cause
scalability issues or even OutOfMemoryExceptions
using (SqlDataReader reader = await
command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) {
while (await reader.ReadAsync()) {
Console.WriteLine("{0}: ", reader.GetInt32(0));

if (await reader.IsDBNullAsync(1)) {
Console.WriteLine("\t(NULL)");
}
else {
using (XmlReader xmlReader = reader.GetXmlReader(1)) {
int depth = 1;
// NOTE: The XmlReader returned by GetXmlReader does NOT support async operations
// See the example below (PrintXmlValuesViaNVarChar) for how to get an XmlReader
with asynchronous capabilities
while (xmlReader.Read()) {
switch (xmlReader.NodeType) {
case XmlNodeType.Element:
Console.WriteLine("{0}<{1}>", new string('\t', depth), xmlReader.Name);
depth++;
break;
case XmlNodeType.Text:
Console.WriteLine("{0}{1}", new string('\t', depth), xmlReader.Value);
break;
case XmlNodeType.EndElement:
depth--;
Console.WriteLine("{0}</{1}>", new string('\t', depth), xmlReader.Name);
Console.WriteLine("{0}</{1}>", new string('\t', depth), xmlReader.Name);
break;
}
}
}
}
}
}
}
}
}

// Application transferring a large Xml Document from SQL Server in .NET 4.5
// This goes via NVarChar and TextReader to enable asynchronous reading
private static async Task PrintXmlValuesViaNVarChar() {
XmlReaderSettings xmlSettings = new XmlReaderSettings() {
// Async must be explicitly enabled in the XmlReaderSettings otherwise the XmlReader will throw
exceptions when async methods are called
Async = true,
// Since we will immediately wrap the TextReader we are creating in an XmlReader, we will permit
the XmlReader to take care of closing\disposing it
CloseInput = true,
// If the Xml you are reading is not a valid document (as per
<https://docs.microsoft.com/previous-versions/dotnet/netframework-4.0/6bts1x50(v=vs.100)>) you will need to
set the conformance level to Fragment
ConformanceLevel = ConformanceLevel.Fragment
};

using (SqlConnection connection = new SqlConnection(connectionString)) {


await connection.OpenAsync();

// Cast the XML into NVarChar to enable GetTextReader - trying to use GetTextReader on an XML
type will throw an exception
using (SqlCommand command = new SqlCommand("SELECT [id], CAST([xmldata] AS NVARCHAR(MAX)) FROM
[Streams]", connection)) {

// The reader needs to be executed with the SequentialAccess behavior to enable network
streaming
// Otherwise ReadAsync will buffer the entire Xml Document into memory which can cause
scalability issues or even OutOfMemoryExceptions
using (SqlDataReader reader = await
command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) {
while (await reader.ReadAsync()) {
Console.WriteLine("{0}:", reader.GetInt32(0));

if (await reader.IsDBNullAsync(1)) {
Console.WriteLine("\t(NULL)");
}
else {
// Grab the row as a TextReader, then create an XmlReader on top of it
// We are not keeping a reference to the TextReader since the XmlReader is created
with the "CloseInput" setting (so it will close the TextReader when needed)
using (XmlReader xmlReader = XmlReader.Create(reader.GetTextReader(1), xmlSettings))
{
int depth = 1;
// The XmlReader above now supports asynchronous operations, so we can use
ReadAsync here
while (await xmlReader.ReadAsync()) {
switch (xmlReader.NodeType) {
case XmlNodeType.Element:
Console.WriteLine("{0}<{1}>", new string('\t', depth), xmlReader.Name);
depth++;
break;
case XmlNodeType.Text:
// Depending on what your data looks like, you should either use Value or
GetValueAsync
// Value has less overhead (since it doesn't create a Task), but it may
also block if additional data is required
Console.WriteLine("{0}{1}", new string('\t', depth), await
xmlReader.GetValueAsync());
break;
case XmlNodeType.EndElement:
depth--;
Console.WriteLine("{0}</{1}>", new string('\t', depth), xmlReader.Name);
break;
}
}
}
}
}
}
}
}
}
}
}

Ejemplo: streaming a SQL Server


Use el siguiente Transact-SQL para crear la base de datos de ejemplo:

CREATE DATABASE [Demo2]


GO
USE [Demo2]
GO
CREATE TABLE [BinaryStreams] (
[id] INT PRIMARY KEY IDENTITY(1, 1),
[bindata] VARBINARY(MAX))
GO
CREATE TABLE [TextStreams] (
[id] INT PRIMARY KEY IDENTITY(1, 1),
[textdata] NVARCHAR(MAX))
GO
CREATE TABLE [BinaryStreamsCopy] (
[id] INT PRIMARY KEY IDENTITY(1, 1),
[bindata] VARBINARY(MAX))
GO

El ejemplo muestra cómo hacer lo siguiente:


Transferencia de un BLOB grande a SQL Server en .NET Framework 4,5.
Transferir un archivo de texto grande a SQL Server en .NET Framework 4,5.
Usar la nueva característica asincrónica para transferir un BLOB grande.
Usar la nueva característica asincrónica y la palabra clave await para transferir un BLOB grande.
Cancelar la transferencia de un BLOB grande.
Transmisión por secuencias de una SQL Server a otra mediante la nueva característica asincrónica.

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace StreamingToServer {
class Program {
// Replace the connection string if needed, for instance to connect to SQL Express: @"Server=
(local)\SQLEXPRESS;Database=Demo2;Integrated Security=true"
(local)\SQLEXPRESS;Database=Demo2;Integrated Security=true"
private const string connectionString = @"Server=(localdb)\V11.0;Database=Demo2";

static void Main(string[] args) {


CreateDemoFiles();

StreamBLOBToServer().Wait();
StreamTextToServer().Wait();

// Create a CancellationTokenSource that will be cancelled after 100ms


// Typically this token source will be cancelled by a user request (e.g. a Cancel button)
CancellationTokenSource tokenSource = new CancellationTokenSource();
tokenSource.CancelAfter(100);
try {
CancelBLOBStream(tokenSource.Token).Wait();
}
catch (AggregateException ex) {
// Cancelling an async operation will throw an exception
// Since we are using the Task's Wait method, this exception will be wrapped in an
AggregateException
// If you were using the 'await' keyword, the compiler would take care of unwrapping the
AggregateException
// Depending on when the cancellation occurs, you can either get an error from SQL Server or from
.Net
if ((ex.InnerException is SqlException) || (ex.InnerException is TaskCanceledException)) {
// This is an expected exception
Console.WriteLine("Got expected exception: {0}", ex.InnerException.Message);
}
else {
// Did not expect this exception - re-throw it
throw;
}
}

Console.WriteLine("Done");
}

// This is used to generate the files which are used by the other sample methods
private static void CreateDemoFiles() {
Random rand = new Random();
byte[] data = new byte[1024];
rand.NextBytes(data);

using (FileStream file = File.Open("binarydata.bin", FileMode.Create)) {


file.Write(data, 0, data.Length);
}

using (StreamWriter writer = new StreamWriter(File.Open("textdata.txt", FileMode.Create))) {


writer.Write(Convert.ToBase64String(data));
}
}

// Application transferring a large BLOB to SQL Server in .NET 4.5


private static async Task StreamBLOBToServer() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
using (SqlCommand cmd = new SqlCommand("INSERT INTO [BinaryStreams] (bindata) VALUES (@bindata)",
conn)) {
using (FileStream file = File.Open("binarydata.bin", FileMode.Open)) {

// Add a parameter which uses the FileStream we just opened


// Size is set to -1 to indicate "MAX"
cmd.Parameters.Add("@bindata", SqlDbType.Binary, -1).Value = file;

// Send the data to the server asynchronously


await cmd.ExecuteNonQueryAsync();
}
}
}
}
}

// Application transferring a large Text File to SQL Server in .NET 4.5


private static async Task StreamTextToServer() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
using (SqlCommand cmd = new SqlCommand("INSERT INTO [TextStreams] (textdata) VALUES (@textdata)",
conn)) {
using (StreamReader file = File.OpenText("textdata.txt")) {

// Add a parameter which uses the StreamReader we just opened


// Size is set to -1 to indicate "MAX"
cmd.Parameters.Add("@textdata", SqlDbType.NVarChar, -1).Value = file;

// Send the data to the server asynchronously


await cmd.ExecuteNonQueryAsync();
}
}
}
}

// Cancelling the transfer of a large BLOB


private static async Task CancelBLOBStream(CancellationToken cancellationToken) {
using (SqlConnection conn = new SqlConnection(connectionString)) {
// We can cancel not only sending the data to the server, but also opening the connection
await conn.OpenAsync(cancellationToken);

// Artificially delay the command by 100ms


using (SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:00:100';INSERT INTO [BinaryStreams]
(bindata) VALUES (@bindata)", conn)) {
using (FileStream file = File.Open("binarydata.bin", FileMode.Open)) {

// Add a parameter which uses the FileStream we just opened


// Size is set to -1 to indicate "MAX"
cmd.Parameters.Add("@bindata", SqlDbType.Binary, -1).Value = file;

// Send the data to the server asynchronously


// Pass the cancellation token such that the command will be cancelled if needed
await cmd.ExecuteNonQueryAsync(cancellationToken);
}
}
}
}
}
}

Ejemplo: transmisión por secuencias de un SQL Server a otro SQL


Server
Este ejemplo muestra cómo transmitir de forma asincrónica un BLOB grande de un SQL Server a otro, con
compatibilidad para la cancelación.

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace StreamingFromServerToAnother {
class Program {
// Replace the connection string if needed, for instance to connect to SQL Express: @"Server=
(local)\SQLEXPRESS;Database=Demo2;Integrated Security=true"
private const string connectionString = @"Server=(localdb)\V11.0;Database=Demo2";

static void Main(string[] args) {


static void Main(string[] args) {
// For this example, we don't want to cancel
// So we can pass in a "blank" cancellation token
E2EStream(CancellationToken.None).Wait();

Console.WriteLine("Done");
}

// Streaming from one SQL Server to Another One using the new Async.NET
private static async Task E2EStream(CancellationToken cancellationToken) {
using (SqlConnection readConn = new SqlConnection(connectionString)) {
using (SqlConnection writeConn = new SqlConnection(connectionString)) {

// Note that we are using the same cancellation token for calls to both connections\commands
// Also we can start both the connection opening asynchronously, and then wait for both to
complete
Task openReadConn = readConn.OpenAsync(cancellationToken);
Task openWriteConn = writeConn.OpenAsync(cancellationToken);
await Task.WhenAll(openReadConn, openWriteConn);

using (SqlCommand readCmd = new SqlCommand("SELECT [bindata] FROM [BinaryStreams]", readConn))


{
using (SqlCommand writeCmd = new SqlCommand("INSERT INTO [BinaryStreamsCopy] (bindata)
VALUES (@bindata)", writeConn)) {

// Add an empty parameter to the write command which will be used for the streams we are
copying
// Size is set to -1 to indicate "MAX"
SqlParameter streamParameter = writeCmd.Parameters.Add("@bindata", SqlDbType.Binary, -
1);

// The reader needs to be executed with the SequentialAccess behavior to enable network
streaming
// Otherwise ReadAsync will buffer the entire BLOB into memory which can cause
scalability issues or even OutOfMemoryExceptions
using (SqlDataReader reader = await
readCmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)) {
while (await reader.ReadAsync(cancellationToken)) {
// Grab a stream to the binary data in the source database
using (Stream dataStream = reader.GetStream(0)) {

// Set the parameter value to the stream source that was opened
streamParameter.Value = dataStream;

// Asynchronously send data from one database to another


await writeCmd.ExecuteNonQueryAsync(cancellationToken);
}
}
}
}
}
}
}
}
}
}

Vea también
Recuperar y modificar datos en ADO.NET
LINQ to DataSet
23/10/2019 • 2 minutes to read • Edit Online

LINQ to DataSet hace que sea más fácil y rápido consultar los datos almacenados en caché DataSet en un objeto.
En concreto, LINQ to DataSet simplifica las consultas, ya que permite a los desarrolladores escribir consultas
desde el propio lenguaje de programación, en lugar de usar un lenguaje de consulta independiente. Esto es
especialmente útil para los desarrolladores de Visual Studio, que ahora pueden aprovechar las ventajas de la
comprobación de sintaxis en tiempo de compilación, los tipos estáticos y la compatibilidad con IntelliSense que
proporciona Visual Studio en sus consultas.
LINQ to DataSet también puede usarse para consultar los datos que se han consolidado de uno o varios orígenes
de datos. Esto permite muchos casos que requieren flexibilidad en la forma de representar y controlar los datos,
como consultar datos agregados localmente y almacenar en caché en el nivel medio en aplicaciones web. En
concreto, las aplicaciones de inteligencia empresaria, análisis e informes genéricos requieren este método de
manipulación.
La funcionalidad de LINQ to DataSet se expone principalmente a través de los métodos DataRowExtensions de
DataTableExtensions extensión de las clases y. LINQ to DataSet se basa en y usa la arquitectura de ADO.NET
existente y no está diseñada para reemplazar ADO.NET en el código de la aplicación. El código de ADO.NET
existente seguirá funcionando en una aplicación LINQ to DataSet. La relación de LINQ to DataSet con ADO.NET y
el almacén de datos se muestra en el diagrama siguiente.

En esta sección
Introducción
Guía de programación

Referencia
DataTableExtensions
DataRowExtensions
DataRowComparer
Vea también
Language Integrated Query (LINQ) (C#)
Language Integrated Query (LINQ) (Visual Basic)
LINQ y ADO.NET
ADO.NET
Introducción (LINQ to DataSet)
23/10/2019 • 2 minutes to read • Edit Online

En esta sección se proporciona información introductoria sobre la programación con LINQ to DataSet.

En esta sección
Información general de LINQ to DataSet
Proporciona información general conceptual de LINQ to DataSet.
Carga de datos en un conjunto de datos
Proporciona un ejemplo de llenado de DataSet. Este ejemplo utiliza DataAdapter para recuperar datos de la base
de datos.
Descargar bases de datos de ejemplo
Proporciona información sobre cómo descargar la base de datos de ejemplo AdventureWorks, que se usa en los
ejemplos de en la sección LINQ to DataSet.
Procedimientos: Creación de un proyecto de LINQ to DataSet en Visual Studio
Proporciona información sobre cómo crear un proyecto de LINQ to DataSet en Visual Studio.

Referencia
DataRowComparer
DataRowExtensions
DataTableExtensions

Vea también
LINQ y ADO.NET
Language Integrated Query (LINQ) (C#)
Language Integrated Query (LINQ) (Visual Basic)
Información general de LINQ to DataSet
08/01/2020 • 7 minutes to read • Edit Online

La DataSet es uno de los componentes más utilizados de ADO.NET. Es un elemento clave del modelo de
programación desconectado en el que se basa ADO.NET y permite almacenar explícitamente en caché los datos de
diferentes orígenes de datos. Para el nivel de presentación, DataSet está estrechamente integrado en los controles
de GUI para el enlace de datos. Para el nivel medio, proporciona una caché que conserva la forma relacional de los
datos e incluye servicios de exploración de jerarquías y consultas rápidos y sencillos. Una técnica común que se
usa para reducir el número de solicitudes en una base de datos es usar el DataSet para el almacenamiento en
caché en el nivel intermedio. Por ejemplo, considere una aplicación Web ASP.NET controlada por datos. A menudo
una parte importante de los datos de aplicación no cambia frecuentemente y es común entre sesiones o usuarios.
Estos datos se pueden conservar en memoria o en un servidor web, lo que reduce el número de solicitudes en la
base de datos y acelera las interacciones del usuario. Otro aspecto útil del DataSet es que permite que una
aplicación lleve subconjuntos de datos de uno o varios orígenes de datos al espacio de la aplicación. La aplicación
puede manipular los datos en memoria mientras retiene su forma relacional.
A pesar de su importancia, DataSet tiene capacidades de consulta limitadas. El método Select se puede usar para
filtrar y ordenar y los métodos GetChildRows y GetParentRow se pueden usar para la exploración de jerarquías. Sin
embargo, para cualquier tarea más compleja, el programador debe escribir una consulta personalizada. Esto puede
tener como resultado aplicaciones con un bajo rendimiento y con un mantenimiento difícil.
LINQ to DataSet facilita y agiliza la consulta de datos almacenados en caché en un objeto DataSet. Esas consultas
se expresan en el lenguaje de programación mismo, en lugar de como literales de cadena incrustados en el código
de la aplicación. Esto significa que los desarrolladores no tienen que aprender un lenguaje de consultas diferente.
Además, LINQ to DataSet permite a los desarrolladores de Visual Studio trabajar de forma más productiva, ya que
el IDE de Visual Studio proporciona comprobación de sintaxis en tiempo de compilación, tipos estáticos y
compatibilidad con IntelliSense para LINQ. LINQ to DataSet también puede usarse para consultar los datos que se
han consolidado de uno o varios orígenes de datos. Esto permite muchos casos que requieren flexibilidad en la
forma de representar y controlar los datos. En concreto, las aplicaciones de inteligencia empresaria, análisis e
informes genéricos requieren este método de manipulación.

Consultar conjuntos de datos usando LINQ to DataSet


Antes de poder empezar a consultar un objeto de DataSet mediante LINQ to DataSet, debe rellenar el DataSet. Hay
varias maneras de cargar datos en un DataSet, como el uso de la clase DataAdapter o LINQ to SQL. Una vez que los
datos se han cargado en un objeto DataSet, puede empezar a consultarlos. La formulación de consultas mediante
LINQ to DataSet es similar al uso de Language-Integrated Query (LINQ) en otros orígenes de datos habilitados
para LINQ. Las consultas LINQ se pueden realizar en tablas únicas en un DataSet o en más de una tabla usando los
operadores de consulta estándar Join y GroupJoin.
Las consultas LINQ se admiten en objetos de DataSet con tipo y sin tipo. Si el esquema de DataSet es desconocido
en tiempo de diseño, se recomienda un DataSet con tipo. En un DataSet con tipo, las tablas y las filas tienen
miembros con tipo para cada una de las columnas, lo que hace que las consultas sean más sencillas y legibles.
Además de los operadores de consulta estándar implementados en System. Core. dll, LINQ to DataSet agrega
varias extensiones específicas de DataSetque facilitan la consulta en un conjunto de objetos DataRow. Estas
extensiones específicas de DataSet incluyen operadores para comparar secuencias de filas, así como métodos que
proporcionan acceso a los valores de columna de un DataRow.

Aplicaciones con n niveles y LINQ to DataSet


Las aplicaciones de datos con n niveles son aplicaciones centradas en datos que están separadas en varias capas
lógicas (o niveles). Una aplicación con n capas típica incluye una capa de presentación, una capa media y una capa
de datos. Al separar los componentes de la aplicación en niveles independientes, se aumenta la facilidad de
mantenimiento y la escalabilidad de la aplicación. Para obtener más información sobre las aplicaciones de datos
con N niveles, vea trabajar con conjuntos de datos en aplicaciones de n niveles.
En las aplicaciones con n niveles, a menudo se utiliza DataSet en el nivel medio para almacenar en caché
información para una aplicación web. La funcionalidad de consulta de LINQ to DataSet se implementa a través de
métodos de extensión y extiende el DataSetADO.NET 2,0 existente.

Vea también
Consulta de conjuntos de datos
Language Integrated Query (LINQ) (C#)
Language Integrated Query (LINQ) (Visual Basic)
LINQ to SQL
Cargar datos en un conjunto de datos
23/10/2019 • 4 minutes to read • Edit Online

Primero DataSet se debe rellenar un objeto para poder realizar consultas sobre él con LINQ to DataSet.
Existen varias formas de rellenar DataSet. Por ejemplo, puede utilizar LINQ to SQL para consultar la base de
datos y cargar los resultados DataSeten. Para más información, vea LINQ to SQL.
Otra forma habitual de cargar datos en DataSet es utilizar la clase DataAdapter para recuperar datos desde
la base de datos. Esto se muestra en el ejemplo siguiente.

Ejemplo
En este ejemplo se utiliza DataAdapter para consultar en la base de datos AdventureWorks la información
de ventas del año 2002, y cargar los resultados en un DataSet. DataSet Una vez que se ha rellenado, puede
escribir consultas en él mediante LINQ to DataSet. El FillDataSet método de este ejemplo se usa en las
consultas de ejemplo de LINQ to DataSet ejemplos. Para obtener más información, vea consultar
conjuntosde datos.

try
{
// Create a new adapter and give it a query to fetch sales order, contact,
// address, and product information for sales in the year 2002. Point connection
// information to the configuration setting "AdventureWorks".
string connectionString = "Data Source=localhost;Initial Catalog=AdventureWorks;"
+ "Integrated Security=true;";

SqlDataAdapter da = new SqlDataAdapter(


"SELECT SalesOrderID, ContactID, OrderDate, OnlineOrderFlag, " +
"TotalDue, SalesOrderNumber, Status, ShipToAddressID, BillToAddressID " +
"FROM Sales.SalesOrderHeader " +
"WHERE DATEPART(YEAR, OrderDate) = @year; " +

"SELECT d.SalesOrderID, d.SalesOrderDetailID, d.OrderQty, " +


"d.ProductID, d.UnitPrice " +
"FROM Sales.SalesOrderDetail d " +
"INNER JOIN Sales.SalesOrderHeader h " +
"ON d.SalesOrderID = h.SalesOrderID " +
"WHERE DATEPART(YEAR, OrderDate) = @year; " +

"SELECT p.ProductID, p.Name, p.ProductNumber, p.MakeFlag, " +


"p.Color, p.ListPrice, p.Size, p.Class, p.Style, p.Weight " +
"FROM Production.Product p; " +

"SELECT DISTINCT a.AddressID, a.AddressLine1, a.AddressLine2, " +


"a.City, a.StateProvinceID, a.PostalCode " +
"FROM Person.Address a " +
"INNER JOIN Sales.SalesOrderHeader h " +
"ON a.AddressID = h.ShipToAddressID OR a.AddressID = h.BillToAddressID " +
"WHERE DATEPART(YEAR, OrderDate) = @year; " +

"SELECT DISTINCT c.ContactID, c.Title, c.FirstName, " +


"c.LastName, c.EmailAddress, c.Phone " +
"FROM Person.Contact c " +
"INNER JOIN Sales.SalesOrderHeader h " +
"ON c.ContactID = h.ContactID " +
"WHERE DATEPART(YEAR, OrderDate) = @year;",
connectionString);

// Add table mappings.


// Add table mappings.
da.SelectCommand.Parameters.AddWithValue("@year", 2002);
da.TableMappings.Add("Table", "SalesOrderHeader");
da.TableMappings.Add("Table1", "SalesOrderDetail");
da.TableMappings.Add("Table2", "Product");
da.TableMappings.Add("Table3", "Address");
da.TableMappings.Add("Table4", "Contact");

// Fill the DataSet.


da.Fill(ds);

// Add data relations.


DataTable orderHeader = ds.Tables["SalesOrderHeader"];
DataTable orderDetail = ds.Tables["SalesOrderDetail"];
DataRelation order = new DataRelation("SalesOrderHeaderDetail",
orderHeader.Columns["SalesOrderID"],
orderDetail.Columns["SalesOrderID"], true);
ds.Relations.Add(order);

DataTable contact = ds.Tables["Contact"];


DataTable orderHeader2 = ds.Tables["SalesOrderHeader"];
DataRelation orderContact = new DataRelation("SalesOrderContact",
contact.Columns["ContactID"],
orderHeader2.Columns["ContactID"], true);
ds.Relations.Add(orderContact);
}
catch (SqlException ex)
{
Console.WriteLine("SQL exception occurred: " + ex.Message);
}
Try
Dim connectionString As String

connectionString = "Data Source=localhost;Initial Catalog=AdventureWorks;" & _


"Integrated Security=true;"

' Create a new adapter and give it a query to fetch sales order, contact,
' address, and product information for sales in the year 2002. Point connection
' information to the configuration setting "AdventureWorks".
Dim da = New SqlDataAdapter( _
"SELECT SalesOrderID, ContactID, OrderDate, OnlineOrderFlag, " & _
"TotalDue, SalesOrderNumber, Status, ShipToAddressID, BillToAddressID " & _
"FROM Sales.SalesOrderHeader " & _
"WHERE DATEPART(YEAR, OrderDate) = @year; " & _
"SELECT d.SalesOrderID, d.SalesOrderDetailID, d.OrderQty, " & _
"d.ProductID, d.UnitPrice " & _
"FROM Sales.SalesOrderDetail d " & _
"INNER JOIN Sales.SalesOrderHeader h " & _
"ON d.SalesOrderID = h.SalesOrderID " & _
"WHERE DATEPART(YEAR, OrderDate) = @year; " & _
"SELECT p.ProductID, p.Name, p.ProductNumber, p.MakeFlag, " & _
"p.Color, p.ListPrice, p.Size, p.Class, p.Style " & _
"FROM Production.Product p; " & _
"SELECT DISTINCT a.AddressID, a.AddressLine1, a.AddressLine2, " & _
"a.City, a.StateProvinceID, a.PostalCode " & _
"FROM Person.Address a " & _
"INNER JOIN Sales.SalesOrderHeader h " & _
"ON a.AddressID = h.ShipToAddressID OR a.AddressID = h.BillToAddressID " & _
"WHERE DATEPART(YEAR, OrderDate) = @year; " & _
"SELECT DISTINCT c.ContactID, c.Title, c.FirstName, " & _
"c.LastName, c.EmailAddress, c.Phone " & _
"FROM Person.Contact c " & _
"INNER JOIN Sales.SalesOrderHeader h " & _
"ON c.ContactID = h.ContactID " & _
"WHERE DATEPART(YEAR, OrderDate) = @year;", _
connectionString)

' Add table mappings.


da.SelectCommand.Parameters.AddWithValue("@year", 2002)
da.TableMappings.Add("Table", "SalesOrderHeader")
da.TableMappings.Add("Table1", "SalesOrderDetail")
da.TableMappings.Add("Table2", "Product")
da.TableMappings.Add("Table3", "Address")
da.TableMappings.Add("Table4", "Contact")

da.Fill(ds)

' Add data relations.


Dim orderHeader As DataTable = ds.Tables("SalesOrderHeader")
Dim orderDetail As DataTable = ds.Tables("SalesOrderDetail")
Dim co As DataRelation = New DataRelation("SalesOrderHeaderDetail", _
orderHeader.Columns("SalesOrderID"), _
orderDetail.Columns("SalesOrderID"), True)
ds.Relations.Add(co)

Dim contact As DataTable = ds.Tables("Contact")


Dim orderContact As DataRelation = New DataRelation("SalesOrderContact", _
contact.Columns("ContactID"), _
orderHeader.Columns("ContactID"), True)
ds.Relations.Add(orderContact)
Catch ex As SqlException
Console.WriteLine("SQL exception occurred: " & ex.Message)
End Try

Vea también
Información general de LINQ to DataSet
Consulta de conjuntos de datos
Ejemplos de LINQ to DataSet
Descargar bases de datos de ejemplo (LINQ to
DataSet)
23/10/2019 • 5 minutes to read • Edit Online

En los ejemplos y los tutoriales de la documentación de LINQ to DataSet se usa la base de datos de ejemplo
AdventureWorks. Puede descargar este producto de forma gratuita del sitio de descarga de Microsoft. Los
ejemplos y los tutoriales de la documentación de LINQ to DataSet usan SQL Server como almacén de datos. SQL
Server Express Edition, que está disponible de forma gratuita, también se puede usar como almacén de datos en
lugar de SQL Server.

Descargar e instalar la base de datos de AdventureWorks


Para descargar e instalar la base de datos de ejemplo de AdventureWorks para SQL Server
1. Abra Internet Explorer.
2. Vaya al sitio Web ejemplos de SQL Server 2005 y bases de datos de ejemplo .
3. Siga las instrucciones para descargar la base de datos de ejemplo para el tipo de procesador (por ejemplo,
AdventureWorksDB.msi) y guarde el archivo .MSI en su equipo local.
4. Si tiene una versión anterior de AdventureWorks instalada desde la descarga o durante la configuración de
SQL Server, debe quitarla antes de ejecutar AdventureWorks.msi.
Para quitar una descarga anterior de una base de datos de ejemplo de AdventureWorks
1. Quite la base de datos de AdventureWorks o AdventureWorksDW.
2. En Agregar o quitar programas , seleccione AdventureWorksDB o AdventureWorksBI y haga clic en
quitar .
Para quitar una base de datos de ejemplo de AdventureWorks instalada previamente durante la configuración
1. Quite la base de datos de AdventureWorks o AdventureWorksDW.
2. En Agregar o quitar programas , seleccione Microsoft SQL Ser ver 2005 y haga clic en cambiar .
3. En selección de componentes , seleccione componentes de la estación de trabajo y, a continuación,
haga clic en siguiente .
4. En la Página principal del Asistente para la instalación de SQL Ser ver , haga clic en siguiente .
5. En comprobación de la configuración del sistema , haga clic en siguiente .
6. En cambiar o quitar instancia , haga clic en cambiar componentes instalados .
7. En selección de características , expanda el nodo documentación, ejemplos y bases de datos de
ejemplo .
8. Seleccione código y aplicaciones de ejemplo . Expanda bases de datos de ejemplo , seleccione la base de
datos de ejemplo que se va a quitar y seleccione la característica completa no estará disponible . Haga
clic en Next .
9. Haga clic en instalar y finalice el Asistente para la instalación.
Para adjuntar los archivos de base de datos de ejemplo de AdventureWorks a una instancia de SQL Server
1. Una vez descargado el archivo del instalador de la base de datos de ejemplo de archivo, haga doble clic en el
archivo AdventureWorksDB. msi (o en el archivo que descargó) para instalar la base de datos. De forma
predeterminada, la base de datos se instala en c:\Archivos de programa\Microsoft SQL
Server\MSSQL.1\MSSQL\Data.
2. Adjunte los archivos de la base de datos de AdventureWorks a una instancia de SQL Server ejecutando el
siguiente script SQLCMD o SQL Server Management Studio:

exec sp_attach_db @dbname=N'AdventureWorks', @filename1=N'C:\Program Files\Microsoft SQL


Server\MSSQL.1\MSSQL\Data\AdventureWorks_Data.mdf', @filename2=N'C:\Program Files\Microsoft SQL
Server\MSSQL.1\MSSQL\Data\AdventureWorks_log.ldf'

Si ha instalado esos archivos en una unidad o en un directorio diferente, debe revisar las rutas de acceso
correctamente antes de ejecutar el procedimiento almacenado sp_attach_db .

Descargar SQL Server Express Edition


En los ejemplos y los tutoriales de la sección LINQ to DataSet se usa SQL Server 2005 como almacén de datos,
pero se pueden modificar para usar SQL Server Express Edition, en su lugar. SQL Server Express Edition está
disponible gratuitamente y puede redistribuirse con aplicaciones. Si usa Visual Studio, SQL Server Express Edition
se incluye en las ediciones Pro y superior.
Para descargar e instalar SQL Server Express Edition
1. Inicie Internet Explorer.
2. Vaya a la página de descarga de Microsoft SQL Server 2005 Express Edition .
3. Siga las instrucciones de instalación del sitio web.

Vea también
Introducción
Cómo: crear un proyecto de LINQ to DataSet en
Visual Studio
04/12/2019 • 2 minutes to read • Edit Online

Los diferentes tipos de proyectos de LINQ requieren ciertas referencias de ensamblado y espacios de nombres
importados (Visual Basic) o mediante directivas (C#). El requisito mínimo para LINQ es una referencia a
System. Core. dll y una directiva de using para System.Linq.
Estos requisitos se proporcionan de forma predeterminada si crea un nuevo C# proyecto de aplicación de
consola en Visual Studio 2017 o una versión posterior. Si va a actualizar un proyecto de una versión anterior de
Visual Studio, es posible que tenga que proporcionar estas referencias relacionadas con LINQ de forma
manual.
LINQ to DataSet requiere dos referencias adicionales a System. Data. dll y System. Data. DataSetExtensions. dll.

NOTE
Si va a compilar desde un símbolo del sistema, debe hacer referencia manualmente a las DLL relacionadas con LINQ en
%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.5.

Para habilitar la funcionalidad LINQ to DataSet


Siga estos pasos para habilitar LINQ to DataSet funcionalidad en un proyecto existente.
1. Agregue referencias a System. Core , System. Data y System. Data. DataSetExtensions .
En Explorador de soluciones , haga clic con el botón secundario en el nodo referencias y seleccione
Agregar referencia . En el cuadro de diálogo Administrador de referencias , seleccione System.
Core , System. Data y System. Data. DataSetExtensions . Seleccione Aceptar .
2. Agregue directivas using (o instrucciones Imports en Visual Basic) para System. Data y System. Linq .

using System.Data;
using System.Linq;

3. Opcionalmente, agregue una using Directiva (o Imports instrucción) para System. Data. Common o
System. Data. SqlClient , dependiendo de cómo se conecte a la base de datos.

Vea también
Introducción a LINQ to DataSet
Guía de programación (LINQ to DataSet)
08/01/2020 • 2 minutes to read • Edit Online

En esta sección se proporciona información conceptual y ejemplos para programar con LINQ to DataSet.

Esta sección
Consultas en LINQ to DataSet
Proporciona información sobre cómo escribir consultas de LINQ to DataSet.
Consulta de conjuntos de datos
Describe cómo consultar objetos DataSet.
Comparación de objetos DataRow
Describe cómo utilizar el objeto DataRowComparer para comparar filas de datos.
Creación de un objeto DataTable a partir de una consulta
Proporciona información sobre cómo crear un DataTable a partir de una consulta de LINQ to DataSet mediante el
método CopyToDataTable.
Cómo: implementar CopyToDataTable<T > donde el tipo genérico T no es DataRow
Describe cómo implementar un método CopyToDataTable<T> personalizado en el que el parámetro genérico T no
es de tipo DataRow.
Métodos genéricos Field y SetField
Proporciona información acerca de los métodos genéricos Field y SetField.
Enlace de datos y LINQ to DataSet
Describe el enlace de datos mediante el objeto DataView.
Depuración de consultas de LINQ to DataSet
Proporciona información sobre la depuración y solución de problemas de consultas LINQ to DataSet.
Seguridad
Describe problemas de seguridad en LINQ to DataSet.
Ejemplos de LINQ to DataSet
Proporciona ejemplos de consultas que utilizan los operadores de LINQ.

Referencia
DataRowComparer
DataRowExtensions
DataTableExtensions
DataView

Vea también
LINQ y ADO.NET
Language-Integrated Query (LINQ)
Consultas en LINQ to DataSet
08/01/2020 • 12 minutes to read • Edit Online

Una consulta es una expresión que recupera datos de un origen de datos. Las consultas se suelen expresar en un
lenguaje de consulta especializado, como SQL para bases de datos relacionales y XQuery para XML. Por lo tanto,
los programadores han tenido que aprender un lenguaje de consultas nuevo para cada tipo de origen de datos o
formato de datos que consultan. Language-Integrated Query (LINQ) ofrece un modelo coherente y más sencillo
para trabajar con los datos de varios formatos y orígenes de datos. En una consulta de LINQ siempre se trabaja
con objetos de programación.
Una operación de consulta de LINQ consta de tres acciones: obtener el origen o los orígenes de datos, crear la
consulta y ejecutar la consulta.
Los orígenes de datos que implementan la interfaz genérica IEnumerable<T> se pueden consultar a través de
LINQ. La llamada a AsEnumerable en un DataTable devuelve un objeto que implementa la interfaz genérica de
IEnumerable<T>, que actúa como origen de datos para las consultas de LINQ to DataSet.
En la consulta se especifica exactamente la información que se desea recuperar del origen de datos. Una consulta
también puede especificar cómo se debe ordenar, agrupar y conformar esa información antes de que se devuelva.
En LINQ, una consulta se almacena en una variable. Si la consulta está diseñada para devolver una secuencia de
valores, la variable misma de la consulta debe ser de tipo enumerable. Esta variable de consulta no realiza ninguna
acción y no devuelve datos; solamente almacena la información de la consulta. Tras crear una consulta debe
ejecutarla para recuperar los datos.
En una consulta que devuelve una secuencia de valores, la variable de consulta por sí misma nunca conserva los
resultados de la consulta y solo almacena los comandos de la misma. La ejecución de la consulta se aplaza hasta
que la variable de consulta se recorre en iteración en un bucle foreach o For Each . Esto se denomina ejecución
aplazada; es decir, la ejecución de la consulta se produce una vez después de la construcción de la consulta. Esto
significa que se puede ejecutar una consulta con la frecuencia que se desee. Esto es útil cuando, por ejemplo, se
tiene una base de datos que otras aplicaciones están actualizando. En su aplicación puede crear una consulta para
recuperar la información más reciente y ejecutar de forma repetida la consulta, devolviendo cada vez la
información actualizada.
A diferencia de las consultas en diferido, que devuelven una secuencia de valores, las consultas que devuelven un
valor singleton se ejecutan inmediatamente. Algunos ejemplos de consultas singleton son Count, Max, Average y
First. Se ejecutan inmediatamente porque se necesitan los resultados de la consulta para calcular el resultado
singleton. Por ejemplo, para buscar la media de los resultados de consultas, debe ejecutarse la consulta para que la
función de cálculo de media tenga datos de entrada con los que trabajar. También puede usar los métodos ToList o
ToArray en una consulta para forzar la ejecución inmediata de una consulta que no crea un valor singleton. Esas
técnicas para forzar la ejecución inmediata pueden ser útiles si desea almacenar en caché los resultados de una
consulta.

Consultas
LINQ to DataSet consultas se pueden formular en dos sintaxis diferentes: sintaxis de expresiones de consulta y
sintaxis de consulta basada en métodos.
Sintaxis de la expresión de consulta de
Las expresiones de consulta son una sintaxis de consulta declarativa. Esta sintaxis permite a un desarrollador
escribir consultas en C# o Visual Basic en un formato similar a SQL. Si se utiliza la sintaxis de expresiones de
consulta, se pueden realizar incluso operaciones complejas de filtrado, ordenación y agrupamiento en orígenes de
datos con código mínimo. Para obtener más información, vea expresiones de consulta LINQ y operaciones básicas
de consulta (Visual Basic).
El Common Language Runtime de .NET Framework (CLR) no puede leer la sintaxis de expresiones de consulta en
sí. Por lo tanto, en tiempo de compilación, las expresiones de consulta se traducen a algo que CLR no comprende:
las llamadas a métodos. Estos métodos se conocen como operadores de consulta estándar. Como programador,
tiene la opción de llamarlos directamente utilizando la sintaxis de método en lugar de la sintaxis de consulta. Para
obtener más información, vea Query Syntax and Method Syntax in LINQ (Sintaxis de consulta y sintaxis de método
en LINQ). Para obtener más información acerca de los operadores de consulta estándar, vea información general
sobre operadores de consulta estándar.
El siguiente ejemplo usa Select para devolver todas las filas de la tabla Product y mostrar los nombres de
producto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
from product in products.AsEnumerable()
select product;

Console.WriteLine("Product Names:");
foreach (DataRow p in query)
{
Console.WriteLine(p.Field<string>("Name"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = From product In products.AsEnumerable() _


Select product
Console.WriteLine("Product Names:")
For Each p In query
Console.WriteLine(p.Field(Of String)("Name"))
Next

Sintaxis de consultas basadas en métodos


La otra forma de formular consultas LINQ to DataSet es mediante el uso de consultas basadas en métodos. La
sintaxis de las consultas basadas en métodos es una secuencia de llamadas de método directo a los métodos de
operador de LINQ, pasando expresiones lambda como parámetros. Para obtener más información, vea Expresiones
lambda.
Este ejemplo usa Select para devolver todas las filas de Product y mostrar los nombres de producto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query = products.AsEnumerable().


Select(product => new
{
ProductName = product.Field<string>("Name"),
ProductNumber = product.Field<string>("ProductNumber"),
Price = product.Field<decimal>("ListPrice")
});

Console.WriteLine("Product Info:");
foreach (var productInfo in query)
{
Console.WriteLine("Product name: {0} Product number: {1} List price: ${2} ",
productInfo.ProductName, productInfo.ProductNumber, productInfo.Price);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = products.AsEnumerable() _


.Select(Function(product As DataRow) New With _
{ _
.ProductName = product.Field(Of String)("Name"), _
.ProductNumber = product.Field(Of String)("ProductNumber"), _
.Price = product.Field(Of Decimal)("ListPrice") _
})

Console.WriteLine("Product Info:")
For Each product In query
Console.Write("Product name: " & product.ProductName)
Console.Write("Product number: " & product.ProductNumber)
Console.WriteLine("List price: $ " & product.Price)
Next

Crear consultas
Tal y como se ha mencionado anteriormente en este tema, la variable de consulta solo almacena los comandos de
la consulta cuando ésta se diseña para devolver una secuencia de valores. Si la consulta no contiene un método
que cause una ejecución inmediata, la ejecución real de la consulta se aplaza hasta que la variable de consulta se
recorra en iteración en un bucle foreach o For Each . La ejecución aplazada permite combinar varias consultas o
ampliar una consulta. Cuando se amplía una consulta, se modifica para incluir las nuevas operaciones. La ejecución
eventual reflejará los cambios. En el siguiente ejemplo, la primera consulta devuelve todos los productos. La
segunda consulta amplía la primera usando Where para devolver todos los productos del tamaño "L":
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> productsQuery =
from product in products.AsEnumerable()
select product;

IEnumerable<DataRow> largeProducts =
productsQuery.Where(p => p.Field<string>("Size") == "L");

Console.WriteLine("Products of size 'L':");


foreach (DataRow product in largeProducts)
{
Console.WriteLine(product.Field<string>("Name"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim productsQuery = From product In products.AsEnumerable() _


Select product

Dim largeProducts = _
productsQuery.Where(Function(p) p.Field(Of String)("Size") = "L")

Console.WriteLine("Products of size 'L':")


For Each product In largeProducts
Console.WriteLine(product.Field(Of String)("Name"))
Next

Una vez que se ha ejecutado una consulta, no se pueden crear consultas adicionales y todas las consultas
posteriores utilizarán los operadores de LINQ en memoria. La ejecución de la consulta se producirá al recorrer en
iteración la variable de consulta en una instrucción foreach o For Each , o mediante una llamada a uno de los
operadores de conversión de LINQ que causan la ejecución inmediata. Entre estos operadores se incluyen los
siguientes: ToList, ToArray, ToLookup y ToDictionary.
En el siguiente ejemplo, la primera consulta devuelve todos los productos ordenados por el precio de la lista. El
método ToArray se usa para forzar la ejecución inmediata de la consulta:
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
from product in products.AsEnumerable()
orderby product.Field<Decimal>("ListPrice") descending
select product;

// Force immediate execution of the query.


IEnumerable<DataRow> productsArray = query.ToArray();

Console.WriteLine("Every price from highest to lowest:");


foreach (DataRow prod in productsArray)
{
Console.WriteLine(prod.Field<Decimal>("ListPrice"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of Decimal)("ListPrice") Descending _
Select product

' Force immediate execution of the query.


Dim productsArray = query.ToArray()

Console.WriteLine("Every price From highest to lowest:")


For Each prod In productsArray
Console.WriteLine(prod.Field(Of Decimal)("ListPrice"))
Next

Vea también
Guía de programación
Consulta de conjuntos de datos
Introducción a LINQ en C#
Introducción a LINQ en Visual Basic
Consultar conjuntos de datos (LINQ to DataSet)
08/01/2020 • 2 minutes to read • Edit Online

Después de rellenar un objeto DataSet con datos se puede empezar a realizar consultas sobre él. La formulación
de consultas con LINQ to DataSet es similar a usar Language-Integrated Query (LINQ) en otros orígenes de datos
habilitados para LINQ. No obstante, recuerde que cuando utiliza consultas LINQ sobre un objeto DataSet, está
consultando una enumeración de objetos de DataRow en lugar de una enumeración de un tipo personalizado.
Esto significa que puede usar cualquiera de los miembros de la clase DataRow en las consultas LINQ. Esto permite
crear consultas enriquecidas y complejas.
Como con otras implementaciones de LINQ, puede crear LINQ to DataSet consultas de dos formas diferentes:
sintaxis de expresiones de consulta y sintaxis de consulta basada en métodos. Puede usar la sintaxis de expresión
de consulta o la sintaxis de consulta basada en métodos para realizar consultas en tablas únicas en un DataSet, en
tablas múltiples en un DataSet o en tablas en un DataSet con tipo.

Esta sección
Consultas de tabla única
Describe cómo realizar consultas en una sola tabla.
Consultas entre tablas
Describe cómo realizar consultas entre tablas.
Consultar objetos DataSet con tipo
Describe cómo consultar objetos DataSet con tipo.

Vea también
Ejemplos de LINQ to DataSet
Carga de datos en un conjunto de datos
Consultas de tabla única (LINQ to DataSet)
27/03/2020 • 5 minutes to read • Edit Online

Las consultas de Language-Integrated Query (LINQ) IEnumerable<T> funcionan IQueryable<T> en orígenes de


datos que implementan la interfaz o la interfaz. La DataTable clase no implementa ninguna interfaz, AsEnumerable
por lo que DataTable debe llamar al From método si desea usar como origen en la cláusula de una consulta LINQ.
En el ejemplo siguiente se obtienen todos los pedidos en línea desde la tabla SalesOrderHeader y se envían los
resultados de id., fecha y número de pedido a la consola.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
where order.Field<bool>("OnlineOrderFlag") == true
select new
{
SalesOrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate"),
SalesOrderNumber = order.Field<string>("SalesOrderNumber")
};

foreach (var onlineOrder in query)


{
Console.WriteLine("Order ID: {0} Order date: {1:d} Order number: {2}",
onlineOrder.SalesOrderID,
onlineOrder.OrderDate,
onlineOrder.SalesOrderNumber);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True _
Select New With { _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
.SalesOrderNumber = order.Field(Of String)("SalesOrderNumber") _
}

For Each onlineOrder In query


Console.Write("Order ID: " & onlineOrder.SalesOrderID)
Console.Write(" Order date: " & onlineOrder.OrderDate)
Console.WriteLine(" Order number: " & onlineOrder.SalesOrderNumber)
Next
La consulta de variable local se inicializa con una expresión de consulta, que funciona en uno o varios orígenes de
información aplicando uno o DataSet varios operadores de consulta de los operadores de consulta estándar o, en
el caso de LINQ to DataSet, operadores específicos de la clase. La expresión de consulta del ejemplo anterior utiliza
dos de los operadores estándar de consulta: Where y Select .
La cláusula Where filtra la secuencia basándose en una condición, en este caso que OnlineOrderFlag se establezca
en true . El operador Select asigna y devuelve un objeto enumerable que captura los argumentos pasados al
operador. En el ejemplo anterior, se crea un tipo anónimo con tres propiedades: SalesOrderID , OrderDate y
SalesOrderNumber . Los valores de estas tres propiedades se establecen en los valores de las columnas
SalesOrderID , OrderDate y SalesOrderNumber a partir de la tabla SalesOrderHeader .

A continuación, el bucle foreach enumera el objeto enumerable devuelto por Select y produce los resultados de
la consulta. Dado que una consulta es un tipo Enumerable, que implementa IEnumerable<T>, la evaluación de la
consulta se difiere hasta que se procesa una iteración en la variable de la consulta mediante el bucle foreach . La
evaluación de la consulta en diferido permite que éstas se mantengan como valores que se pueden evaluar varias
veces, y cada vez produciendo resultados potencialmente diferentes.
El método Field proporciona acceso a los valores de columna de DataRow y SetField (que no se mostraba en el
ejemplo anterior) establece los valores de columna en DataRow. Tanto Field el SetField método como el método
controlan los tipos de valor que aceptan valores NULL, por lo que no es necesario comprobar explícitamente si hay
valores NULL. Además, ambos son métodos genéricos, lo que significa que no es necesario convertir el tipo de
valor devuelto. Puede utilizar el descriptor de acceso de columna preexistente en DataRow (por ejemplo,
o["OrderDate"] ), pero hacerlo le exigiría convertir el objeto de valor devuelto al tipo apropiado. Si la columna es un
tipo de valor que acepta valores IsNull NULL, debe comprobar si el valor es null mediante el método. Para obtener
más información, vea Métodos genéricos Field y SetField.
Observe que el tipo de datos especificado en el parámetro T genérico de los métodos Field y SetField deben
coincidir con el tipo del valor subyacente; en caso contrario, se producirá una InvalidCastException. El nombre de
columna especificado debe también coincidir con el nombre de una columna en DataSet, en caso contrario, se
producirá una ArgumentException. En ambos casos, la excepción se produce en tiempo de ejecución de
enumeración de datos, cuando se ejecuta la consulta.

Vea también
Consultas entre tablas
Consultar objetos DataSet con tipo
Métodos genéricos Field y SetField
Consultas entre tablas (LINQ to DataSet)
08/01/2020 • 4 minutes to read • Edit Online

Además de consultar una sola tabla, también puede realizar consultas entre tablas en LINQ to DataSet. Esto se
realiza mediante una combinación. Una combinación es la asociación de objetos en un origen de datos con objetos
que comparten un atributo común en otro origen de datos, como un identificador de contacto o de producto. En la
programación orientada a objetos, las relaciones entre dichos objetos son relativamente fáciles de navegar debido
a que cada uno de ellos tiene un miembro que hace referencia a otro. Sin embargo, en tablas de bases de datos
externas, navegar por relaciones no es tan sencillo. Las tablas de bases de datos no contienen relaciones
integradas. En estos casos, la operación de combinación se puede utilizar para hacer coincidir elementos de cada
origen. Por ejemplo, con dos tablas que contienen información de producto y de ventas, se puede utilizar una
operación de combinación para hacer coincidir información de ventas y producto del mismo pedido de ventas.
El marco Language-Integrated Query (LINQ) proporciona dos operadores de combinación, Join y GroupJoin. Estos
operadores realizan combinaciones de igualdad: es decir, combinaciones que coinciden con dos orígenes de datos
solo cuando sus claves son iguales. (Por el contrario, Transact-SQL admite operadores de combinación distintos de
equals , como el operador less than ).

En cuanto a las bases de datos relacionales, Join implementa una combinación interna. Una combinación interna
es un tipo de combinación en la que solamente se devuelven los objetos que tienen una correspondencia en el
conjunto de datos opuesto.
Los operadores GroupJoin no tienen ningún equivalente directo en términos de bases de datos relacionales;
implementan un superconjunto de combinaciones internas y combinaciones externas izquierdas. Una combinación
externa izquierda es una combinación que devuelve cada elemento de la primera colección (izquierda), aunque
éste no tenga elementos correlacionados en la segunda colección.
Para obtener más información acerca de las combinaciones, vea operaciones de combinación.

Ejemplo
El ejemplo siguiente realiza una combinación tradicional de las tablas SalesOrderHeader y SalesOrderDetail de la
base de datos de ejemplo AdventureWorks para obtener pedidos en línea del mes de agosto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];


DataTable details = ds.Tables["SalesOrderDetail"];

var query =
from order in orders.AsEnumerable()
join detail in details.AsEnumerable()
on order.Field<int>("SalesOrderID") equals
detail.Field<int>("SalesOrderID")
where order.Field<bool>("OnlineOrderFlag") == true
&& order.Field<DateTime>("OrderDate").Month == 8
select new
{
SalesOrderID =
order.Field<int>("SalesOrderID"),
SalesOrderDetailID =
detail.Field<int>("SalesOrderDetailID"),
OrderDate =
order.Field<DateTime>("OrderDate"),
ProductID =
detail.Field<int>("ProductID")
};

foreach (var order in query)


{
Console.WriteLine("{0}\t{1}\t{2:d}\t{3}",
order.SalesOrderID,
order.SalesOrderDetailID,
order.OrderDate,
order.ProductID);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")


Dim details As DataTable = ds.Tables("SalesOrderDetail")

Dim query = _
From order In orders.AsEnumerable() _
Join detail In details.AsEnumerable() _
On order.Field(Of Integer)("SalesOrderID") Equals _
detail.Field(Of Integer)("SalesOrderID") _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True And _
order.Field(Of DateTime)("OrderDate").Month = 8 _
Select New With _
{ _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.SalesOrderDetailID = detail.Field(Of Integer)("SalesOrderDetailID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
.ProductID = detail.Field(Of Integer)("ProductID") _
}

For Each order In query


Console.WriteLine(order.SalesOrderID & vbTab & _
order.SalesOrderDetailID & vbTab & _
order.OrderDate & vbTab & _
order.ProductID)
Next

Vea también
Consulta de conjuntos de datos
Consultas de tabla única
Consultar objetos DataSet con tipo
Operaciones de combinación
Ejemplos de LINQ to DataSet
Consultar conjuntos de DataSet con tipo
23/10/2019 • 2 minutes to read • Edit Online

Si el esquema de se DataSet conoce en tiempo de diseño de la aplicación, se recomienda usar un DataSet tipo
cuando se use LINQ to DataSet. Un con tipo DataSet es una clase que se deriva DataSetde. Como tal, hereda todos
los métodos, eventos y propiedades de un DataSet. Además, un con tipo DataSet proporciona métodos, eventos y
propiedades fuertemente tipados. Esto significa que se puede tener acceso a tablas y columnas por su nombre, en
lugar de utilizar métodos de una colección. Esto hace que las consultas sean más sencillas y más legibles. Para
obtener más información, vea conjuntos de datos con tipo.
LINQ to DataSet también admite la consulta en un tipo DataSet. Con un tipo DataSet, no es necesario utilizar el
método o SetField método genérico Field para tener acceso a los datos de la columna. Los nombres de propiedad
están disponibles en tiempo de compilación porque la información de tipo DataSetse incluye en. LINQ to DataSet
proporciona acceso a los valores de columna como tipo correcto, de modo que los errores de coincidencia de tipos
se detectan cuando se compila el código en lugar de en tiempo de ejecución.
Antes de poder empezar a consultar un tipo DataSet, debe generar la clase mediante el diseñador de DataSet en
Visual Studio. Para obtener más información, vea crear y configurar conjuntosde datos.

Ejemplo
En el siguiente ejemplo se muestra una consulta sobre un DataSet con tipo:

var query = from o in orders


where o.OnlineOrderFlag == true
select new { o.SalesOrderID,
o.OrderDate,
o.SalesOrderNumber };

foreach(var order in query)


{
Console.WriteLine("{0}\t{1:d}\t{2}",
order.SalesOrderID,
order.OrderDate,
order.SalesOrderNumber);
}

Dim orders = ds.Tables("SalesOrderHeader")

Dim query = _
From o In orders _
Where o.OnlineOrderFlag = True _
Select New {SalesOrderID := o.SalesOrderID, _
OrderDate := o.OrderDate, _
SalesOrderNumber := o.SalesOrderNumber}

For Each Dim onlineOrder In query


Console.WriteLine("{0}\t{1:d}\t{2}", _
onlineOrder.SalesOrderID, _
onlineOrder.OrderDate, _
onlineOrder.SalesOrderNumber)
Next
Vea también
Consulta de conjuntos de datos
Consultas entre tablas
Consultas de tabla única
Comparar objetos DataRow (LINQ to DataSet)
08/01/2020 • 3 minutes to read • Edit Online

Language-Integrated Query (LINQ) define varios operadores de conjuntos para comparar los elementos de origen
y ver si son iguales. LINQ proporciona los siguientes operadores de conjuntos:
Distinct
Union
Intersect
Except
Estos operadores comparan elementos origen llamando a los métodos GetHashCode y Equals de cada colección
de elementos. En el caso de DataRow, estos operadores realizan una comparación de referencia, lo que en general
no constituye un comportamiento ideal para operaciones de conjunto en datos tabulares. Para las operaciones de
conjuntos, por lo general deseará determinar si los valores del elemento son iguales o no a las referencias del
elemento. Por lo tanto, se ha agregado la clase DataRowComparer a LINQ to DataSet. Esta clase se puede utilizar
para comparar valores de fila.
La clase DataRowComparer contiene una implementación de comparación del valor para DataRow, de modo que
esta clase se puede utilizar para operaciones de conjuntos como Distinct. No se puede crear una instancia
directamente de esta clase. En su lugar, debe utilizarse la propiedad Default para devolver una instancia de
DataRowComparer<TRow>. Entonces, se llama al método Equals y los dos objetos DataRow que se van a
comparar se pasan como parámetros de entrada. El método Equals devuelve true si los conjuntos ordenados de
valores de columna de ambos objetos DataRow son iguales; de lo contrario, devuelve false .

Ejemplo
Este ejemplo usa Intersect para devolver contactos que aparecen en ambas tablas.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contactTable = ds.Tables["Contact"];

// Create two tables.


IEnumerable<DataRow> query1 = from contact in contactTable.AsEnumerable()
where contact.Field<string>("Title") == "Ms."
select contact;

IEnumerable<DataRow> query2 = from contact in contactTable.AsEnumerable()


where contact.Field<string>("FirstName") == "Sandra"
select contact;

DataTable contacts1 = query1.CopyToDataTable();


DataTable contacts2 = query2.CopyToDataTable();

// Find the intersection of the two tables.


var contacts = contacts1.AsEnumerable().Intersect(contacts2.AsEnumerable(),
DataRowComparer.Default);

Console.WriteLine("Intersection of contacts tables");


foreach (DataRow row in contacts)
{
Console.WriteLine("Id: {0} {1} {2} {3}",
row["ContactID"], row["Title"], row["FirstName"], row["LastName"]);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contactTable As DataTable = ds.Tables("Contact")

Dim query1 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("Title") = "Ms." _
Select contact

Dim query2 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Sandra" _
Select contact

Dim contacts1 = query1.CopyToDataTable()


Dim contacts2 = query2.CopyToDataTable()

Dim contacts = contacts1.AsEnumerable() _


.Intersect(contacts2.AsEnumerable(), DataRowComparer.Default)

Console.WriteLine("Intersect of employees tables")

For Each row In contacts


Console.WriteLine("Id: {0} {1} {2} {3}", _
row("ContactID"), row("Title"), row("FirstName"), row("LastName"))
Next

Ejemplo
El ejemplo siguiente compara dos filas y obtiene sus códigos hash.
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

' Get two rows from the SalesOrderHeader table.


Dim table As DataTable = ds.Tables("SalesOrderHeader")
Dim left = table.Rows(0)
Dim right = table.Rows(1)

' Compare the two different rows.


Dim comparer As IEqualityComparer(Of DataRow) = DataRowComparer.Default
Dim bEqual = comparer.Equals(left, right)

If (bEqual = True) Then


Console.WriteLine("Two rows are equal")
Else
Console.WriteLine("Two rows are not equal")
End If

' Output the hash codes of the two rows.


Console.WriteLine("The hashcodes for the two rows are {0}, {1}", _
comparer.GetHashCode(left), _
comparer.GetHashCode(right))

Vea también
DataRowComparer
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Crear un objeto DataTable a partir de una consulta
(LINQ to DataSet)
21/03/2020 • 13 minutes to read • Edit Online

El enlace de datos es una utilización muy frecuente del objeto DataTable. El método CopyToDataTable toma los
resultados de una consulta y copia los datos en un objeto DataTable que puede utilizarse después para el enlace de
datos. Cuando las operaciones de datos se han realizado, el DataTable nuevo se vuelve a combinar en el DataTable
de origen.
El método CopyToDataTable utiliza el siguiente proceso para crear un DataTable a partir de una consulta:
1. El método CopyToDataTable clona un DataTable a partir de la tabla origen (un objeto DataTable que
implementa la interfaz IQueryable<T>). El IEnumerable origen se ha originado generalmente a partir de
una LINQ to DataSet expresión o consulta de método.
2. El esquema del DataTable clonado se compila a partir de las columnas del primer objeto DataRow
enumerado en la tabla origen; el nombre de la tabla clonada es el nombre de la tabla origen con la palabra
"query" anexada.
3. En cada fila de la tabla origen, el contenido de la fila se copia en un objetoDataRow nuevo, el cual, a
continuación, se inserta en la tabla clonada. Las propiedades RowState y RowError se mantienen en toda la
operación de copia. Si los objetos ArgumentException de origen pertenecen a tablas diferentes, se produce
unaDataRow.
4. Cuando todos los objetos DataTable de la tabla de entrada que se puede consultar se han copiado, se
devuelve el DataRow clonado. Si la secuencia origen no contiene objetos DataRow, el método devuelve un
DataTable vacío.
Llamar CopyToDataTable al método hace que se ejecute la consulta enlazada a la tabla de origen.
Cuando el método CopyToDataTable encuentra una referencia NULL o un tipo de valor que admite valores NULL
en una fila de la tabla origen, reemplaza el valor con Value. De este modo, los valores NULL se controlan
correctamente en el DataTable devuelto.
Nota: el método CopyToDataTable acepta como entrada una consulta que pueda devolver filas de varios objetos
DataTable o DataSet. El método CopyToDataTable copiará los datos pero no las propiedades desde los objetos
DataTable o DataSet de origen en el elemento DataTable que se va a devolver. Necesitará establecer explícitamente
las propiedades de los elementos DataTable devueltos, como Locale y TableName.
En el ejemplo siguiente se consultan en la tabla SalesOrderHeader los pedidos posteriores al 8 de agosto de 2001
y se utiliza el método CopyToDataTable para crear un DataTable a partir de esa consulta. A continuación, se enlaza
DataTable a un BindingSource, que actúa como proxy para un objeto DataGridView.
// Bind the System.Windows.Forms.DataGridView object
// to the System.Windows.Forms.BindingSource object.
dataGridView.DataSource = bindingSource;

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

// Query the SalesOrderHeader table for orders placed


// after August 8, 2001.
IEnumerable<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") > new DateTime(2001, 8, 1)
select order;

// Create a table from the query.


DataTable boundTable = query.CopyToDataTable<DataRow>();

// Bind the table to a System.Windows.Forms.BindingSource object,


// which acts as a proxy for a System.Windows.Forms.DataGridView object.
bindingSource.DataSource = boundTable;

' Bind the System.Windows.Forms.DataGridView object


' to the System.Windows.Forms.BindingSource object.
dataGridView.DataSource = bindingSource

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

' Query the SalesOrderHeader table for orders placed


' after August 8, 2001.
Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") > New DateTime(2001, 8, 1) _
Select order

' Create a table from the query.


Dim boundTable As DataTable = query.CopyToDataTable()

' Bind the table to a System.Windows.Forms.BindingSource object,


' which acts as a proxy for a System.Windows.Forms.DataGridView object.
bindingSource.DataSource = boundTable

Creación de un método<de> CopyToDataTable T personalizado


Los métodos CopyToDataTable existentes solo funcionan en un origen IEnumerable<T> en el que el parámetro
genérico T es de tipo DataRow. Aunque esto es útil, no permite la creación de tablas a partir de una secuencia de
tipos escalares, a partir de consultas que devuelven tipos anónimos, o a partir de consultas que realizan
combinaciones de tablas. Para obtener un ejemplo de CopyToDataTable cómo implementar dos métodos
personalizados que cargan una tabla de una secuencia de tipos escalares o anónimos, vea Cómo:
implementar<CopyToDataTable T> donde el tipo genérico T no es un DataRows.
Los ejemplos de esta sección utilizan los tipos personalizados siguientes:
public class Item
{
public int Id { get; set; }
public double Price { get; set; }
public string Genre { get; set; }
}

public class Book : Item


{
public string Author { get; set; }
}

public class Movie : Item


{
public string Director { get; set; }
}
Public Class Item
Private _Id As Int32
Private _Price As Double
Private _Genre As String

Public Property Id() As Int32


Get
Return Id
End Get
Set(ByVal value As Int32)
_Id = value
End Set
End Property

Public Property Price() As Double


Get
Return _Price
End Get
Set(ByVal value As Double)
_Price = value
End Set
End Property

Public Property Genre() As String


Get
Return _Genre
End Get
Set(ByVal value As String)
_Genre = value
End Set
End Property

End Class
Public Class Book
Inherits Item
Private _Author As String
Public Property Author() As String
Get
Return _Author
End Get
Set(ByVal value As String)
_Author = value
End Set
End Property
End Class

Public Class Movie


Inherits Item
Private _Director As String
Public Property Director() As String
Get
Return _Director
End Get
Set(ByVal value As String)
_Director = value
End Set
End Property

End Class

Ejemplo
En este ejemplo se realiza una combinación en las tablas SalesOrderHeader y SalesOrderDetail para obtener
pedidos en línea del mes de agosto y se crea una tabla a partir de la consulta.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];


DataTable details = ds.Tables["SalesOrderDetail"];

var query =
from order in orders.AsEnumerable()
join detail in details.AsEnumerable()
on order.Field<int>("SalesOrderID") equals
detail.Field<int>("SalesOrderID")
where order.Field<bool>("OnlineOrderFlag") == true
&& order.Field<DateTime>("OrderDate").Month == 8
select new
{
SalesOrderID =
order.Field<int>("SalesOrderID"),
SalesOrderDetailID =
detail.Field<int>("SalesOrderDetailID"),
OrderDate =
order.Field<DateTime>("OrderDate"),
ProductID =
detail.Field<int>("ProductID")
};

DataTable orderTable = query.CopyToDataTable();

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")


Dim details As DataTable = ds.Tables("SalesOrderDetail")

Dim query = _
From order In orders.AsEnumerable() _
Join detail In details.AsEnumerable() _
On order.Field(Of Integer)("SalesOrderID") Equals _
detail.Field(Of Integer)("SalesOrderID") _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True And _
order.Field(Of DateTime)("OrderDate").Month = 8 _
Select New With _
{ _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.SalesOrderDetailID = detail.Field(Of Integer)("SalesOrderDetailID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
.ProductID = detail.Field(Of Integer)("ProductID") _
}

Dim table As DataTable = query.CopyToDataTable()

Ejemplo
En el ejemplo siguiente se consulta una colección de elementos cuyo precio es superior a $ 9,99 y se crea una tabla
a partir de los resultados de la consulta.
// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Query for items with price greater than 9.99.


var query = from i in items
where i.Price > 9.99
orderby i.Price
select i;

// Load the query results into new DataTable.


DataTable table = query.CopyToDataTable();

Dim book1 As New Book()


book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book


book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie


movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie


movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item


items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

' Query for items with price greater than 9.99.


Dim query = From i In items _
Where i.Price > 9.99 _
Order By i.Price _
Select New With {i.Price, i.Genre}

Dim table As DataTable


table = query.CopyToDataTable()

Ejemplo
En el ejemplo siguiente se consulta una colección de elementos cuyo precio es superior a $ 9,99 y se proyectan los
resultados. La secuencia de tipos anónimos devuelta se carga en una tabla existente.
// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Create a table with a schema that matches that of the query results.
DataTable table = new DataTable();
table.Columns.Add("Price", typeof(int));
table.Columns.Add("Genre", typeof(string));

var query = from i in items


where i.Price > 9.99
orderby i.Price
select new { i.Price, i.Genre };

query.CopyToDataTable(table, LoadOption.PreserveChanges);

Dim book1 As New Book()


book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book


book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie


movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie


movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item


items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

' Create a table with a schema that matches that of the query results.
Dim table As DataTable = New DataTable()
table.Columns.Add("Price", GetType(Integer))
table.Columns.Add("Genre", GetType(String))

' Query for items with price greater than 9.99.


Dim query = From i In items _
Where i.Price > 9.99 _
Order By i.Price _
Select New With {i.Price, i.Genre}

query.CopyToDataTable(table, LoadOption.PreserveChanges)

Ejemplo
En el ejemplo siguiente se consulta una colección de elementos cuyo precio es superior a $ 9,99 y se proyectan los
resultados. La secuencia de tipos anónimos devuelta se carga en una tabla existente. El esquema de la tabla se
amplía automáticamente porque los tipos Book y Movies se derivan del tipo Item .

// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Load into an existing DataTable, expand the schema and


// autogenerate a new Id.
DataTable table = new DataTable();
DataColumn dc = table.Columns.Add("NewId", typeof(int));
dc.AutoIncrement = true;
table.Columns.Add("ExtraColumn", typeof(string));

var query = from i in items


where i.Price > 9.99
orderby i.Price
select new { i.Price, i.Genre };

query.CopyToDataTable(table, LoadOption.PreserveChanges);
Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book


book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie


movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie


movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item


items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

' Load into an existing DataTable, expand the schema and


' autogenerate a new Id.
Dim table As DataTable = New DataTable()
Dim dc As DataColumn = table.Columns.Add("NewId", GetType(Integer))
dc.AutoIncrement = True
table.Columns.Add("ExtraColumn", GetType(String))

Dim query = From i In items _


Where i.Price > 9.99 _
Order By i.Price _
Select New With {i.Price, i.Genre}

query.CopyToDataTable(table, LoadOption.PreserveChanges)

Ejemplo
En el ejemplo siguiente se consulta una colección de elementos cuyo precio es superior a $ 9,99 y se devuelve una
secuencia de Double, que se carga en una tabla nueva.

// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// load sequence of scalars.


IEnumerable<double> query = from i in items
where i.Price > 9.99
orderby i.Price
select i.Price;

DataTable table = query.CopyToDataTable();


Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book


book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie


movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie


movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item


items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

Dim query = From i In items _


Where i.Price > 9.99 _
Order By i.Price _
Select i.Price

Dim table As DataTable


table = query.CopyToDataTable()

Consulte también
Guía de programación
Métodos genéricos Field y SetField
Ejemplos de LINQ to DataSet
Cómo: Implementar CopyToDataTable<T> donde el
tipo genérico T no es un DataRow
27/03/2020 • 10 minutes to read • Edit Online

El objeto DataTable se suele utilizar para el enlace de datos. El método CopyToDataTable toma los resultados de
una consulta y copia los datos en un objeto DataTable que puede utilizarse después para el enlace de datos. Sin
embargo, los métodos CopyToDataTable solo funcionan en un origen IEnumerable<T> en el que el parámetro
genérico T es de tipo DataRow. Aunque esto es útil, no permite la creación de tablas a partir de una secuencia de
tipos escalares, a partir de consultas que proyectan tipos anónimos, o a partir de consultas que realizan
combinaciones de tablas.
En este tema se describe cómo se implementan dos métodos de extensión CopyToDataTable<T> personalizados
que aceptan un parámetro genérico T de un tipo distinto de DataRow. La lógica para crear un objeto DataTable
desde un origen IEnumerable<T> está contenida en la clase ObjectShredder<T> , que se incluye después en dos
métodos de extensión CopyToDataTable<T> sobrecargados. El método Shred de la clase ObjectShredder<T>
devuelve el objeto DataTable relleno y acepta tres parámetros de entrada: un origen IEnumerable<T>, un objeto
DataTable y una enumeración LoadOption. El esquema inicial del objeto DataTable devuelto está basado en el
esquema del tipo T . Si se proporciona una tabla existente como entrada, el esquema debe ser coherente con el
esquema del tipo T . Cada propiedad y campo públicos del tipo T se convierten a un objeto DataColumn en la
tabla devuelta. Si la secuencia de origen contiene un tipo derivado de T , el esquema de la tabla devuelta se
expande para las propiedades o campos públicos adicionales.
Para obtener ejemplos de uso de los métodos CopyToDataTable<T> personalizados, vea Crear un objeto DataTable a
partir de una consulta.
Para implementar los métodos CopyToDataTable<T> personalizados en la aplicación
1. Implemente la clase ObjectShredder<T> para crear un objeto DataTable desde un origen IEnumerable<T>:

public class ObjectShredder<T>


{
private System.Reflection.FieldInfo[] _fi;
private System.Reflection.PropertyInfo[] _pi;
private System.Collections.Generic.Dictionary<string, int> _ordinalMap;
private System.Type _type;

// ObjectShredder constructor.
public ObjectShredder()
{
_type = typeof(T);
_fi = _type.GetFields();
_pi = _type.GetProperties();
_ordinalMap = new Dictionary<string, int>();
}

/// <summary>
/// Loads a DataTable from a sequence of objects.
/// </summary>
/// <param name="source">The sequence of objects to load into the DataTable.</param>
/// <param name="table">The input table. The schema of the table must match that
/// the type T. If the table is null, a new table is created with a schema
/// created from the public properties and fields of the type T.</param>
/// <param name="options">Specifies how values from the source sequence will be applied to
/// existing rows in the table.</param>
/// <returns>A DataTable created from the source sequence.</returns>
public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options)
public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options)
{
// Load the table from the scalar sequence if T is a primitive type.
if (typeof(T).IsPrimitive)
{
return ShredPrimitive(source, table, options);
}

// Create a new table if the input table is null.


table ??= new DataTable(typeof(T).Name);

// Initialize the ordinal map and extend the table schema based on type T.
table = ExtendTable(table, typeof(T));

// Enumerate the source sequence and load the object values into rows.
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator())
{
while (e.MoveNext())
{
if (options != null)
{
table.LoadDataRow(ShredObject(table, e.Current), (LoadOption)options);
}
else
{
table.LoadDataRow(ShredObject(table, e.Current), true);
}
}
}
table.EndLoadData();

// Return the table.


return table;
}

public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options)


{
// Create a new table if the input table is null.
table ??= new DataTable(typeof(T).Name);

if (!table.Columns.Contains("Value"))
{
table.Columns.Add("Value", typeof(T));
}

// Enumerate the source sequence and load the scalar values into rows.
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator())
{
Object[] values = new object[table.Columns.Count];
while (e.MoveNext())
{
values[table.Columns["Value"].Ordinal] = e.Current;

if (options != null)
{
table.LoadDataRow(values, (LoadOption)options);
}
else
{
table.LoadDataRow(values, true);
}
}
}
table.EndLoadData();

// Return the table.


return table;
}
}

public object[] ShredObject(DataTable table, T instance)


{

FieldInfo[] fi = _fi;
PropertyInfo[] pi = _pi;

if (instance.GetType() != typeof(T))
{
// If the instance is derived from T, extend the table schema
// and get the properties and fields.
ExtendTable(table, instance.GetType());
fi = instance.GetType().GetFields();
pi = instance.GetType().GetProperties();
}

// Add the property and field values of the instance to an array.


Object[] values = new object[table.Columns.Count];
foreach (FieldInfo f in fi)
{
values[_ordinalMap[f.Name]] = f.GetValue(instance);
}

foreach (PropertyInfo p in pi)


{
values[_ordinalMap[p.Name]] = p.GetValue(instance, null);
}

// Return the property and field values of the instance.


return values;
}

public DataTable ExtendTable(DataTable table, Type type)


{
// Extend the table schema if the input table was null or if the value
// in the sequence is derived from type T.
foreach (FieldInfo f in type.GetFields())
{
if (!_ordinalMap.ContainsKey(f.Name))
{
// Add the field as a column in the table if it doesn't exist
// already.
DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
: table.Columns.Add(f.Name, f.FieldType);

// Add the field to the ordinal map.


_ordinalMap.Add(f.Name, dc.Ordinal);
}
}
foreach (PropertyInfo p in type.GetProperties())
{
if (!_ordinalMap.ContainsKey(p.Name))
{
// Add the property as a column in the table if it doesn't exist
// already.
DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
: table.Columns.Add(p.Name, p.PropertyType);

// Add the property to the ordinal map.


_ordinalMap.Add(p.Name, dc.Ordinal);
}
}

// Return the table.


return table;
}
}
Public Class ObjectShredder(Of T)
' Fields
Private _fi As FieldInfo()
Private _ordinalMap As Dictionary(Of String, Integer)
Private _pi As PropertyInfo()
Private _type As Type

' Constructor
Public Sub New()
Me._type = GetType(T)
Me._fi = Me._type.GetFields
Me._pi = Me._type.GetProperties
Me._ordinalMap = New Dictionary(Of String, Integer)
End Sub

Public Function ShredObject(ByVal table As DataTable, ByVal instance As T) As Object()


Dim fi As FieldInfo() = Me._fi
Dim pi As PropertyInfo() = Me._pi
If (Not instance.GetType Is GetType(T)) Then
' If the instance is derived from T, extend the table schema
' and get the properties and fields.
Me.ExtendTable(table, instance.GetType)
fi = instance.GetType.GetFields
pi = instance.GetType.GetProperties
End If

' Add the property and field values of the instance to an array.
Dim values As Object() = New Object(table.Columns.Count - 1) {}
Dim f As FieldInfo
For Each f In fi
values(Me._ordinalMap.Item(f.Name)) = f.GetValue(instance)
Next
Dim p As PropertyInfo
For Each p In pi
values(Me._ordinalMap.Item(p.Name)) = p.GetValue(instance, Nothing)
Next

' Return the property and field values of the instance.


Return values
End Function

' Summary: Loads a DataTable from a sequence of objects.


' source parameter: The sequence of objects to load into the DataTable.</param>
' table parameter: The input table. The schema of the table must match that
' the type T. If the table is null, a new table is created
' with a schema created from the public properties and fields
' of the type T.
' options parameter: Specifies how values from the source sequence will be applied to
' existing rows in the table.
' Returns: A DataTable created from the source sequence.

Public Function Shred(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As
LoadOption?) As DataTable

' Load the table from the scalar sequence if T is a primitive type.
If GetType(T).IsPrimitive Then
Return Me.ShredPrimitive(source, table, options)
End If

' Create a new table if the input table is null.


If (table Is Nothing) Then
table = New DataTable(GetType(T).Name)
End If

' Initialize the ordinal map and extend the table schema based on type T.
table = Me.ExtendTable(table, GetType(T))
' Enumerate the source sequence and load the object values into rows.
table.BeginLoadData()
Using e As IEnumerator(Of T) = source.GetEnumerator
Do While e.MoveNext
If options.HasValue Then
table.LoadDataRow(Me.ShredObject(table, e.Current), options.Value)
Else
table.LoadDataRow(Me.ShredObject(table, e.Current), True)
End If
Loop
End Using
table.EndLoadData()

' Return the table.


Return table
End Function

Public Function ShredPrimitive(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal
options As LoadOption?) As DataTable
' Create a new table if the input table is null.
If (table Is Nothing) Then
table = New DataTable(GetType(T).Name)
End If
If Not table.Columns.Contains("Value") Then
table.Columns.Add("Value", GetType(T))
End If

' Enumerate the source sequence and load the scalar values into rows.
table.BeginLoadData()
Using e As IEnumerator(Of T) = source.GetEnumerator
Dim values As Object() = New Object(table.Columns.Count - 1) {}
Do While e.MoveNext
values(table.Columns.Item("Value").Ordinal) = e.Current
If options.HasValue Then
table.LoadDataRow(values, options.Value)
Else
table.LoadDataRow(values, True)
End If
Loop
End Using
table.EndLoadData()

' Return the table.


Return table
End Function

Public Function ExtendTable(ByVal table As DataTable, ByVal type As Type) As DataTable


' Extend the table schema if the input table was null or if the value
' in the sequence is derived from type T.
Dim f As FieldInfo
Dim p As PropertyInfo

For Each f In type.GetFields


If Not Me._ordinalMap.ContainsKey(f.Name) Then
Dim dc As DataColumn

' Add the field as a column in the table if it doesn't exist


' already.
dc = IIf(table.Columns.Contains(f.Name), table.Columns.Item(f.Name),
table.Columns.Add(f.Name, f.FieldType))

' Add the field to the ordinal map.


Me._ordinalMap.Add(f.Name, dc.Ordinal)
End If

Next

For Each p In type.GetProperties


For Each p In type.GetProperties
If Not Me._ordinalMap.ContainsKey(p.Name) Then
' Add the property as a column in the table if it doesn't exist
' already.
Dim dc As DataColumn
dc = IIf(table.Columns.Contains(p.Name), table.Columns.Item(p.Name),
table.Columns.Add(p.Name, p.PropertyType))

' Add the property to the ordinal map.


Me._ordinalMap.Add(p.Name, dc.Ordinal)
End If
Next

' Return the table.


Return table
End Function

End Class

En el ejemplo anterior se supone DataColumn que las propiedades de la no son tipos de valor que aceptan
valores NULL. Para controlar las propiedades con tipos de valor que aceptan valores NULL, utilice el código
siguiente:

DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name] : table.Columns.Add(p.Name,


Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);

2. Implemente los métodos de extensión CopyToDataTable<T> personalizados en una clase:

public static class CustomLINQtoDataSetMethods


{
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
{
return new ObjectShredder<T>().Shred(source, null, null);
}

public static DataTable CopyToDataTable<T>(this IEnumerable<T> source,


DataTable table, LoadOption? options)
{
return new ObjectShredder<T>().Shred(source, table, options);
}
}

Public Module CustomLINQtoDataSetMethods


<Extension()> _
Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T)) As DataTable
Return New ObjectShredder(Of T)().Shred(source, Nothing, Nothing)
End Function

<Extension()> _
Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T), ByVal table As DataTable,
ByVal options As LoadOption?) As DataTable
Return New ObjectShredder(Of T)().Shred(source, table, options)
End Function

End Module

3. Agregue la clase ObjectShredder<T> y los métodos de extensión CopyToDataTable<T> a la aplicación.


Module Module1
Sub Main()
' Your application code using CopyToDataTable<T>.
End Sub
End Module

Public Module CustomLINQtoDataSetMethods



End Module

Public Class ObjectShredder(Of T)



End Class

class Program
{
static void Main(string[] args)
{
// Your application code using CopyToDataTable<T>.
}
}
public static class CustomLINQtoDataSetMethods
{

}
public class ObjectShredder<T>
{

}

Vea también
Creación de un objeto DataTable a partir de una consulta
Guía de programación
Métodos genéricos Field y SetField (LINQ to
DataSet)
27/03/2020 • 4 minutes to read • Edit Online

LINQ to DataSetLINQ to DataRow DataSet proporciona métodos de extensión a la clase para tener acceso a los
valores de columna: el Field método y el SetField método. Estos métodos facilitan el acceso a los valores de
columna a los desarrolladores, sobre todo en lo relativo a valores NULL. El DataSet DBNull.Value uso para
representar valores null, Nullable Nullable<T> mientras que LINQ usa los tipos y. El uso del descriptor DataRow
de acceso de columna preexistente en requiere que convierta el objeto devuelto al tipo adecuado. Si un campo
DataRow determinado en un puede ser null, debe comprobar DBNull.Value explícitamente si hay un valor
InvalidCastExceptionnulo porque devolverlo e horizontalmente convertirlo a otro tipo produce un archivo . En el
ejemplo siguiente, si el DataRow.IsNull método no se usó para comprobar si hay DBNull.Value un valor nulo,
Stringse produciría una excepción si el indizador devolvió e intentó convertirlo en un archivo .

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query =
from product in products.AsEnumerable()
where !product.IsNull("Color") &&
(string)product["Color"] == "Red"
select new
{
Name = product["Name"],
ProductNumber = product["ProductNumber"],
ListPrice = product["ListPrice"]
};

foreach (var product in query)


{
Console.WriteLine("Name: {0}", product.Name);
Console.WriteLine("Product number: {0}", product.ProductNumber);
Console.WriteLine("List price: ${0}", product.ListPrice);
Console.WriteLine("");
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Where product!Color IsNot DBNull.Value AndAlso product!Color = "Red" _
Select New With _
{ _
.Name = product!Name, _
.ProductNumber = product!ProductNumber, _
.ListPrice = product!ListPrice _
}

For Each product In query


Console.WriteLine("Name: " & product.Name)
Console.WriteLine("Product number: " & product.ProductNumber)
Console.WriteLine("List price: $" & product.ListPrice & vbNewLine)
Next

El método Field proporciona acceso a los valores de columna de DataRow y SetField establece los valores de
columna en DataRow. Tanto Field el SetField método como el método controlan los tipos de valor que aceptan
valores NULL, por lo que no es necesario comprobar explícitamente los valores NU como en el ejemplo anterior.
Además, ambos son métodos genéricos, lo que significa que no es necesario convertir el tipo de valor devuelto.
El siguiente ejemplo utiliza el método Field.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query =
from product in products.AsEnumerable()
where product.Field<string>("Color") == "Red"
select new
{
Name = product.Field<string>("Name"),
ProductNumber = product.Field<string>("ProductNumber"),
ListPrice = product.Field<Decimal>("ListPrice")
};

foreach (var product in query)


{
Console.WriteLine("Name: {0}", product.Name);
Console.WriteLine("Product number: {0}", product.ProductNumber);
Console.WriteLine("List price: ${0}", product.ListPrice);
Console.WriteLine("");
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Where product.Field(Of String)("Color") = "Red" _
Select New With _
{ _
.Name = product.Field(Of String)("Name"), _
.ProductNumber = product.Field(Of String)("ProductNumber"), _
.ListPrice = product.Field(Of Decimal)("ListPrice") _
}

For Each product In query


Console.WriteLine("Name: " & product.Name)
Console.WriteLine("Product number: " & product.ProductNumber)
Console.WriteLine("List price: $ " & product.ListPrice & vbNewLine)
Next

Observe que el tipo de datos especificado en el parámetro T genérico de los métodos Field y SetField deben
coincidir con el tipo del valor subyacente. De lo contrario, se producirá una excepción InvalidCastException. El
nombre de columna especificado debe también coincidir con el nombre de una columna en DataSet; en caso
contrario, se producirá una ArgumentException. En ambos casos, la excepción se produce en tiempo de ejecución
durante la enumeración de datos cuando se ejecuta la consulta.
El método SetField en sí no realiza ninguna conversión de tipos. Sin embargo, esto no significa que no se realizará
una conversión de tipos. El SetField método expone el comportamiento DataRow ADO.NET de la clase. El objeto
podría realizar DataRow una conversión de tipos y DataRow el valor convertido se guardaría en el objeto.

Vea también
DataRowExtensions
Enlace de datos y LINQ to DataSet
08/01/2020 • 3 minutes to read • Edit Online

El enlace de datos es el proceso que establece una conexión entre la interfaz de usuario de la aplicación y la lógica
de negocios. Si el enlace está configurado correctamente y los datos proporcionan la notificaciones adecuadas, al
cambiar los valores de los datos, los elementos enlazados a los mismos reflejarán de manera automática dichos
cambios. DataSet es una representación de datos residente en memoria que proporciona un modelo de
programación relacional coherente independientemente del origen de datos que contiene. DataView de ADO.NET
2.0 permite ordenar y filtrar los datos almacenados en DataTable. Esta funcionalidad se utiliza con frecuencia en
aplicaciones de enlace de datos. Mediante DataView puede exponer los datos de una tabla con distintos criterios
de ordenación y filtrar los datos por el estado de fila o basándose en una expresión de filtro. Para obtener más
información sobre el objeto DataView, vea objetos de datos.
LINQ to DataSet permite a los desarrolladores crear consultas complejas y eficaces en un DataSet mediante el uso
de Language-Integrated Query (LINQ). Sin embargo, una consulta de LINQ to DataSet devuelve una enumeración
de objetos DataRow, que no se usa fácilmente en un escenario de enlace. Para facilitar el enlace, puede crear una
DataView a partir de una consulta de LINQ to DataSet. Este DataView utiliza el filtrado y la ordenación
especificados en la consulta, pero es más adecuado para el enlace de datos. LINQ to DataSet extiende la
funcionalidad del DataView proporcionando filtrado y ordenación basados en expresiones de LINQ, lo que permite
operaciones de filtrado y ordenación mucho más complejas y eficaces que el filtrado y la ordenación basados en
cadenas.
Observe que DataView representa la propia consulta y no es una vista encima de la consulta. DataView se enlaza a
un control de la interfaz de usuario, como DataGrid o DataGridView, proporcionando un modelo de enlace de
datos simple. DataView se puede crear también a partir de DataTable, proporcionando una vista predeterminada
de esa tabla.

Esta sección
Creación de un objeto DataView
Proporciona información sobre creación de DataView.
Filtrado con DataView
Describe cómo filtrar con DataView.
Ordenación con DataView
Describe cómo ordenar con DataView.
Consulta de la colección de DataRowView en un objeto DataView
Proporciona información sobre cómo consultar la colección DataRowView expuesta por DataView.
Rendimiento de DataView
Proporciona información sobre DataView y rendimiento.
Enlace de un objeto DataView a un control DataGridView de Windows Forms
Describe cómo enlazar un objeto DataView a DataGridView.

Vea también
Guía de programación
Crear un objeto DataView (LINQ to DataSet)
23/10/2019 • 7 minutes to read • Edit Online

Hay dos maneras de crear un DataView en el contexto de LINQ to DataSet. Puede crear a DataView partir de una
consulta de LINQ to DataSet sobre DataTableun, o puede crearlo a partir de un tipo DataTableo sin tipo. En ambos
casos, se crea DataView mediante uno de los AsDataView métodos de extensión; DataView no se construye
directamente en el contexto de LINQ to DataSet.
Cuando se ha creado DataView, puede enlazarlo con un control de la interfaz de usuario en una aplicación de
Windows Forms o en una aplicación ASP.NET, o cambiar la configuración de filtro y ordenación.
DataView construye un índice, que mejora considerablemente el rendimiento de las operaciones que pueden
utilizarlo, como filtro y ordenación. El índice de DataView se compila cuando se crea DataView y cuando se
modifica cualquier información de filtro u ordenación. La creación de DataView y el posterior establecimiento de
información de filtro y ordenación hace que el índice se compile al menos dos veces: una cuando se crea DataView
y la otra cuando se modifica cualquiera de las propiedades de ordenación y filtrado.
Para obtener más información sobre el filtrado y la DataViewordenación con, vea filtrar con DataView y ordenar
con DataView.

Crear DataView desde una consulta LINQ to DataSet


Se DataView puede crear un objeto a partir de los resultados de una consulta de LINQ to DataSet, donde los
resultados son DataRow una proyección de objetos. El objeto DataView que se acaba de crear hereda la
información de filtrado y ordenación de la consulta a partir de la cual se creó.

NOTE
En la mayor parte de los casos, las expresiones utilizadas en el filtrado y la ordenación no deben tener efectos secundarios y
deben ser deterministas. Además, las expresiones no deben tener ninguna lógica que dependa de un número de conjunto de
ejecuciones, ya que las operaciones de filtrado y ordenación se pueden ejecutar un número ilimitado de veces.

No se admite la creación de DataView a partir de una consulta que devuelve tipos anónimos o consultas que
realizan operaciones de combinación.
Sólo se admiten los siguientes operadores de consulta en una consulta utilizada para crear DataView:
Cast
OrderBy
OrderByDescending
Select
ThenBy
ThenByDescending
Where
Tenga en cuenta que DataView cuando se crea un a partir de Select una consulta de LINQ to DataSet, el método
debe ser el último método llamado en la consulta. Esto se muestra en el ejemplo siguiente, que crea un DataView
de pedidos en línea ordenados por el total a pagar:
DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<bool>("OnlineOrderFlag") == true
orderby order.Field<decimal>("TotalDue")
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True _
Order By order.Field(Of Decimal)("TotalDue") _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

También puede utilizar las propiedades y RowFilter Sort basadas en cadenas para filtrar y ordenar una DataView
después de que se haya creado a partir de una consulta. Observe que con esto se borrará la información de
ordenación y filtro heredada de la consulta. En el ejemplo siguiente se DataView crea un a partir de una LINQ to
DataSet consulta que filtra por los apellidos que empiezan por ' a '. La propiedad basada en cadena Sort se
establece para ordenar por apellidos en orden ascendente y después por nombres en orden descendente:

DataTable contacts = dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()


where contact.Field<string>("LastName").StartsWith("S")
select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

view.Sort = "LastName desc, FirstName asc";

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName").StartsWith("S") _
Select contact

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view
view.Sort = "LastName desc, FirstName asc"

Crear DataView a partir de DataTable


Además de crearse a partir de una consulta de LINQ to DataSet DataView , un objeto se puede crear DataTable a
partir de AsDataView un mediante el método.
En el ejemplo siguiente, se crea DataView a partir de la tabla SalesOrderDetail y se establece ese objeto como
origen de datos de un objeto BindingSource: Este objeto actúa como proxy para un control DataGridView.

DataTable orders = dataSet.Tables["SalesOrderDetail"];

DataView view = orders.AsDataView();


bindingSource1.DataSource = view;

dataGridView1.AutoResizeColumns();

Dim orders As DataTable = dataSet.Tables("SalesOrderDetail")

Dim view As DataView = orders.AsDataView()


bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

El filtro y la ordenación pueden establecerse en DataView después de que se haya creado a partir de DataTable. El
ejemplo siguiente crea DataView a partir de la tabla Contact y establece la propiedad Sort para ordenar por
apellidos en orden ascendente y luego por nombres en orden descendente:

DataTable contacts = dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.Sort = "LastName desc, FirstName asc";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()

view.Sort = "LastName desc, FirstName asc"

bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

No obstante, se produce una pérdida de rendimiento cuando se establece la propiedad RowFilter o Sort después
de haberse creado DataView a partir de una consulta, debido a que DataView construye un índice para admitir
operaciones de filtro y ordenación. El establecimiento de la propiedad RowFilter o Sort hace que se recompile el
índice de los datos, lo que agrega sobrecarga a la aplicación y reduce el rendimiento. Siempre que sea posible, se
recomienda especificar la información sobre filtro y ordenación cuando se cree por primera vez DataView y evitar
modificaciones posteriores.

Vea también
Enlace de datos y LINQ to DataSet
Filtrado con DataView
Ordenación con DataView
Filtrar con DataView (LINQ to DataSet)
08/01/2020 • 14 minutes to read • Edit Online

La capacidad de filtrar datos utilizando criterios específicos y después presentarlos a un cliente mediante un
control de IU es un aspecto importante del enlace de datos. DataView proporciona varias maneras de filtrar datos
y devolver subconjuntos de filas de datos que reúnan determinados criterios. Además de las funcionalidades de
filtrado basadas en cadenas, DataView también proporciona la capacidad de usar expresiones LINQ para los
criterios de filtrado. Las expresiones LINQ permiten operaciones de filtrado mucho más complejas y eficaces que
el filtrado basado en cadenas.
Existen dos maneras de filtrar datos utilizando DataView:
Cree una DataView a partir de una consulta de LINQ to DataSet con una cláusula WHERE.
Utilizar las capacidades de filtro basado en cadena de DataView existentes.

Crear DataView desde una consulta con información de filtro


Se puede crear un objeto DataView a partir de una consulta de LINQ to DataSet. Si dicha consulta tiene una
cláusula Where , DataView se crea con información de filtro desde la consulta. La expresión de la cláusula Where
se utiliza para determinar qué filas de datos se incluirán en DataView, y constituye la base para el filtro.
Los filtros basados en expresión son más eficaces y complejos que los sencillos filtros basados en cadena. Los
filtros basados en cadena y los basados en expresión se excluyen mutuamente. Cuando el RowFilter basado en
cadena se establece después de haber creado DataView desde una consulta, se borra el filtro basado en expresión
inferido a partir de la consulta.

NOTE
En la mayor parte de los casos, las expresiones utilizadas en el filtro no deben tener efectos secundarios y deben ser
deterministas. Además, las expresiones no deben tener ninguna lógica que dependa de un número de conjunto de
ejecuciones, porque las operaciones de filtrado se pueden ejecutar un número ilimitado de veces.

Ejemplo
El siguiente ejemplo consulta en la tabla SalesOrderDetail los pedidos con una cantidad superior a 2 e inferior a 6;
crea un DataView desde una consulta y enlaza DataView a un BindingSource:

DataTable orders = dataSet.Tables["SalesOrderDetail"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()


where order.Field<Int16>("OrderQty") > 2 && order.Field<Int16>
("OrderQty") < 6
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
Dim orders As DataTable = dataSet.Tables("SalesOrderDetail")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Int16)("OrderQty") > 2 And _
order.Field(Of Int16)("OrderQty") < 6 _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

Ejemplo
El siguiente ejemplo crea un DataView desde una consulta de pedidos efectuados con posterioridad al 6 de junio
de 2001:

DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()


where order.Field<DateTime>("OrderDate") > new DateTime(2002, 6, 1)
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") > New DateTime(2002, 6, 1) _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

Ejemplo
El filtro se puede combinar también con la ordenación. El siguiente ejemplo crea un DataView desde una consulta
de contactos cuyos apellidos empiezan por la letra "S", y los ordena primero por el apellido y después por el
nombre:

DataTable contacts = dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()


where contact.Field<string>("LastName").StartsWith("S")
orderby contact.Field<string>("LastName"), contact.Field<string>
("FirstName")
select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName").StartsWith("S") _
Order By contact.Field(Of String)("LastName"), contact.Field(Of String)("FirstName") _
Select contact

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

Ejemplo
El siguiente ejemplo utiliza el algoritmo SoundEx para encontrar contactos cuyo apellido es similar a "Zhu". El
algoritmo SoundEx se implementa en el método SoundEx.

DataTable contacts = dataSet.Tables["Contact"];

string soundExCode = SoundEx("Zhu");

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()


where SoundEx(contact.Field<string>("LastName")) == soundExCode
select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

Dim contacts As DataTable = dataSet.Tables("Contact")


Dim soundExCode As String = SoundEx("Zhu")

Dim query = _
From contact In contacts.AsEnumerable() _
Where SoundEx(contact.Field(Of String)("LastName")) = soundExCode _
Select contact

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

SoundEx es un algoritmo fonético utilizado para indizar nombres según el sonido, tal como se pronuncian en
inglés, que fue desarrollado originalmente por la Oficina del Censo de Estados Unidos. El método SoundEx
devuelve un código de cuatro caracteres para un nombre que consiste en una letra inglesa seguida de tres
números. La letra es la primera letra del nombre y los números codifican el resto de las consonantes del mismo.
Los nombres que suenan parecidos comparten el mismo código SoundEx. A continuación se muestra la
implementación del SoundEx utilizado en el método SoundEx del ejemplo anterior:

static private string SoundEx(string word)


{
// The length of the returned code.
int length = 4;

// Value to return.
string value = "";

// The size of the word to process.


int size = word.Length;

// The word must be at least two characters in length.


if (size > 1)
{
// Convert the word to uppercase characters.
word = word.ToUpper(System.Globalization.CultureInfo.InvariantCulture);

// Convert the word to a character array.


char[] chars = word.ToCharArray();

// Buffer to hold the character codes.


StringBuilder buffer = new StringBuilder();
buffer.Length = 0;

// The current and previous character codes.


int prevCode = 0;
int currCode = 0;

// Add the first character to the buffer.


buffer.Append(chars[0]);

// Loop through all the characters and convert them to the proper character code.
for (int i = 1; i < size; i++)
{
switch (chars[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
case 'H':
case 'W':
case 'Y':
currCode = 0;
break;
case 'B':
case 'F':
case 'P':
case 'V':
currCode = 1;
break;
case 'C':
case 'G':
case 'J':
case 'K':
case 'Q':
case 'S':
case 'X':
case 'Z':
currCode = 2;
break;
case 'D':
case 'T':
currCode = 3;
break;
case 'L':
currCode = 4;
break;
case 'M':
case 'N':
currCode = 5;
break;
case 'R':
currCode = 6;
break;
}

// Check if the current code is the same as the previous code.


if (currCode != prevCode)
{
{
// Check to see if the current code is 0 (a vowel); do not process vowels.
if (currCode != 0)
buffer.Append(currCode);
}
// Set the previous character code.
prevCode = currCode;

// If the buffer size meets the length limit, exit the loop.
if (buffer.Length == length)
break;
}
// Pad the buffer, if required.
size = buffer.Length;
if (size < length)
buffer.Append('0', (length - size));

// Set the value to return.


value = buffer.ToString();
}
// Return the value.
return value;
}

Private Function SoundEx(ByVal word As String) As String

Dim length As Integer = 4


' Value to return
Dim value As String = ""
' Size of the word to process
Dim size As Integer = word.Length
' Make sure the word is at least two characters in length
If (size > 1) Then
' Convert the word to all uppercase
word = word.ToUpper(System.Globalization.CultureInfo.InvariantCulture)
' Convert the word to character array for faster processing
Dim chars As Char() = word.ToCharArray()
' Buffer to build up with character codes
Dim buffer As StringBuilder = New StringBuilder()
' The current and previous character codes
Dim prevCode As Integer = 0
Dim currCode As Integer = 0
' Append the first character to the buffer
buffer.Append(chars(0))
' Loop through all the characters and convert them to the proper character code
For i As Integer = 1 To size - 1
Select Case chars(i)

Case "A", "E", "I", "O", "U", "H", "W", "Y"


currCode = 0

Case "B", "F", "P", "V"


currCode = 1

Case "C", "G", "J", "K", "Q", "S", "X", "Z"


currCode = 2

Case "D", "T"


currCode = 3

Case "L"
currCode = 4

Case "M", "N"


currCode = 5

Case "R"
currCode = 6
currCode = 6
End Select

' Check to see if the current code is the same as the last one
If (currCode <> prevCode) Then

' Check to see if the current code is 0 (a vowel); do not process vowels
If (currCode <> 0) Then
buffer.Append(currCode)
End If
End If
' Set the new previous character code
prevCode = currCode
' If the buffer size meets the length limit, then exit the loop
If (buffer.Length = length) Then
Exit For
End If
Next
' Pad the buffer, if required
size = buffer.Length
If (size < length) Then
buffer.Append("0", (length - size))
End If
' Set the value to return
value = buffer.ToString()
End If
' Return the value
Return value
End Function

Utilizar la propiedad RowFilter


La funcionalidad de filtrado basada en cadena existente de DataView sigue funcionando en el contexto de LINQ to
DataSet. Para obtener más información sobre el filtrado de RowFilter basado en cadenas, vea ordenar y filtrar
datos.
El siguiente ejemplo crea un DataView desde la tabla Contact y, a continuación, establece la propiedad RowFilter
para que devuelva filas cuando el apellido del contacto sea "Zhu":

DataTable contacts = dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.RowFilter = "LastName='Zhu'";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()


view.RowFilter = "LastName='Zhu'"
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

Una vez creada una DataView a partir de una consulta de DataTable o LINQ to DataSet, puede usar la propiedad
RowFilter para especificar subconjuntos de filas basándose en sus valores de columna. Los filtros basados en
cadena y los basados en expresión se excluyen mutuamente. Al establecer la propiedad RowFilter se borrará la
expresión de filtro que se deduce de la consulta LINQ to DataSet y no se puede restablecer la expresión de filtro.
DataTable contacts = dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()


where contact.Field<string>("LastName") == "Hernandez"
select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

view.RowFilter = "LastName='Zhu'";

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName") = "Hernandez" _
Select contact

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

dataGridView1.AutoResizeColumns()
view.RowFilter = "LastName='Zhu'"

Si desea devolver los resultados de una consulta determinada en los datos, en lugar de proporcionar una vista
dinámica de un subconjunto de los datos, para conseguir el máximo rendimiento puede utilizar los métodos Find
o FindRows de la DataView, en lugar de establecer la propiedad RowFilter. La propiedad RowFilter es más idónea
en una aplicación enlazada a datos donde un control enlazado muestra resultados filtrados. El establecimiento de
la propiedad RowFilter hace que se recompile el índice de los datos, lo que agrega sobrecarga a la aplicación y
reduce el rendimiento. Los métodos Find y FindRows utilizan el índice actual, sin necesidad de recompilarlo. Si va
a llamar a Find o a FindRows una única vez, entonces debería utilizar el DataView existente. Si va a llamar a Find o
a FindRows varias veces, debería crear un nuevo DataView para recompilar el índice en la columna en la que
desea buscar y, a continuación, llamar a los métodos Find o FindRows. Para obtener más información sobre los
métodos Find y FindRows, vea Buscar filas y rendimiento de DataView.

Borrar el filtro
Después de haber configurado el filtro de DataView, éste se puede borrar mediante la propiedad RowFilter. El
filtro de DataView se pude borrar de dos maneras:
Establezca la propiedad RowFilter en null .
Establecer la propiedad RowFilter en una cadena vacía.
Ejemplo
El siguiente ejemplo crea un DataView desde una consulta y, a continuación, borra el filtro estableciendo la
propiedad RowFilter en null :
DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()


where order.Field<DateTime>("OrderDate") > new DateTime(2002, 11,
20)
&& order.Field<Decimal>("TotalDue") < new Decimal(60.00)
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

view.RowFilter = null;

Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") > New DateTime(2002, 11, 20) _
And order.Field(Of Decimal)("TotalDue") < New Decimal(60.0) _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view
view.RowFilter = Nothing

Ejemplo
El siguiente ejemplo crea un DataView desde una tabla, establece la propiedad RowFilter y, a continuación, borra
el filtro estableciendo la propiedad RowFilter en una cadena vacía:

DataTable contacts = dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.RowFilter = "LastName='Zhu'";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

// Clear the row filter.


view.RowFilter = "";

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()


view.RowFilter = "LastName='Zhu'"
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

' Clear the row filter.


view.RowFilter = ""

Vea también
Enlace de datos y LINQ to DataSet
Ordenación con DataView
Ordenar con DataView (LINQ to DataSet)
08/01/2020 • 8 minutes to read • Edit Online

La capacidad de ordenar datos basándose en criterios específicos y, a continuación, presentarlos a un cliente


mediante un control de interfaz de usuario es un aspecto importante del enlace de datos. DataView proporciona
varias maneras de ordenar datos y devolver filas de datos ordenados según criterios de ordenación específicos.
Además de las funcionalidades de ordenación basada en cadena, DataView también permite usar expresiones
LINQ (Language Integrated Query) para los criterios de ordenación. Las expresiones LINQ permiten operaciones
de ordenación mucho más complejas y eficaces que la ordenación basada en cadena. En este tema se describen
ambos enfoques de ordenación utilizando DataView.

Crear DataView desde una consulta con información de ordenación


Se puede crear un objeto DataView a partir de una consulta de LINQ to DataSet. Si esa consulta contiene una
cláusula OrderBy, OrderByDescending, ThenByo ThenByDescending, las expresiones de estas cláusulas se utilizan
como base para ordenar los datos de la DataView. Por ejemplo, si la consulta contiene las cláusulas Order By… y
Then By… , el DataView resultante ordenará los datos por ambas columnas especificadas.

La ordenación basada en expresión es más eficaz y compleja que la simple ordenación basada en cadena.
Observe que la ordenación basada en cadena y expresión se excluyen mutuamente. Cuando el Sort basado en
cadena se establece después de haber creado DataView desde una consulta, el filtro basado en expresión inferido
a partir de la consulta se borra y no se puede volver a restablecer.
El índice de DataView se compila cuando se crea DataView y cuando se modifica cualquier información de filtro u
ordenación. Para obtener el mejor rendimiento, proporcione criterios de ordenación en la consulta LINQ to
DataSet en la que se crea el DataView y sin modificar la información de ordenación, más adelante. Para obtener
más información, vea rendimiento de DataView.

NOTE
En la mayor parte de los casos, las expresiones utilizadas en la ordenación no deben tener efectos secundarios y deben ser
deterministas. Además, las expresiones no deben tener ninguna lógica que dependa de un número de conjunto de
ejecuciones, porque las operaciones de ordenación se pueden ejecutar un número ilimitado de veces.

Ejemplo
El siguiente ejemplo consulta en la tabla SalesOrderHeader y ordena las filas devueltas por fecha de pedido; crea
un DataView desde una consulta y enlaza DataView a un BindingSource:

DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()


orderby order.Field<DateTime>("OrderDate")
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Order By order.Field(Of DateTime)("OrderDate") _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

Ejemplo
El siguiente ejemplo consulta en la tabla SalesOrderHeader y ordena la fila devuelta por el importe total a pagar;
crea un DataView desde una consulta y enlaza DataView a un BindingSource:

DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query =
from order in orders.AsEnumerable()
orderby order.Field<decimal>("TotalDue")
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Order By order.Field(Of Decimal)("TotalDue") _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

Ejemplo
El siguiente ejemplo consulta en la tabla SalesOrderDetail y ordena las filas devueltas por cantidad de pedido y, a
continuación, por id. del pedido de ventas; crea un DataView desde una consulta y enlaza DataView a un
BindingSource:

DataTable orders = dataSet.Tables["SalesOrderDetail"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()


orderby order.Field<Int16>("OrderQty"), order.Field<int>
("SalesOrderID")
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
Dim orders As DataTable = dataSet.Tables("SalesOrderDetail")

Dim query = _
From order In orders.AsEnumerable() _
Order By order.Field(Of Int16)("OrderQty"), order.Field(Of Integer)("SalesOrderID") _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view

Utilizar la propiedad Sort basada en cadena


La funcionalidad de ordenación basada en cadena de DataView sigue funcionando con LINQ to DataSet. Después
de crear una DataView a partir de una consulta de LINQ to DataSet, puede utilizar la propiedad Sort para
establecer la ordenación en el DataView.
Las funcionalidades de ordenación basada en cadena y expresión se excluyen mutuamente. Al establecer la
propiedad Sort se borrará la ordenación basada en expresión heredada de la consulta a partir de la que se ha
creado DataView.
Para obtener más información sobre el filtrado de Sort basado en cadenas, vea ordenar y filtrar datos.
Ejemplo
El ejemplo siguiente crea DataView a partir de la tabla Contact y ordena las filas por apellido en orden
descendente y luego por nombres en orden ascendente:

DataTable contacts = dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.Sort = "LastName desc, FirstName asc";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()

view.Sort = "LastName desc, FirstName asc"

bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

Ejemplo
En el siguiente ejemplo se consultan los apellidos que empiezan por la letra "S" en la tabla Contact. Se crea una
clase DataView a partir de dicha consulta y se enlaza a un objeto BindingSource.
DataTable contacts = dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()


where contact.Field<string>("LastName").StartsWith("S")
select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

view.Sort = "LastName desc, FirstName asc";

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName").StartsWith("S") _
Select contact

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view
view.Sort = "LastName desc, FirstName asc"

Borrar la ordenación
Se puede borrar la información de ordenación de DataView después de haberla establecido mediante la
propiedad Sort. Hay dos formas de borrar información de ordenación en DataView:
Establezca la propiedad Sort en null .
Establecer la propiedad Sort en una cadena vacía.
Ejemplo
El siguiente ejemplo crea un DataView desde una consulta y borra la ordenación mediante el establecimiento de
la propiedad Sort en una cadena vacía:

DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()


orderby order.Field<decimal>("TotalDue")
select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

view.Sort = "";

Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Order By order.Field(Of Decimal)("TotalDue") _
Select order

Dim view As DataView = query.AsDataView()


bindingSource1.DataSource = view
view.Sort = ""
Ejemplo
El siguiente ejemplo crea un DataView desde la tabla Contact y establece la propiedad Sort para ordenar por
apellidos en orden descendente. A continuación, la información de ordenación se borra estableciendo la
propiedad Sort en null :

DataTable contacts = dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.Sort = "LastName desc";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

// Clear the sort.


view.Sort = null;

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()


view.Sort = "LastName desc"

bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

'Clear the sort.


view.Sort = Nothing

Vea también
Enlace de datos y LINQ to DataSet
Filtrado con DataView
Ordenación de datos
Consultar la colección de DataRowView en un objeto
DataView
23/10/2019 • 4 minutes to read • Edit Online

El objeto DataView expone una colección enumerable de objetos DataRowView. DataRowView representa una vista
personalizada de un objeto DataRow y muestra una versión concreta de dicho objeto DataRow en un control. Solo
se puede mostrar una versión de un objeto DataRow mediante un control, como por ejemplo, DataGridView. Se
puede tener acceso al objeto DataRow que está expuesto por DataRowView mediante la propiedad Row de
DataRowView. Cuando se ven valores mediante el uso de DataRowView, la propiedad RowStateFilter determina
qué versión de fila del objeto DataRow subyacente está expuesta. Para obtener información sobre cómo obtener
acceso a diferentes versiones DataRowde fila mediante, vea Estados de fila y versiones de fila. Dado que la
colección DataRowView de objetos expuesta DataView por es Enumerable, puede utilizar LINQ to DataSet para
realizar consultas sobre él.
En el ejemplo siguiente se realiza una consulta en la tabla Product para los productos de color rojo y se crea una
tabla a partir de dicha consulta. Se crea un objeto DataView a partir de la tabla y se establece la propiedad
RowStateFilter para filtrar por filas modificadas o eliminadas. DataView se utiliza después como origen en una
consulta LINQ, y los objetos DataRowView que se han modificado y eliminado se enlazan a un control
DataGridView.

DataTable products = dataSet.Tables["Product"];

// Query for red colored products.


EnumerableRowCollection<DataRow> redProductsQuery =
from product in products.AsEnumerable()
where product.Field<string>("Color") == "Red"
orderby product.Field<decimal>("ListPrice")
select product;

// Create a table and view from the query.


DataTable redProducts = redProductsQuery.CopyToDataTable<DataRow>();
DataView view = new DataView(redProducts);

// Mark a row as deleted.


redProducts.Rows[0].Delete();

// Modify product price.


redProducts.Rows[1]["ListPrice"] = 20.00;
redProducts.Rows[2]["ListPrice"] = 30.00;

view.RowStateFilter = DataViewRowState.ModifiedCurrent | DataViewRowState.Deleted;

// Query for the modified and deleted rows.


IEnumerable<DataRowView> modifiedDeletedQuery = from DataRowView rowView in view
select rowView;

dataGridView2.DataSource = modifiedDeletedQuery.ToList();
Dim products As DataTable = dataSet.Tables("Product")

' Query for red colored products.


Dim redProductsQuery = _
From product In products.AsEnumerable() _
Where product.Field(Of String)("Color") = "Red" _
Order By product.Field(Of Decimal)("ListPrice") _
Select product
' Create a table and view from the query.
Dim redProducts As DataTable = redProductsQuery.CopyToDataTable()
Dim view As DataView = New DataView(redProducts)

' Mark a row as deleted.


redProducts.Rows(0).Delete()

' Modify product price.


redProducts.Rows(1)("ListPrice") = 20.0
redProducts.Rows(2)("ListPrice") = 30.0

view.RowStateFilter = DataViewRowState.ModifiedCurrent Or DataViewRowState.Deleted

' Query for the modified and deleted rows.


Dim modifiedDeletedQuery = From rowView As DataRowView In view _
Select rowView

dataGridView2.DataSource = modifiedDeletedQuery.ToList()

En el ejemplo siguiente se crea una tabla de productos a partir de una vista que se enlaza a un control
DataGridView. Se realiza una consulta en DataView para los productos de color rojo y los resultados ordenados se
enlazan a un control DataGridView.

// Create a table from the bound view representing a query of


// available products.
DataView view = (DataView)bindingSource1.DataSource;
DataTable productsTable = (DataTable)view.Table;

// Set RowStateFilter to display the current rows.


view.RowStateFilter = DataViewRowState.CurrentRows ;

// Query the DataView for red colored products ordered by list price.
var productQuery = from DataRowView rowView in view
where rowView.Row.Field<string>("Color") == "Red"
orderby rowView.Row.Field<decimal>("ListPrice")
select new { Name = rowView.Row.Field<string>("Name"),
Color = rowView.Row.Field<string>("Color"),
Price = rowView.Row.Field<decimal>("ListPrice")};

// Bind the query results to another DataGridView.


dataGridView2.DataSource = productQuery.ToList();
' Create a table from the bound view representing a query of
' available products.
Dim view As DataView = CType(bindingSource1.DataSource, DataView)
Dim productsTable As DataTable = CType(view.Table, DataTable)

' Set RowStateFilter to display the current rows.


view.RowStateFilter = DataViewRowState.CurrentRows

' Query the DataView for red colored products ordered by list price.
Dim productQuery = From rowView As DataRowView In view _
Where rowView.Row.Field(Of String)("Color") = "Red" _
Order By rowView.Row.Field(Of Decimal)("ListPrice") _
Select New With {.Name = rowView.Row.Field(Of String)("Name"), _
.Color = rowView.Row.Field(Of String)("Color"), _
.Price = rowView.Row.Field(Of Decimal)("ListPrice")}

' Bind the query results to another DataGridView.


dataGridView2.DataSource = productQuery.ToList()

Vea también
Enlace de datos y LINQ to DataSet
Rendimiento de DataView
21/03/2020 • 5 minutes to read • Edit Online

En este tema se abordan las ventajas del uso de los métodos Find y FindRows de la clase DataView, y del
almacenamiento en memoria caché de DataView en una aplicación web.

Find y FindRows
DataView construye un índice. Un índice contiene claves generadas a partir de una o varias columnas de la tabla o
la vista. Dichas claves están almacenadas en una estructura que permite que DataView busque de forma rápida y
eficiente la fila o filas asociadas a los valores de cada clave. Las operaciones que usan el índice, como el filtrado y
la ordenación, ven aumentos significativos del rendimiento. El índice de DataView se compila cuando se crea
DataView y cuando se modifica cualquier información de filtro u ordenación. La creación de DataView y el
posterior establecimiento de información de filtro y ordenación hace que el índice se compile al menos dos veces:
una cuando se crea DataView y la otra cuando se modifica cualquiera de las propiedades de ordenación y filtrado.
Para obtener más información DataViewsobre cómo filtrar y ordenar con , vea Filtrado con DataView y
Ordenación con DataView.
Si desea devolver los resultados de una consulta determinada en los datos, en lugar de proporcionar una vista
dinámica de un subconjunto de los datos, para conseguir el máximo rendimiento puede utilizar los métodos Find
o FindRows de la DataView, en lugar de establecer la propiedad RowFilter. La propiedad RowFilter es más idónea
en una aplicación enlazada a datos donde un control enlazado muestra resultados filtrados. El establecimiento de
la propiedad RowFilter hace que se recompile el índice de los datos, lo que agrega sobrecarga a la aplicación y
reduce el rendimiento. Los métodos Find y FindRows utilizan el índice actual, sin necesidad de recompilarlo. Si va
a llamar a Find o a FindRows una única vez, entonces debería utilizar el DataView existente. Si va a llamar a Find o
a FindRows varias veces, debería crear un nuevo DataView para recompilar el índice en la columna en la que
desea buscar y, a continuación, llamar a los métodos Find o FindRows. Para obtener más Find FindRows
información acerca de los métodos, vea Buscar filas.
El ejemplo siguiente utiliza el método Find para buscar un contacto con el apellido "Zhu".

DataTable contacts = dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()


orderby contact.Field<string>("LastName")
select contact;

DataView view = query.AsDataView();

// Find a contact with the last name of Zhu.


int found = view.Find("Zhu");
Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Order By contact.Field(Of String)("LastName") _
Select contact

Dim view As DataView = query.AsDataView()

Dim found As Integer = view.Find("Zhu")

En el ejemplo siguiente se usa el método FindRows para buscar todos los productos de color rojo.

DataTable products = dataSet.Tables["Product"];

EnumerableRowCollection<DataRow> query = from product in products.AsEnumerable()


orderby product.Field<Decimal>("ListPrice"), product.Field<string>
("Color")
select product;

DataView view = query.AsDataView();

view.Sort = "Color";

object[] criteria = new object[] { "Red"};

DataRowView[] foundRowsView = view.FindRows(criteria);

Dim products As DataTable = dataSet.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of Decimal)("ListPrice"), product.Field(Of String)("Color") _
Select product

Dim view As DataView = query.AsDataView()


view.Sort = "Color"

Dim criteria As Object() = New Object() {"Red"}

Dim foundRowsView As DataRowView() = view.FindRows(criteria)

ASP.NET
ASP.NET dispone de un mecanismo de almacenamiento en memoria caché de objetos cuya creación en memoria
requiere una gran cantidad de recursos del servidor. Si se almacenan estos tipos de recursos en la memoria caché
se puede mejorar de forma significativa el rendimiento de la aplicación. La clase Cache implementa el
almacenamiento en memoria caché, con instancias de la memoria caché privadas para cada aplicación. Dado que
la creación de un objeto DataView nuevo puede consumir muchos recursos, quizás desee utilizar esta
funcionalidad de almacenamiento en caché en aplicaciones web para que DataView no tenga que recompilarse
cada vez que se actualiza la página web.
En el ejemplo siguiente, DataView se almacena en caché para que los datos no tengan que volver a ordenarse
cuando la página se actualice.
If (Cache("ordersView") = Nothing) Then

Dim dataSet As New DataSet()

FillDataSet(dataSet)

Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True _
Order By order.Field(Of Decimal)("TotalDue") _
Select order

Dim view As DataView = query.AsDataView()

Cache.Insert("ordersView", view)

End If

Dim ordersView = CType(Cache("ordersView"), DataView)

GridView1.DataSource = ordersView
GridView1.DataBind()

if (Cache["ordersView"] == null)
{
// Fill the DataSet.
DataSet dataSet = FillDataSet();

DataTable orders = dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<bool>("OnlineOrderFlag") == true
orderby order.Field<decimal>("TotalDue")
select order;

DataView view = query.AsDataView();


Cache.Insert("ordersView", view);
}

DataView ordersView = (DataView)Cache["ordersView"];

GridView1.DataSource = ordersView;
GridView1.DataBind();

Consulte también
Enlace de datos y LINQ to DataSet
Procedimiento para enlazar un objeto DataView a un
control DataGridView de formularios Windows Forms
23/10/2019 • 4 minutes to read • Edit Online

El control DataGridView proporciona una forma eficaz y flexible de mostrar datos en formato de tabla. El control
DataGridView admite el modelo de enlace de datos de Windows Forms estándar, por lo que se enlazará a DataView
y a otros orígenes de datos. Sin embargo, en la mayoría de las situaciones se enlazará a un componente
BindingSource que administrará los detalles de la interacción con el origen de datos.
Para obtener más información sobre DataGridView el control, vea información general sobre el control
DataGridView.
Para conectar un control DataGridView a DataView
1. Implemente un método que controle los detalles de recuperación de datos desde una base de datos. En el
ejemplo de código siguiente se implementa un método GetData que inicializa un componente
SqlDataAdapter y lo utiliza para rellenar un DataSet. Asegúrese de establecer la variable connectionString
en un valor que sea adecuado para la base de datos. Necesitará tener acceso a un servidor que tenga la base
de datos de ejemplo AdventureWorks de SQL Server instalada.

private void GetData()


{
try
{
// Initialize the DataSet.
dataSet = new DataSet();
dataSet.Locale = CultureInfo.InvariantCulture;

// Create the connection string for the AdventureWorks sample database.


string connectionString = "Data Source=localhost;Initial Catalog=AdventureWorks;"
+ "Integrated Security=true;";

// Create the command strings for querying the Contact table.


string contactSelectCommand = "SELECT ContactID, Title, FirstName, LastName, EmailAddress, Phone
FROM Person.Contact";

// Create the contacts data adapter.


contactsDataAdapter = new SqlDataAdapter(
contactSelectCommand,
connectionString);

// Create a command builder to generate SQL update, insert, and


// delete commands based on the contacts select command. These are used to
// update the database.
SqlCommandBuilder contactsCommandBuilder = new SqlCommandBuilder(contactsDataAdapter);

// Fill the data set with the contact information.


contactsDataAdapter.Fill(dataSet, "Contact");
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
}
Private Sub GetData()
Try
' Initialize the DataSet.
dataSet = New DataSet()
dataSet.Locale = CultureInfo.InvariantCulture

' Create the connection string for the AdventureWorks sample database.
Dim connectionString As String = "Data Source=localhost;Initial Catalog=AdventureWorks;" _
& "Integrated Security=true;"

' Create the command strings for querying the Contact table.
Dim contactSelectCommand As String = "SELECT ContactID, Title, FirstName, LastName,
EmailAddress, Phone FROM Person.Contact"

' Create the contacts data adapter.


contactsDataAdapter = New SqlDataAdapter( _
contactSelectCommand, _
connectionString)

' Create a command builder to generate SQL update, insert, and


' delete commands based on the contacts select command. These are used to
' update the database.
Dim contactsCommandBuilder As SqlCommandBuilder = New SqlCommandBuilder(contactsDataAdapter)

' Fill the data set with the contact information.


contactsDataAdapter.Fill(dataSet, "Contact")

Catch ex As SqlException
MessageBox.Show(ex.Message)
End Try
End Sub

2. En el controlador de eventos Load del formulario, enlace el control DataGridView al componente


BindingSource y llame al método GetData para recuperar los datos de la base de datos. Se crea a partir de
una consulta de LINQ to DataSet sobre DataTable el contacto y, a continuación BindingSource , se enlaza al
componente. DataView

private void Form1_Load(object sender, EventArgs e)


{
// Connect to the database and fill the DataSet.
GetData();

contactDataGridView.DataSource = contactBindingSource;

// Create a LinqDataView from a LINQ to DataSet query and bind it


// to the Windows forms control.
EnumerableRowCollection<DataRow> contactQuery = from row in dataSet.Tables["Contact"].AsEnumerable()
where row.Field<string>("EmailAddress") != null
orderby row.Field<string>("LastName")
select row;

contactView = contactQuery.AsDataView();

// Bind the DataGridView to the BindingSource.


contactBindingSource.DataSource = contactView;
contactDataGridView.AutoResizeColumns();
}
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
' Connect to the database and fill the DataSet.
GetData()

contactDataGridView.DataSource = contactBindingSource

' Create a LinqDataView from a LINQ to DataSet query and bind it


' to the Windows forms control.
Dim contactQuery = _
From row In dataSet.Tables("Contact").AsEnumerable() _
Where row.Field(Of String)("EmailAddress") <> Nothing _
Order By row.Field(Of String)("LastName") _
Select row

contactView = contactQuery.AsDataView()

' Bind the DataGridView to the BindingSource.


contactBindingSource.DataSource = contactView
contactDataGridView.AutoResizeColumns()
End Sub

Vea también
Enlace de datos y LINQ to DataSet
Depurar consultas de LINQ to DataSet
23/10/2019 • 6 minutes to read • Edit Online

Visual Studio admite la depuración de código de LINQ to DataSet. Sin embargo, hay algunas diferencias entre la
depuración de código LINQ to DataSet y el código no LINQ to DataSet administrado. La mayoría de las
características de depuración funcionan con instrucciones de LINQ to DataSet, incluida la ejecución paso a paso, el
establecimiento de puntos de interrupción y la visualización de resultados que se muestran en ventanas del
depurador. Sin embargo, la ejecución de consultas diferida en tiene algunos efectos secundarios que se deben
tener en cuenta durante la depuración LINQ to DataSet código y existen algunas limitaciones en el uso de editar y
continuar. En este tema se tratan aspectos de la depuración que son únicos en LINQ to DataSet comparados con el
código no LINQ to DataSet administrado.

Ver los resultados


Puede ver el resultado de una instrucción LINQ to DataSet mediante información sobre herramientas, el ventana
Inspección y el cuadro de diálogo Inspección rápida. Al usar una ventana de código fuente, puede pausar el puntero
en una consulta en la ventana de código fuente para que aparezca una información sobre datos. Puede copiar una
variable de LINQ to DataSet y pegarla en el ventana Inspección o en el cuadro de diálogo Inspección rápida. En
LINQ to DataSet, una consulta no se evalúa cuando se crea o declara, pero solo cuando se ejecuta la consulta. Esto
se denomina ejecución aplazada. Por consiguiente, la variable de consulta no tiene un valor hasta que se evalúe.
Para obtener más información, vea consultas en LINQ to DataSet.
Para mostrar el resultado de una consulta, el depurador debe evaluarla. Esta evaluación implícita se produce
cuando se ve un LINQ to DataSet resultado de la consulta en el depurador y tiene algunos efectos que se deben
tener en cuenta. Cada evaluación de la consulta lleva tiempo. Expandir el nodo de resultados lleva tiempo. Para
algunas consultas, la evaluación repetida podría producir una reducción notable del rendimiento. Evaluar una
consulta también puede producir efectos secundarios, que son cambios en el valor de los datos o en el estado del
programa. No todas las consultas tienen los efectos secundarios. Para determinar si una consulta se puede evaluar
sin ningún riesgo ni efectos secundarios, debe entender el código que implementa la consulta. Para obtener más
información, vea efectos secundarios y expresiones.

Editar y continuar
Editar y continuar no admite cambios en las consultas de LINQ to DataSet. Si agrega, quita o cambia una
instrucción LINQ to DataSet durante una sesión de depuración, aparece un cuadro de diálogo que indica que el
cambio no es compatible con editar y continuar. En ese momento, puede deshacer los cambios o detener la sesión
de depuración y reiniciar una nueva sesión con el código revisado.
Además, editar y continuar no permite cambiar el tipo o el valor de una variable que se usa en una instrucción
LINQ to DataSet. De nuevo, puede deshacer los cambios o detener y reiniciar la sesión de depuración.
En Visual C# Studio, no puede usar editar y continuar en cualquier código en un método que contenga una
consulta LINQ to DataSet.
En Visual Basic en Visual Studio, puede usar editar y continuar en código no LINQ to DataSet, incluso en un método
que contenga una consulta LINQ to DataSet. Puede Agregar o quitar código antes de la instrucción LINQ to
DataSet, incluso si los cambios afectan al número de línea de la consulta LINQ to DataSet. La experiencia de
depuración de Visual Basic para el código que no es de LINQ to DataSet sigue siendo la misma que antes de que se
introdujera LINQ to DataSet. Sin embargo, no se puede cambiar, agregar o quitar una consulta LINQ to DataSet, a
menos que se detenga la depuración para aplicar los cambios.
Vea también
Depurar código administrado
Guía de programación
Seguridad (LINQ to DataSet)
23/10/2019 • 4 minutes to read • Edit Online

En este tema se describen los problemas de seguridad de LINQ to DataSet.

Pasar una consulta a un componente que no sea de confianza


Una consulta de LINQ to DataSet se puede formular en un punto de un programa y ejecutarse en otro diferente. En
el momento en que se formula la consulta, ésta puede hacer referencia a cualquier elemento visible en ese
momento, como miembros privados de la clase a la que pertenece el método de llamada o símbolos que
representan variables o argumentos locales. En tiempo de ejecución, la consulta podrá obtener acceso a los
miembros a los que la consulta hizo referencia durante la formulación, incluso si el código de llamada no puede
verlos. Al código que ejecuta la consulta no se le ha agregado visibilidad de manera arbitraria por lo que no puede
elegir a qué miembro debe obtener acceso. Sólo podrá obtener acceso a lo que la consulta obtenga acceso y sólo a
través de la misma consulta.
Esto implica que, al pasar una referencia a una consulta realizada a otra parte del código, el componente que recibe
la consulta tiene acceso a todos los miembros públicos y privados a los que hace referencia la consulta. En general,
LINQ to DataSet consultas no se deben pasar a los componentes que no son de confianza, a menos que la consulta
se haya construido cuidadosamente para que no exponga la información que debe mantenerse privada.

Entrada externa
A menudo, las aplicaciones obtienen entradas externas (de un usuario o de otro agente externo) y realizan acciones
basadas en dicha entrada. En el caso de LINQ to DataSet, la aplicación podría construir una consulta de una manera
determinada, basada en la entrada externa o usar la entrada externa en la consulta. LINQ to DataSet consultas
aceptan parámetros en todas partes donde se aceptan literales. Los desarrolladores de aplicaciones deben utilizar
consultas parametrizadas en lugar de insertar literales en la consulta procedentes de un agente externo.
Cualquier entrada derivada directa o indirectamente del usuario o de un agente externo puede inyectar contenido
que aproveche la sintaxis del lenguaje de destino para realizar acciones no autorizadas. Esto se conoce como
ataque de inyección de SQL porque en el patrón de ataque el idioma de destino es Transact-SQL. La entrada de
usuario inyectada directamente en la consulta se utiliza para quitar una tabla de base de datos, provocar un ataque
de denegación de servicio o cambiar la naturaleza de la operación que se está realizando. Aunque la composición
de consultas es posible en LINQ to DataSet, se realiza a través de la API del modelo de objetos. LINQ to DataSet
consultas no se componen mediante la manipulación o concatenación de cadenas, como en Transact-SQL, y no son
susceptibles de ataques de inyección de SQL en el sentido tradicional.

Vea también
Guía de programación
Ejemplos de LINQ to DataSet
23/10/2019 • 2 minutes to read • Edit Online

En esta sección se proporciona LINQ to DataSet ejemplos de programación que utilizan los operadores de
consulta estándar. El DataSet usado en estos ejemplos se rellena con el FillDataSet método, que se especifica
en cargar datos en un conjunto de datos. Para obtener más información, vea información general sobre
operadoresC#de consulta estándar () o información general sobre operadores de consulta estándar (Visual
Basic).

En esta sección
Ejemplos de expresiones de consultas
Contiene los siguientes ejemplos:
Proyección
Restricción
Creación de particiones
Ordenación
Operadores de elementos
Operadores de agregado
Operadores de combinación
Ejemplos de consultas basadas en métodos
Contiene los siguientes ejemplos:
Proyección
Particionamiento
Ordenación
Operadores Set
Operadores de conversión
Operadores de elementos
Operadores de agregado
Join
Ejemplos de operadores específicos de conjuntos de datos
Contiene ejemplos que demuestran cómo usar el método CopyToDataTable y la clase DataRowComparer.

Vea también
Guía de programación
Carga de datos en un conjunto de datos
Ejemplos de expresiones de consultas (LINQ to
DataSet)
23/10/2019 • 2 minutes to read • Edit Online

En esta sección se proporcionan ejemplos de programación de LINQ to DataSet en la sintaxis de expresiones de


consulta que utilizan los operadores de consulta estándar. El DataSet usado en estos ejemplos se rellena con el
FillDataSet método, que se especifica en cargar datos en un conjunto de datos. Para obtener más información,
vea información general sobre operadoresC#de consulta estándar () o información general sobre operadores de
consulta estándar (Visual Basic).

En esta sección
Proyección
Los ejemplos de este tema muestran cómo usar los métodos Select y SelectMany para consultar un DataSet.
Restricción
Los ejemplos de este tema muestran cómo usar el método Where para consultar un DataSet.
Creación de particiones
Los ejemplos de este tema muestran cómo usar los métodos Skip y Take para consultar un DataSet y particionar
los resultados.
Ordenación
Los ejemplos de este tema muestran cómo usar los métodos OrderBy, OrderByDescending, Reverse y
ThenByDescending para consultar un DataSet y ordenar los resultados.
Operadores de elementos
Los ejemplos de este tema muestran cómo utilizar los métodos First y ElementAt para obtener elementos DataRow
de un DataSet.
Operadores de agregado
Los ejemplos de este tema muestran cómo usar los métodos Average, Count, Max, Min y Sum para consultar
DataSet y agregar datos.
Operadores de combinación
Los ejemplos de este tema muestran cómo usar los métodos GroupJoin y Join para consultar un DataSet.

Vea también
Ejemplos de consultas basadas en métodos
Ejemplos de operadores específicos de conjuntos de datos
Ejemplos de LINQ to DataSet
Ejemplos de sintaxis de expresiones de consulta:
Proyección (LINQ to DataSet)
23/10/2019 • 6 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos Select y SelectMany para consultar un DataSet
usando la sintaxis de expresión de consultas.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Seleccionar
Ejemplo
Este ejemplo utiliza Select para devolver todas las filas de la tabla Product y mostrar los nombres de producto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
from product in products.AsEnumerable()
select product;

Console.WriteLine("Product Names:");
foreach (DataRow p in query)
{
Console.WriteLine(p.Field<string>("Name"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = From product In products.AsEnumerable() _


Select product
Console.WriteLine("Product Names:")
For Each p In query
Console.WriteLine(p.Field(Of String)("Name"))
Next

Ejemplo
Este ejemplo utiliza Select para devolver una secuencia de nombres de producto solamente.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<string> query =
from product in products.AsEnumerable()
select product.Field<string>("Name");

Console.WriteLine("Product Names:");
foreach (string productName in query)
{
Console.WriteLine(productName);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = From product In products.AsEnumerable() _


Select product.Field(Of String)("Name")

Console.WriteLine("Product Names:")
For Each productName In query
Console.WriteLine(productName)
Next

SelectMany
Ejemplo
En este ejemplo se utiliza From …, … (el equivalente del método SelectMany) para seleccionar todos los pedidos en
los que TotalDue es inferior a 500,00.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from contact in contacts.AsEnumerable()
from order in orders.AsEnumerable()
where contact.Field<int>("ContactID") == order.Field<int>("ContactID")
&& order.Field<decimal>("TotalDue") < 500.00M
select new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
FirstName = contact.Field<string>("FirstName"),
OrderID = order.Field<int>("SalesOrderID"),
Total = order.Field<decimal>("TotalDue")
};

foreach (var smallOrder in query)


{
Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due: ${4} ",
smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName,
smallOrder.OrderID, smallOrder.Total);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From contact In contacts.AsEnumerable() _
From order In orders.AsEnumerable() _
Where (contact.Field(Of Integer)("ContactID") = _
order.Field(Of Integer)("ContactID")) _
And (order.Field(Of Decimal)("TotalDue") < 500D) _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.LastName = contact.Field(Of String)("LastName"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.TotalDue = order.Field(Of Decimal)("TotalDue") _
}

For Each smallOrder In query


Console.Write("ContactID: " & smallOrder.ContactID)
Console.Write(" Name: {0}, {1}", smallOrder.LastName, _
smallOrder.FirstName)
Console.Write(" OrderID: " & smallOrder.OrderID)
Console.WriteLine(" TotalDue: $" & smallOrder.TotalDue)
Next

Ejemplo
En este ejemplo se utiliza From …, … (el equivalente del método SelectMany) para seleccionar todos los pedidos
efectuados a partir del 1 de octubre.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from contact in contacts.AsEnumerable()
from order in orders.AsEnumerable()
where contact.Field<int>("ContactID") == order.Field<int>("ContactID") &&
order.Field<DateTime>("OrderDate") >= new DateTime(2002, 10, 1)
select new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
FirstName = contact.Field<string>("FirstName"),
OrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate")
};

foreach (var order in query)


{
Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Order date: {4:d} ",
order.ContactID, order.LastName, order.FirstName,
order.OrderID, order.OrderDate);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From contact In contacts.AsEnumerable() _
From order In orders.AsEnumerable() _
Where contact.Field(Of Integer)("ContactID") = order.Field(Of Integer)("ContactID") And _
order.Field(Of DateTime)("OrderDate") >= New DateTime(2002, 10, 1) _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.LastName = contact.Field(Of String)("LastName"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate") _
}

For Each order In query


Console.Write("Contact ID: " & order.ContactID)
Console.Write(" Name: " & order.LastName & ", " & order.FirstName)
Console.Write(" Order ID: " & order.OrderID)
Console.WriteLine(" Order date: {0:d} ", order.OrderDate)
Next

Ejemplo
En este ejemplo se utiliza From …, … (el equivalente al método SelectMany) para seleccionar todos los pedidos en
los que el total del pedido es superior a 10.000,00 y utiliza la asignación From para evitar que se solicite dos veces
el total.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from contact in contacts.AsEnumerable()
from order in orders.AsEnumerable()
let total = order.Field<decimal>("TotalDue")
where contact.Field<int>("ContactID") == order.Field<int>("ContactID") &&
total >= 10000.0M
select new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
OrderID = order.Field<int>("SalesOrderID"),
total
};
foreach (var order in query)
{
Console.WriteLine("Contact ID: {0} Last name: {1} Order ID: {2} Total: {3}",
order.ContactID, order.LastName, order.OrderID, order.total);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From contact In contacts.AsEnumerable() _
From order In orders.AsEnumerable() _
Let total = order.Field(Of Decimal)("TotalDue") _
Where contact.Field(Of Integer)("ContactID") = order.Field(Of Integer)("ContactID") And _
total >= 10000D _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.LastName = contact.Field(Of String)("LastName"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
total _
}

For Each order In query


Console.Write("Contact ID: " & order.ContactID)
Console.Write(" Last Name: " & order.LastName)
Console.Write(" Order ID: " & order.OrderID)
Console.WriteLine(" Total: $" & order.total)
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de expresiones de consulta:
Restricción (LINQ to DataSet)
21/03/2020 • 4 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo utilizar el método Where para consultar DataSet utilizando sintaxis de
expresión de consultas.
El FillDataSet método utilizado en estos ejemplos se especifica en Carga de datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
Los ejemplos de este using / Imports tema utilizan las siguientes instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Para obtener más información, vea Cómo: crear un proyecto LINQ to DataSet en Visual Studio.

Where
Ejemplo
Este ejemplo devuelve todos los pedidos en línea.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
where order.Field<bool>("OnlineOrderFlag") == true
select new
{
SalesOrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate"),
SalesOrderNumber = order.Field<string>("SalesOrderNumber")
};

foreach (var onlineOrder in query)


{
Console.WriteLine("Order ID: {0} Order date: {1:d} Order number: {2}",
onlineOrder.SalesOrderID,
onlineOrder.OrderDate,
onlineOrder.SalesOrderNumber);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True _
Select New With { _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
.SalesOrderNumber = order.Field(Of String)("SalesOrderNumber") _
}

For Each onlineOrder In query


Console.Write("Order ID: " & onlineOrder.SalesOrderID)
Console.Write(" Order date: " & onlineOrder.OrderDate)
Console.WriteLine(" Order number: " & onlineOrder.SalesOrderNumber)
Next

Ejemplo
Este ejemplo devuelve los pedidos en los que la cantidad de pedido es superior a 2 e inferior a 6.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderDetail"];

var query =
from order in orders.AsEnumerable()
where order.Field<Int16>("OrderQty") > 2 &&
order.Field<Int16>("OrderQty") < 6
select new
{
SalesOrderID = (int)order.Field<int>("SalesOrderID"),
OrderQty = order.Field<Int16>("OrderQty")
};

foreach (var order in query)


{
Console.WriteLine("Order ID: {0} Order quantity: {1}",
order.SalesOrderID, order.OrderQty);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderDetail")

Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Short)("OrderQty") > 2 And _
order.Field(Of Short)("OrderQty") < 6 _
Select New With _
{ _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderQty = order.Field(Of Short)("OrderQty") _
}

For Each order In query


Console.Write("Order ID: " & order.SalesOrderID)
Console.WriteLine(" Order quantity: " & order.OrderQty)
Next

Ejemplo
Este ejemplo devuelve todos los productos de color rojo.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query =
from product in products.AsEnumerable()
where product.Field<string>("Color") == "Red"
select new
{
Name = product.Field<string>("Name"),
ProductNumber = product.Field<string>("ProductNumber"),
ListPrice = product.Field<Decimal>("ListPrice")
};

foreach (var product in query)


{
Console.WriteLine("Name: {0}", product.Name);
Console.WriteLine("Product number: {0}", product.ProductNumber);
Console.WriteLine("List price: ${0}", product.ListPrice);
Console.WriteLine("");
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Where product.Field(Of String)("Color") = "Red" _
Select New With _
{ _
.Name = product.Field(Of String)("Name"), _
.ProductNumber = product.Field(Of String)("ProductNumber"), _
.ListPrice = product.Field(Of Decimal)("ListPrice") _
}

For Each product In query


Console.WriteLine("Name: " & product.Name)
Console.WriteLine("Product number: " & product.ProductNumber)
Console.WriteLine("List price: $ " & product.ListPrice & vbNewLine)
Next

Ejemplo
En este ejemplo se utiliza el método Where para buscar pedidos efectuados con posterioridad al 1 de diciembre de
2002 y, a continuación, se utiliza el método GetChildRows para obtener los detalles de cada pedido.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

IEnumerable<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") >= new DateTime(2002, 12, 1)
select order;

Console.WriteLine("Orders that were made after 12/1/2002:");


foreach (DataRow order in query)
{
Console.WriteLine("OrderID {0} Order date: {1:d} ",
order.Field<int>("SalesOrderID"), order.Field<DateTime>("OrderDate"));
foreach (DataRow orderDetail in order.GetChildRows("SalesOrderHeaderDetail"))
{
Console.WriteLine(" Product ID: {0} Unit Price {1}",
orderDetail["ProductID"], orderDetail["UnitPrice"]);
}
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query As IEnumerable(Of DataRow) = _


From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") >= New DateTime(2002, 12, 1) _
Select order

Console.WriteLine("Orders that were made after 12/1/2002:")


For Each order As DataRow In query
Console.WriteLine("OrderID {0} Order date: {1:d} ", _
order.Field(Of Integer)("SalesOrderID"), order.Field(Of DateTime)("OrderDate"))
For Each orderDetail As DataRow In order.GetChildRows("SalesOrderHeaderDetail")
Console.WriteLine(" Product ID: {0} Unit Price {1}", _
orderDetail("ProductID"), orderDetail("UnitPrice"))
Next
Next

Consulte también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Información general sobre operadores de consulta estándar (C#)
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de expresiones de consulta:
Creación de particiones (LINQ to DataSet)
23/10/2019 • 3 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos Skip y Take para consultar un DataSet usando la
sintaxis de expresión de consultas.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Skip
Ejemplo
En este ejemplo se utiliza el método Skip para obtener las dos primeras direcciones de Seattle.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable addresses = ds.Tables["Address"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query = (
from address in addresses.AsEnumerable()
from order in orders.AsEnumerable()
where address.Field<int>("AddressID") == order.Field<int>("BillToAddressID")
&& address.Field<string>("City") == "Seattle"
select new
{
City = address.Field<string>("City"),
OrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate")
}).Skip(2);

Console.WriteLine("All but first 2 orders in Seattle:");


foreach (var order in query)
{
Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}",
order.City, order.OrderID, order.OrderDate);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim addresses As DataTable = ds.Tables("Address")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = ( _
From address In addresses.AsEnumerable() _
From order In orders.AsEnumerable() _
Where (address.Field(Of Integer)("AddressID") = _
order.Field(Of Integer)("BillToAddressID")) _
And address.Field(Of String)("City") = "Seattle" _
Select New With _
{ _
.City = address.Field(Of String)("City"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate") _
}).Skip(2)

Console.WriteLine("All but first 2 orders in Seattle:")


For Each addOrder In query
Console.Write("City: " & addOrder.City)
Console.Write(" Order ID: " & addOrder.OrderID)
Console.WriteLine(" Order date: " & addOrder.OrderDate)
Next

Take
Ejemplo
En este ejemplo se utiliza el método Take para obtener las tres primeras direcciones de Seattle.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable addresses = ds.Tables["Address"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query = (
from address in addresses.AsEnumerable()
from order in orders.AsEnumerable()
where address.Field<int>("AddressID") == order.Field<int>("BillToAddressID")
&& address.Field<string>("City") == "Seattle"
select new
{
City = address.Field<string>("City"),
OrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate")
}).Take(3);

Console.WriteLine("First 3 orders in Seattle:");


foreach (var order in query)
{
Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}",
order.City, order.OrderID, order.OrderDate);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim addresses As DataTable = ds.Tables("Address")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = ( _
From address In addresses.AsEnumerable() _
From order In orders.AsEnumerable() _
Where (address.Field(Of Integer)("AddressID") = _
order.Field(Of Integer)("BillToAddressID")) _
And address.Field(Of String)("City") = "Seattle" _
Select New With _
{ _
.City = address.Field(Of String)("City"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate") _
}).Take(3)

Console.WriteLine("First 3 orders in Seattle:")


For Each order In query
Console.Write("City: " & order.City)
Console.Write(" Order ID: " & order.OrderID)
Console.WriteLine(" Order date: " & order.OrderDate)
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de expresiones de consulta:
Ordenación (LINQ to DataSet)
23/10/2019 • 5 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo se utilizan los métodos OrderBy, OrderByDescending, Reverse y
ThenByDescending para consultar DataSet y ordenar los resultados utilizando la sintaxis de expresiones de
consultas.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, vea Cómo: Cree un proyecto de LINQ to DataSet en VisualStudio.

OrderBy
Ejemplo
En este ejemplo se utiliza OrderBy para devolver una lista de contactos organizados por apellido.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

IEnumerable<DataRow> query =
from contact in contacts.AsEnumerable()
orderby contact.Field<string>("LastName")
select contact;

Console.WriteLine("The sorted list of last names:");


foreach (DataRow contact in query)
{
Console.WriteLine(contact.Field<string>("LastName"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Select contact _
Order By contact.Field(Of String)("LastName")

Console.WriteLine("The sorted list of last names:")


For Each contact In query
Console.WriteLine(contact.Field(Of String)("LastName"))
Next

Ejemplo
En este ejemplo se utiliza OrderBy para ordenar una lista de contactos por longitud del apellido.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

IEnumerable<DataRow> query =
from contact in contacts.AsEnumerable()
orderby contact.Field<string>("LastName").Length
select contact;

Console.WriteLine("The sorted list of last names (by length):");


foreach (DataRow contact in query)
{
Console.WriteLine(contact.Field<string>("LastName"));
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Select contact _
Order By contact.Field(Of String)("LastName").Length

Console.WriteLine("The sorted list of last names (by length):")


For Each contact In query
Console.WriteLine(contact.Field(Of String)("LastName"))
Next

OrderByDescending
Ejemplo
En este ejemplo se utiliza orderby… descending ( Order By … Descending ), equivalente al método
OrderByDescending, para ordenar el precio de venta de mayor a menor.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<Decimal> query =
from product in products.AsEnumerable()
orderby product.Field<Decimal>("ListPrice") descending
select product.Field<Decimal>("ListPrice");

Console.WriteLine("The list price from highest to lowest:");


foreach (Decimal product in query)
{
Console.WriteLine(product);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Select product _
Order By product.Field(Of Decimal)("ListPrice") Descending

Console.WriteLine("The list price From highest to lowest:")

For Each product In query


Console.WriteLine(product.Field(Of Decimal)("ListPrice"))
Next
Reverse
Ejemplo
En este ejemplo se utiliza Reverse para crear una lista de pedidos en los que OrderDate es anterior al 20 de febrero
de 2002.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

IEnumerable<DataRow> query = (
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") < new DateTime(2002, 02, 20)
select order).Reverse();

Console.WriteLine("A backwards list of orders where OrderDate < Feb 20, 2002");
foreach (DataRow order in query)
{
Console.WriteLine(order.Field<DateTime>("OrderDate"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = ( _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") < New DateTime(2002, 2, 20) _
Select order).Reverse()

Console.WriteLine("A backwards list of orders where OrderDate < Feb 20, 2002")

For Each order In query


Console.WriteLine(order.Field(Of DateTime)("OrderDate"))
Next

ThenByDescending
Ejemplo
En este ejemplo se utiliza OrderBy… Descending , que es equivalente al método ThenByDescending, para ordenar
una lista de productos, primero por nombre y después por precio de venta, de mayor a menor.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
from product in products.AsEnumerable()
orderby product.Field<string>("Name"),
product.Field<Decimal>("ListPrice") descending
select product;

foreach (DataRow product in query)


{
Console.WriteLine("Product ID: {0} Product Name: {1} List Price {2}",
product.Field<int>("ProductID"),
product.Field<string>("Name"),
product.Field<Decimal>("ListPrice"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of String)("Name"), _
product.Field(Of Decimal)("ListPrice") Descending _
Select product

For Each product In query


Console.Write("Product ID: " & product.Field(Of Integer)("ProductID"))
Console.Write(" Product Name: " & product.Field(Of String)("Name"))
Console.WriteLine(" List Price: " & product.Field(Of Decimal)("ListPrice"))
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de expresiones de consulta:
Operadores de elementos (LINQ to DataSet)
23/10/2019 • 2 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos First y ElementAt para obtener elementos DataRow
de un DataSet usando la sintaxis de expresiones de consulta.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, vea Cómo: Cree un proyecto de LINQ to DataSet en VisualStudio.

ElementAt
Ejemplo
Este ejemplo usa el método ElementAt para recuperar la quinta dirección donde PostalCode == "M4B 1V7".
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable addresses = ds.Tables["Address"];

var fifthAddress = (
from address in addresses.AsEnumerable()
where address.Field<string>("PostalCode") == "M4B 1V7"
select address.Field<string>("AddressLine1"))
.ElementAt(5);

Console.WriteLine("Fifth address where PostalCode = 'M4B 1V7': {0}",


fifthAddress);

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim addresses As DataTable = ds.Tables("Address")

Dim fifthAddress = ( _
From address In addresses.AsEnumerable() _
Where address.Field(Of String)("PostalCode") = "M4B 1V7" _
Select address.Field(Of String)("AddressLine1")).ElementAt(5)

Console.WriteLine("Fifth address where PostalCode = 'M4B 1V7': " & _


fifthAddress)

Primero
Ejemplo
Este ejemplo usar el método First para devolver el primer contacto cuyo nombre es 'Brooke'.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

DataRow query = (
from contact in contacts.AsEnumerable()
where (string)contact["FirstName"] == "Brooke"
select contact)
.First();

Console.WriteLine("ContactID: " + query.Field<int>("ContactID"));


Console.WriteLine("FirstName: " + query.Field<string>("FirstName"));
Console.WriteLine("LastName: " + query.Field<string>("LastName"));
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query = ( _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Brooke" _
Select contact).First()

Console.WriteLine("ContactID: " & query.Field(Of Integer)("ContactID"))


Console.WriteLine("FirstName: " & query.Field(Of String)("FirstName"))
Console.WriteLine("LastName: " & query.Field(Of String)("LastName"))

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de expresiones de consulta:
Operadores de agregado (LINQ to DataSet)
23/10/2019 • 11 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo utilizar los métodos Average, Count, Max, Min y Sum para consultar un
DataSet y agregar datos utilizando sintaxis de expresiones de consulta.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Average
Ejemplo
En este ejemplo se utiliza el método Average para encontrar el precio de venta promedio de cada estilo de
productos.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var products = ds.Tables["Product"].AsEnumerable();

var query = from product in products


group product by product.Field<string>("Style") into g
select new
{
Style = g.Key,
AverageListPrice =
g.Average(product => product.Field<Decimal>("ListPrice"))
};

foreach (var product in query)


{
Console.WriteLine("Product style: {0} Average list price: {1}",
product.Style, product.AverageListPrice);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As IEnumerable(Of DataRow) = _


ds.Tables("Product").AsEnumerable()

Dim query = _
From product In products _
Group product By style = product.Field(Of String)("Style") Into g = Group _
Select New With _
{ _
.Style = style, _
.AverageListPrice = g.Average(Function(product) _
product.Field(Of Decimal)("ListPrice")) _
}

For Each product In query


Console.WriteLine("Product style: {0} Average list price: {1}", _
product.Style, product.AverageListPrice)
Next

Ejemplo
En este ejemplo se utiliza Average para obtener el importe total a pagar promedio para cada id. de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
averageTotalDue =
g.Average(order => order.Field<decimal>("TotalDue"))
};

foreach (var order in query)


{
Console.WriteLine("ContactID = {0} \t Average TotalDue = {1}",
order.Category,
order.averageTotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.averageTotalDue = g.Average(Function(order) order. _
Field(Of Decimal)("TotalDue")) _
}

For Each order In query


Console.WriteLine("ContactID = {0} " & vbTab & _
" Average TotalDue = {1}", order.Category, _
order.averageTotalDue)
Next

Ejemplo
En este ejemplo se utiliza Average para obtener los pedidos con el TotalDue promedio para cada de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
let averageTotalDue = g.Average(order => order.Field<decimal>("TotalDue"))
select new
{
Category = g.Key,
CheapestProducts =
g.Where(order => order.Field<decimal>("TotalDue") ==
averageTotalDue)
};

foreach (var orderGroup in query)


{
Console.WriteLine("ContactID: {0}", orderGroup.Category);
foreach (var order in orderGroup.CheapestProducts)
{
Console.WriteLine("Average total due for SalesOrderID {1} is: {0}",
order.Field<decimal>("TotalDue"),
order.Field<Int32>("SalesOrderID"));
}
Console.WriteLine("");
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Let averageTotalDue = g.Average(Function(order) order.Field(Of Decimal)("TotalDue")) _
Select New With _
{ _
.Category = contactID, _
.CheapestProducts = g.Where(Function(order) order. _
Field(Of Decimal)("TotalDue") = averageTotalDue) _
}

For Each orderGroup In query


Console.WriteLine("ContactID: " & orderGroup.Category)
For Each order In orderGroup.CheapestProducts
Console.WriteLine("Average total due for SalesOrderID {1} is: {0}", _
order.Field(Of Decimal)("TotalDue"), _
order.Field(Of Int32)("SalesOrderID"))
Next
Console.WriteLine("")
Next

Número
Ejemplo
En este ejemplo se utiliza Count para devolver una lista de id. de contactos y el número de pedidos que tiene cada
uno de ellos.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

var query = from contact in contacts.AsEnumerable()


select new
{
CustomerID = contact.Field<int>("ContactID"),
OrderCount =
contact.GetChildRows("SalesOrderContact").Count()
};

foreach (var contact in query)


{
Console.WriteLine("CustomerID = {0} \t OrderCount = {1}",
contact.CustomerID,
contact.OrderCount);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.OrderCount = contact.GetChildRows("SalesOrderContact").Count() _
}

For Each contact In query


Console.Write("CustomerID = " & contact.ContactID)
Console.WriteLine(vbTab & "OrderCount = " & contact.OrderCount)
Next

Ejemplo
En este ejemplo se agrupan los productos por colores y se utiliza Count para devolver el número de productos de
cada grupo de color.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query =
from product in products.AsEnumerable()
group product by product.Field<string>("Color") into g
select new { Color = g.Key, ProductCount = g.Count() };

foreach (var product in query)


{
Console.WriteLine("Color = {0} \t ProductCount = {1}",
product.Color,
product.ProductCount);
}

Dim ds As New DataSet()


ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Group product By color = product.Field(Of String)("Color") Into g = Group _
Select New With {.Color = color, .ProductCount = g.Count()}

For Each product In query


Console.WriteLine("Color = {0} " & vbTab & "ProductCount = {1}", _
product.Color, _
product.ProductCount)
Next

Máx.
Ejemplo
En este ejemplo se utiliza el método Max para obtener el mayor importe total a pagar de cada id. de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
maxTotalDue =
g.Max(order => order.Field<decimal>("TotalDue"))
};

foreach (var order in query)


{
Console.WriteLine("ContactID = {0} \t Maximum TotalDue = {1}",
order.Category, order.maxTotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.maxTotalDue = _
g.Max(Function(order) order.Field(Of Decimal)("TotalDue")) _
}
For Each order In query
Console.WriteLine("ContactID = {0} " & vbTab & _
" Maximum TotalDue = {1}", _
order.Category, order.maxTotalDue)
Next

Ejemplo
En este ejemplo se utiliza el método Max para obtener los pedidos con el TotalDue mayor de cada id. contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
let maxTotalDue = g.Max(order => order.Field<decimal>("TotalDue"))
select new
{
Category = g.Key,
CheapestProducts =
g.Where(order => order.Field<decimal>("TotalDue") ==
maxTotalDue)
};

foreach (var orderGroup in query)


{
Console.WriteLine("ContactID: {0}", orderGroup.Category);
foreach (var order in orderGroup.CheapestProducts)
{
Console.WriteLine("MaxTotalDue {0} for SalesOrderID {1}: ",
order.Field<decimal>("TotalDue"),
order.Field<Int32>("SalesOrderID"));
}
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Let maxTotalDue = g.Max(Function(order) order.Field(Of Decimal)("TotalDue")) _
Select New With _
{ _
.Category = contactID, _
.CheapestProducts = _
g.Where(Function(order) order. _
Field(Of Decimal)("TotalDue") = maxTotalDue) _
}

For Each orderGroup In query


Console.WriteLine("ContactID: " & orderGroup.Category)
For Each order In orderGroup.CheapestProducts
Console.WriteLine("MaxTotalDue {0} for SalesOrderID {1} ", _
order.Field(Of Decimal)("TotalDue"), _
order.Field(Of Int32)("SalesOrderID"))
Next
Next

Mín.
Ejemplo
En este ejemplo se utiliza el método Min para obtener el menor importe total a pagar de cada id. de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
smallestTotalDue =
g.Min(order => order.Field<decimal>("TotalDue"))
};

foreach (var order in query)


{
Console.WriteLine("ContactID = {0} \t Minimum TotalDue = {1}",
order.Category, order.smallestTotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.smallestTotalDue = g.Min(Function(order) _
order.Field(Of Decimal)("TotalDue")) _
}

For Each order In query


Console.WriteLine("ContactID = {0} " & vbTab & _
"Minimum TotalDue = {1}", order.Category, order.smallestTotalDue)
Next

Ejemplo
En este ejemplo se utiliza el método Min para obtener el pedido con el menor importe total a pagar de cada
contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
let minTotalDue = g.Min(order => order.Field<decimal>("TotalDue"))
select new
{
Category = g.Key,
smallestTotalDue =
g.Where(order => order.Field<decimal>("TotalDue") ==
minTotalDue)
};

foreach (var orderGroup in query)


{
Console.WriteLine("ContactID: {0}", orderGroup.Category);
foreach (var order in orderGroup.smallestTotalDue)
{
Console.WriteLine("Mininum TotalDue {0} for SalesOrderID {1}: ",
order.Field<decimal>("TotalDue"),
order.Field<Int32>("SalesOrderID"));
}
Console.WriteLine("");
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Let minTotalDue = g.Min(Function(order) order.Field(Of Decimal)("TotalDue")) _
Select New With _
{ _
.Category = contactID, _
.smallestTotalDue = g.Where(Function(order) order. _
Field(Of Decimal)("TotalDue") = minTotalDue) _
}

For Each orderGroup In query


Console.WriteLine("ContactID: " & orderGroup.Category)
For Each order In orderGroup.smallestTotalDue
Console.WriteLine("Mininum TotalDue {0} for SalesOrderID {1} ", _
order.Field(Of Decimal)("TotalDue"), _
order.Field(Of Int32)("SalesOrderID"))
Next
Console.WriteLine("")
Next

Sum
Ejemplo
En este ejemplo se utiliza el método Sum para obtener el importe total a pagar de cada id. de contacto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
TotalDue = g.Sum(order => order.Field<decimal>("TotalDue")),
};
foreach (var order in query)
{
Console.WriteLine("ContactID = {0} \t TotalDue sum = {1}",
order.Category, order.TotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.TotalDue = g.Sum(Function(order) order. _
Field(Of Decimal)("TotalDue")) _
}

For Each order In query


Console.WriteLine("ContactID = {0} " & vbTab & "TotalDue sum = {1}", _
order.Category, order.TotalDue)
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de expresiones de consulta:
Operadores de combinación (LINQ to DataSet)
23/10/2019 • 5 minutes to read • Edit Online

La combinación es una operación importante de las consultas dirigidas a orígenes de datos que no tienen
relaciones navegables entre ellos, como las tablas de bases de datos relacionales. Una combinación de dos
orígenes de datos es la asociación de objetos en un origen de datos con objetos que comparten un atributo común
en el otro origen de datos. Para obtener más información, vea información general sobre operadoresC#de consulta
estándar () o información general sobre operadores de consulta estándar (Visual Basic).
Los ejemplos de este tema muestran cómo usar los métodos GroupJoin y Join para consultar un DataSet usando la
sintaxis de expresión de consultas.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

GroupJoin
Ejemplo
Este ejemplo realiza un GroupJoin en las tablas SalesOrderHeader y SalesOrderDetail para buscar el número de
pedidos por cliente. Una combinación de grupo es el equivalente a una combinación externa izquierda, que
devuelve cada elemento del primer origen de datos (izquierdo), incluso si no hay elementos correlacionados en el
otro origen de datos.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var orders = ds.Tables["SalesOrderHeader"].AsEnumerable();


var details = ds.Tables["SalesOrderDetail"].AsEnumerable();

var query =
from order in orders
join detail in details
on order.Field<int>("SalesOrderID")
equals detail.Field<int>("SalesOrderID") into ords
select new
{
CustomerID =
order.Field<int>("SalesOrderID"),
ords = ords.Count()
};

foreach (var order in query)


{
Console.WriteLine("CustomerID: {0} Orders Count: {1}",
order.CustomerID,
order.ords);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders = ds.Tables("SalesOrderHeader").AsEnumerable()


Dim details = ds.Tables("SalesOrderDetail").AsEnumerable()

Dim query = _
From order In orders _
Group Join detail In details _
On order.Field(Of Integer)("SalesOrderID") _
Equals detail.Field(Of Integer)("SalesOrderID") Into ords = Group _
Select New With _
{ _
.CustomerID = order.Field(Of Integer)("SalesOrderID"), _
.ords = ords.Count() _
}

For Each order In query


Console.WriteLine("CustomerID: {0} Orders Count: {1}", _
order.CustomerID, order.ords)
Next

Ejemplo
Este ejemplo realiza un GroupJoin en las tablas Contact y SalesOrderHeader . Una combinación de grupo es el
equivalente a una combinación externa izquierda, que devuelve cada elemento del primer origen de datos
(izquierdo), incluso si no hay elementos correlacionados en el otro origen de datos.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from contact in contacts.AsEnumerable()
join order in orders.AsEnumerable()
on contact.Field<Int32>("ContactID") equals
order.Field<Int32>("ContactID")
select new
{
ContactID = contact.Field<Int32>("ContactID"),
SalesOrderID = order.Field<Int32>("SalesOrderID"),
FirstName = contact.Field<string>("FirstName"),
Lastname = contact.Field<string>("Lastname"),
TotalDue = order.Field<decimal>("TotalDue")
};

foreach (var contact_order in query)


{
Console.WriteLine("ContactID: {0} "
+ "SalesOrderID: {1} "
+ "FirstName: {2} "
+ "Lastname: {3} "
+ "TotalDue: {4}",
contact_order.ContactID,
contact_order.SalesOrderID,
contact_order.FirstName,
contact_order.Lastname,
contact_order.TotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From contact In contacts.AsEnumerable(), order In orders.AsEnumerable() _
Where (contact.Field(Of Integer)("ContactID") = _
order.Field(Of Integer)("ContactID")) _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.Lastname = contact.Field(Of String)("Lastname"), _
.TotalDue = order.Field(Of Decimal)("TotalDue") _
}

For Each contact_order In query


Console.Write("ContactID: " & contact_order.ContactID)
Console.Write(" SalesOrderID: " & contact_order.SalesOrderID)
Console.Write(" FirstName: " & contact_order.FirstName)
Console.Write(" Lastname: " & contact_order.Lastname)
Console.WriteLine(" TotalDue: " & contact_order.TotalDue)
Next
Unir
Ejemplo
Este ejemplo realiza una combinación en las tablas SalesOrderHeader y SalesOrderDetail para obtener pedidos en
línea del mes de agosto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];


DataTable details = ds.Tables["SalesOrderDetail"];

var query =
from order in orders.AsEnumerable()
join detail in details.AsEnumerable()
on order.Field<int>("SalesOrderID") equals
detail.Field<int>("SalesOrderID")
where order.Field<bool>("OnlineOrderFlag") == true
&& order.Field<DateTime>("OrderDate").Month == 8
select new
{
SalesOrderID =
order.Field<int>("SalesOrderID"),
SalesOrderDetailID =
detail.Field<int>("SalesOrderDetailID"),
OrderDate =
order.Field<DateTime>("OrderDate"),
ProductID =
detail.Field<int>("ProductID")
};

foreach (var order in query)


{
Console.WriteLine("{0}\t{1}\t{2:d}\t{3}",
order.SalesOrderID,
order.SalesOrderDetailID,
order.OrderDate,
order.ProductID);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")


Dim details As DataTable = ds.Tables("SalesOrderDetail")

Dim query = _
From order In orders.AsEnumerable() _
Join detail In details.AsEnumerable() _
On order.Field(Of Integer)("SalesOrderID") Equals _
detail.Field(Of Integer)("SalesOrderID") _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True And _
order.Field(Of DateTime)("OrderDate").Month = 8 _
Select New With _
{ _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.SalesOrderDetailID = detail.Field(Of Integer)("SalesOrderDetailID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
.ProductID = detail.Field(Of Integer)("ProductID") _
}

For Each order In query


Console.WriteLine(order.SalesOrderID & vbTab & _
order.SalesOrderDetailID & vbTab & _
order.OrderDate & vbTab & _
order.ProductID)
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de consultas basadas en métodos (LINQ to
DataSet)
23/10/2019 • 2 minutes to read • Edit Online

En esta sección se proporciona LINQ to DataSet ejemplos de programación de la sintaxis de consulta basada en
métodos que usan los operadores de consulta estándar. El DataSet usado en estos ejemplos se rellena con el
FillDataSet método, que se especifica en cargar datos en un conjunto de datos. Para obtener más información,
vea información general sobre operadoresC#de consulta estándar () o información general sobre operadores de
consulta estándar (Visual Basic).

En esta sección
Proyección
Los ejemplos de este tema muestran cómo usar los métodos Select y SelectMany para consultar un DataSet.
Creación de particiones
Los ejemplos de este tema muestran cómo usar los métodos Skip y Take para consultar un DataSet y particionar
los resultados.
Ordenación
Los ejemplos de este tema muestran cómo usar los métodos OrderBy, OrderByDescending, Reverse y
ThenByDescending para consultar un DataSet y ordenar los resultados.
Operadores Set
Los ejemplos de este tema muestran cómo usar los operadores Distinct, Except, Intersect y Union para operaciones
de comparación basadas en valores en conjuntos de filas de datos.
Operadores de conversión
Los ejemplos de este tema muestran cómo usar los métodos ToArray, ToDictionary y ToList para ejecutar
inmediatamente una expresión de consulta.
Operadores de elementos
Los ejemplos de este tema muestran cómo utilizar los métodos First y ElementAt para obtener elementos DataRow
de un DataSet.
Operadores de agregado
Los ejemplos de este tema muestran cómo usar los métodos Average, Count, Max, Min y Sum para consultar
DataSet y agregar datos.
Join
Los ejemplos de este tema muestran cómo usar los métodos GroupJoin y Join para consultar un DataSet.

Vea también
Ejemplos de expresiones de consultas
Ejemplos de operadores específicos de conjuntos de datos
Ejemplos de LINQ to DataSet
Ejemplos de sintaxis de consulta basada en métodos:
Proyección (LINQ to DataSet)
23/10/2019 • 4 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos Select y SelectMany para consultar DataSet usando la
sintaxis de consulta basada en métodos.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, vea Cómo: Cree un proyecto de LINQ to DataSet en VisualStudio.

Seleccionar
Ejemplo
En este ejemplo se utiliza el método Select para proyectar las propiedades Name , ProductNumber y ListPrice en
una secuencia de tipos anónimos. El nombre de la propiedad ListPrice se cambia a Price en el tipo resultante.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query = products.AsEnumerable().


Select(product => new
{
ProductName = product.Field<string>("Name"),
ProductNumber = product.Field<string>("ProductNumber"),
Price = product.Field<decimal>("ListPrice")
});

Console.WriteLine("Product Info:");
foreach (var productInfo in query)
{
Console.WriteLine("Product name: {0} Product number: {1} List price: ${2} ",
productInfo.ProductName, productInfo.ProductNumber, productInfo.Price);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = products.AsEnumerable() _


.Select(Function(product As DataRow) New With _
{ _
.ProductName = product.Field(Of String)("Name"), _
.ProductNumber = product.Field(Of String)("ProductNumber"), _
.Price = product.Field(Of Decimal)("ListPrice") _
})

Console.WriteLine("Product Info:")
For Each product In query
Console.Write("Product name: " & product.ProductName)
Console.Write("Product number: " & product.ProductNumber)
Console.WriteLine("List price: $ " & product.Price)
Next

SelectMany
Ejemplo
En este ejemplo se utiliza el método SelectMany para seleccionar todos los pedidos en los que TotalDue es
inferior a 500,00.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var contacts = ds.Tables["Contact"].AsEnumerable();


var orders = ds.Tables["SalesOrderHeader"].AsEnumerable();

var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.Field<Int32>("ContactID") == order.Field<Int32>("ContactID"))
&& order.Field<decimal>("TotalDue") < 500.00M)
.Select(order => new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
FirstName = contact.Field<string>("FirstName"),
OrderID = order.Field<int>("SalesOrderID"),
Total = order.Field<decimal>("TotalDue")
}));

foreach (var smallOrder in query)


{
Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due: ${4} ",
smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName,
smallOrder.OrderID, smallOrder.Total);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts = ds.Tables("Contact").AsEnumerable()


Dim orders = ds.Tables("SalesOrderHeader").AsEnumerable()

Dim query = _
contacts.SelectMany( _
Function(contact) orders.Where(Function(order) _
(contact.Field(Of Int32)("ContactID") = order.Field(Of Int32)("ContactID")) _
And order.Field(Of Decimal)("TotalDue") < 500D) _
.Select(Function(order) New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.LastName = contact.Field(Of String)("LastName"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.Total = order.Field(Of Decimal)("TotalDue") _
}))

For Each smallOrder In query


Console.Write("ContactID: " & smallOrder.ContactID)
Console.Write(" Name: " & smallOrder.LastName & ", " & smallOrder.FirstName)
Console.Write(" Order ID: " & smallOrder.OrderID)
Console.WriteLine(" Total Due: $" & smallOrder.Total)
Next

Ejemplo
En este ejemplo se utiliza el método SelectMany para seleccionar todos los pedidos efectuados a partir del 1 de
octubre.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var contacts = ds.Tables["Contact"].AsEnumerable();


var orders = ds.Tables["SalesOrderHeader"].AsEnumerable();

var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.Field<Int32>("ContactID") == order.Field<Int32>("ContactID"))
&& order.Field<DateTime>("OrderDate") >= new DateTime(2002, 10, 1))
.Select(order => new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
FirstName = contact.Field<string>("FirstName"),
OrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate")
}));

foreach (var order in query)


{
Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Order date: {4:d} ",
order.ContactID, order.LastName, order.FirstName,
order.OrderID, order.OrderDate);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts = ds.Tables("Contact").AsEnumerable()


Dim orders = ds.Tables("SalesOrderHeader").AsEnumerable()

Dim query = _
contacts.SelectMany( _
Function(contact) orders.Where(Function(order) _
(contact.Field(Of Int32)("ContactID") = order.Field(Of Int32)("ContactID")) _
And order.Field(Of DateTime)("OrderDate") >= New DateTime(2002, 10, 1)) _
.Select(Function(order) New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.LastName = contact.Field(Of String)("LastName"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate") _
}))

For Each order In query


Console.Write("Contact ID: " & order.ContactID)
Console.Write(" Name: " & order.LastName & ", " & order.FirstName)
Console.Write(" Order ID: " & order.OrderID)
Console.WriteLine(" Order date: {0:d}", order.OrderDate)
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
Creación de particiones (LINQ
23/10/2019 • 6 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos Skip, SkipWhile, Take y TakeWhile para consultar un
DataSet usando la sintaxis de expresión de consultas.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Skip
Ejemplo
En este ejemplo se utiliza el método Skip para obtener todos los contactos menos los cinco primeros de la tabla
Contact .
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

IEnumerable<DataRow> allButFirst5Contacts = contacts.AsEnumerable().Skip(5);

Console.WriteLine("All but first 5 contacts:");


foreach (DataRow contact in allButFirst5Contacts)
{
Console.WriteLine("FirstName = {0} \tLastname = {1}",
contact.Field<string>("FirstName"),
contact.Field<string>("Lastname"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim allButFirst5Contacts = contacts.AsEnumerable().Skip(5)

Console.WriteLine("All but first 5 contacts:")

For Each contact In allButFirst5Contacts


Console.Write("FirstName = {0} ", contact.Field(Of String)("FirstName"))
Console.WriteLine(vbTab & " LastName = " & contact.Field(Of String)("LastName"))
Next

Ejemplo
En este ejemplo se utiliza el método Skip para obtener las dos primeras direcciones de Seattle.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable addresses = ds.Tables["Address"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query = (
from address in addresses.AsEnumerable()
from order in orders.AsEnumerable()
where address.Field<int>("AddressID") == order.Field<int>("BillToAddressID")
&& address.Field<string>("City") == "Seattle"
select new
{
City = address.Field<string>("City"),
OrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate")
}).Skip(2);

Console.WriteLine("All but first 2 orders in Seattle:");


foreach (var order in query)
{
Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}",
order.City, order.OrderID, order.OrderDate);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim addresses As DataTable = ds.Tables("Address")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = ( _
From address In addresses.AsEnumerable() _
From order In orders.AsEnumerable() _
Where (address.Field(Of Integer)("AddressID") = _
order.Field(Of Integer)("BillToAddressID")) _
And address.Field(Of String)("City") = "Seattle" _
Select New With _
{ _
.City = address.Field(Of String)("City"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate") _
}).Skip(2)

Console.WriteLine("All but first 2 orders in Seattle:")


For Each addOrder In query
Console.Write("City: " & addOrder.City)
Console.Write(" Order ID: " & addOrder.OrderID)
Console.WriteLine(" Order date: " & addOrder.OrderDate)
Next

SkipWhile
Ejemplo
En este ejemplo se utilizan los métodos OrderBy y SkipWhile para devolver productos de la tabla Product con un
precio de venta superior a 300,00.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> skipWhilePriceLessThan300 =
products.AsEnumerable()
.OrderBy(listprice => listprice.Field<decimal>("ListPrice"))
.SkipWhile(product => product.Field<decimal>("ListPrice") < 300.00M);

Console.WriteLine("Skip while ListPrice is less than 300.00:");


foreach (DataRow product in skipWhilePriceLessThan300)
{
Console.WriteLine(product.Field<decimal>("ListPrice"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")


Dim skipWhilePriceLessThan300 As IEnumerable(Of DataRow) = _
products.AsEnumerable() _
.OrderBy(Function(listprice) listprice.Field(Of Decimal)("ListPrice")) _
.SkipWhile(Function(product) product.Field(Of Decimal)("ListPrice") < 300D)

Console.WriteLine("First ListPrice less than 300.00:")


For Each product As DataRow In skipWhilePriceLessThan300
Console.WriteLine(product.Field(Of Decimal)("ListPrice"))
Next

Take
Ejemplo
En este ejemplo se utiliza el método Take para obtener únicamente los cinco primeros contactos de la tabla
Contact .

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

IEnumerable<DataRow> first5Contacts = contacts.AsEnumerable().Take(5);

Console.WriteLine("First 5 contacts:");
foreach (DataRow contact in first5Contacts)
{
Console.WriteLine("Title = {0} \t FirstName = {1} \t Lastname = {2}",
contact.Field<string>("Title"),
contact.Field<string>("FirstName"),
contact.Field<string>("Lastname"));
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim first5Contacts = contacts.AsEnumerable().Take(5)

Console.WriteLine("First 5 contacts:")
For Each contact In first5Contacts
Console.Write("Title = " & contact.Field(Of String)("Title"))
Console.Write(vbTab & "FirstName = " & contact.Field(Of String)("FirstName"))
Console.WriteLine(vbTab & "LastName = " & contact.Field(Of String)("LastName"))
Next

Ejemplo
En este ejemplo se utiliza el método Take para obtener las tres primeras direcciones de Seattle.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable addresses = ds.Tables["Address"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query = (
from address in addresses.AsEnumerable()
from order in orders.AsEnumerable()
where address.Field<int>("AddressID") == order.Field<int>("BillToAddressID")
&& address.Field<string>("City") == "Seattle"
select new
{
City = address.Field<string>("City"),
OrderID = order.Field<int>("SalesOrderID"),
OrderDate = order.Field<DateTime>("OrderDate")
}).Take(3);

Console.WriteLine("First 3 orders in Seattle:");


foreach (var order in query)
{
Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}",
order.City, order.OrderID, order.OrderDate);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim addresses As DataTable = ds.Tables("Address")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = ( _
From address In addresses.AsEnumerable() _
From order In orders.AsEnumerable() _
Where (address.Field(Of Integer)("AddressID") = _
order.Field(Of Integer)("BillToAddressID")) _
And address.Field(Of String)("City") = "Seattle" _
Select New With _
{ _
.City = address.Field(Of String)("City"), _
.OrderID = order.Field(Of Integer)("SalesOrderID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate") _
}).Take(3)

Console.WriteLine("First 3 orders in Seattle:")


For Each order In query
Console.Write("City: " & order.City)
Console.Write(" Order ID: " & order.OrderID)
Console.WriteLine(" Order date: " & order.OrderDate)
Next

TakeWhile
Ejemplo
En este ejemplo se utilizan los métodos OrderBy y TakeWhile para devolver de la tabla productos Product con un
precio de venta inferior a 300,00.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> takeWhileListPriceLessThan300 =
products.AsEnumerable()
.OrderBy(listprice => listprice.Field<decimal>("ListPrice"))
.TakeWhile(product => product.Field<decimal>("ListPrice") < 300.00M);

Console.WriteLine("First ListPrice less than 300:");


foreach (DataRow product in takeWhileListPriceLessThan300)
{
Console.WriteLine(product.Field<decimal>("ListPrice"));
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim takeWhileListPriceLessThan300 As IEnumerable(Of DataRow) = _


products.AsEnumerable() _
.OrderBy(Function(listprice) listprice.Field(Of Decimal)("ListPrice")) _
.TakeWhile(Function(product) product.Field(Of Decimal)("ListPrice") < 300D)

Console.WriteLine("First ListPrice less than 300.00:")


For Each product As DataRow In takeWhileListPriceLessThan300
Console.WriteLine(product.Field(Of Decimal)("ListPrice"))
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
Ordenación (LINQ to DataSet)
23/10/2019 • 3 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo se utilizan los métodos OrderBy, Reverse y ThenBy para consultar
DataSet y ordenar los resultados utilizando la sintaxis de consulta basada en métodos.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

OrderBy
Ejemplo
Este ejemplo utiliza el método OrderBy con un comparador personalizado para realizar una ordenación con
distinción de mayúsculas y minúsculas de apellidos.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

IEnumerable<DataRow> query =
contacts.AsEnumerable().OrderBy(contact => contact.Field<string>("LastName"),
new CaseInsensitiveComparer());

foreach (DataRow contact in query)


{
Console.WriteLine(contact.Field<string>("LastName"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query As IEnumerable(Of DataRow) = _


contacts.AsEnumerable().OrderBy(Function(contact) _
contact.Field(Of String)("LastName"), _
New CaseInsensitiveComparer())

For Each contact As DataRow In query


Console.WriteLine(contact.Field(Of String)("LastName"))
Next

Reverse
Ejemplo
En este ejemplo se utiliza el método Reverse para crear una lista de pedidos en los que OrderDate es anterior al 20
de febrero de 2002.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

IEnumerable<DataRow> query = (
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") < new DateTime(2002, 02, 20)
select order).Reverse();

Console.WriteLine("A backwards list of orders where OrderDate < Feb 20, 2002");
foreach (DataRow order in query)
{
Console.WriteLine(order.Field<DateTime>("OrderDate"));
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = ( _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") < New DateTime(2002, 2, 20) _
Select order).Reverse()

Console.WriteLine("A backwards list of orders where OrderDate < Feb 20, 2002")

For Each order In query


Console.WriteLine(order.Field(Of DateTime)("OrderDate"))
Next

ThenBy
Ejemplo
En este ejemplo se utilizan los métodos OrderBy y ThenBy con un comparador personalizado para ordenar
primero por precio de venta y después realizar una ordenación con distinción de mayúsculas y minúsculas en
orden descendente de nombres de producto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
products.AsEnumerable().OrderBy(product => product.Field<Decimal>("ListPrice"))
.ThenBy(product => product.Field<string>("Name"),
new CaseInsensitiveComparer());

foreach (DataRow product in query)


{
Console.WriteLine("Product ID: {0} Product Name: {1} List Price {2}",
product.Field<int>("ProductID"),
product.Field<string>("Name"),
product.Field<Decimal>("ListPrice"));
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query As IEnumerable(Of DataRow) = _


products.AsEnumerable().OrderBy(Function(product) product.Field(Of Decimal)("ListPrice")) _
.ThenBy(Function(product) product.Field(Of String)("Name"), _
New CaseInsensitiveComparer())

For Each product As DataRow In query


Console.WriteLine("Product ID: {0} Product Name: {1} List Price {2}", _
product.Field(Of Integer)("ProductID"), _
product.Field(Of String)("Name"), _
product.Field(Of Decimal)("ListPrice"))
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
Operadores de conjuntos (LINQ to DataSet)
23/10/2019 • 6 minutes to read • Edit Online

En los ejemplos de este tema se muestra cómo usar Distinctlos Exceptoperadores Intersect,, Union y para realizar
operaciones de comparación basadas en valores en conjuntos de filas de datos. Cargar datos en un conjunto de
datos ; vea comparar filas de datos para DataRowComparerobtener más información sobre.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Distinct
Ejemplo
Este ejemplo usa el método Distinct para quitar elementos duplicados de una secuencia.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

List<DataRow> rows = new List<DataRow>();

DataTable contact = ds.Tables["Contact"];

// Get 100 rows from the Contact table.


IEnumerable<DataRow> query = (from c in contact.AsEnumerable()
select c).Take(100);

DataTable contactsTableWith100Rows = query.CopyToDataTable();

// Add 100 rows to the list.


foreach (DataRow row in contactsTableWith100Rows.Rows)
rows.Add(row);

// Create duplicate rows by adding the same 100 rows to the list.
foreach (DataRow row in contactsTableWith100Rows.Rows)
rows.Add(row);

DataTable table =
System.Data.DataTableExtensions.CopyToDataTable<DataRow>(rows);

// Find the unique contacts in the table.


IEnumerable<DataRow> uniqueContacts =
table.AsEnumerable().Distinct(DataRowComparer.Default);

Console.WriteLine("Unique contacts:");
foreach (DataRow uniqueContact in uniqueContacts)
{
Console.WriteLine(uniqueContact.Field<Int32>("ContactID"));
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim rows As List(Of DataRow) = New List(Of DataRow)

Dim contacts As DataTable = ds.Tables("Contact")

' Get 100 rows from the Contact table.


Dim query = ( _
From c In contacts.AsEnumerable() _
Select c).Take(100)

Dim contactsTableWith100Rows = query.CopyToDataTable()

' Add 100 rows to the list.


For Each row In contactsTableWith100Rows.Rows
rows.Add(row)
Next

' Create duplicate rows by adding the same 100 rows to the list.
For Each row In contactsTableWith100Rows.Rows
rows.Add(row)
Next

Dim table = _
System.Data.DataTableExtensions.CopyToDataTable(Of DataRow)(rows)

' Find the unique contacts in the table.


Dim uniqueContacts = _
table.AsEnumerable().Distinct(DataRowComparer.Default)

Console.WriteLine("Unique contacts:")
For Each uniqueContact In uniqueContacts
Console.WriteLine(uniqueContact.Field(Of Integer)("ContactID"))
Next

Except
Ejemplo
Este ejemplo usa el método Except para devolver contactos que aparecen en la primera tabla pero no en la
segunda.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contactTable = ds.Tables["Contact"];

// Create two tables.


IEnumerable<DataRow> query1 = from contact in contactTable.AsEnumerable()
where contact.Field<string>("Title") == "Ms."
select contact;

IEnumerable<DataRow> query2 = from contact in contactTable.AsEnumerable()


where contact.Field<string>("FirstName") == "Sandra"
select contact;

DataTable contacts1 = query1.CopyToDataTable();


DataTable contacts2 = query2.CopyToDataTable();

// Find the contacts that are in the first


// table but not the second.
var contacts = contacts1.AsEnumerable().Except(contacts2.AsEnumerable(),
DataRowComparer.Default);

Console.WriteLine("Except of employees tables");


foreach (DataRow row in contacts)
{
Console.WriteLine("Id: {0} {1} {2} {3}",
row["ContactID"], row["Title"], row["FirstName"], row["LastName"]);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contactTable As DataTable = ds.Tables("Contact")

Dim query1 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("Title") = "Ms." _
Select contact

Dim query2 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Sandra" _
Select contact

Dim contacts1 = query1.CopyToDataTable()


Dim contacts2 = query2.CopyToDataTable()

' Find the contacts that are in the first


' table but not the second.
Dim contacts = contacts1.AsEnumerable().Except(contacts2.AsEnumerable(), _
DataRowComparer.Default)

Console.WriteLine("Except of employees tables")

For Each row In contacts


Console.WriteLine("Id: {0} {1} {2} {3}", _
row("ContactID"), row("Title"), row("FirstName"), row("LastName"))
Next
Formar intersección
Ejemplo
Este ejemplo usa el método Intersect para devolver contactos que aparecen en ambas tablas.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contactTable = ds.Tables["Contact"];

// Create two tables.


IEnumerable<DataRow> query1 = from contact in contactTable.AsEnumerable()
where contact.Field<string>("Title") == "Ms."
select contact;

IEnumerable<DataRow> query2 = from contact in contactTable.AsEnumerable()


where contact.Field<string>("FirstName") == "Sandra"
select contact;

DataTable contacts1 = query1.CopyToDataTable();


DataTable contacts2 = query2.CopyToDataTable();

// Find the intersection of the two tables.


var contacts = contacts1.AsEnumerable().Intersect(contacts2.AsEnumerable(),
DataRowComparer.Default);

Console.WriteLine("Intersection of contacts tables");


foreach (DataRow row in contacts)
{
Console.WriteLine("Id: {0} {1} {2} {3}",
row["ContactID"], row["Title"], row["FirstName"], row["LastName"]);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contactTable As DataTable = ds.Tables("Contact")

Dim query1 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("Title") = "Ms." _
Select contact

Dim query2 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Sandra" _
Select contact

Dim contacts1 = query1.CopyToDataTable()


Dim contacts2 = query2.CopyToDataTable()

Dim contacts = contacts1.AsEnumerable() _


.Intersect(contacts2.AsEnumerable(), DataRowComparer.Default)

Console.WriteLine("Intersect of employees tables")

For Each row In contacts


Console.WriteLine("Id: {0} {1} {2} {3}", _
row("ContactID"), row("Title"), row("FirstName"), row("LastName"))
Next

Unión
Ejemplo
Este ejemplo usa el método Union para devolver contactos únicos de cualquiera de las dos tablas.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

// Create two tables.


DataTable contactTable = ds.Tables["Contact"];
IEnumerable<DataRow> query1 = from contact in contactTable.AsEnumerable()
where contact.Field<string>("Title") == "Ms."
select contact;

IEnumerable<DataRow> query2 = from contact in contactTable.AsEnumerable()


where contact.Field<string>("FirstName") == "Sandra"
select contact;

DataTable contacts1 = query1.CopyToDataTable();


DataTable contacts2 = query2.CopyToDataTable();

// Find the union of the two tables.


var contacts = contacts1.AsEnumerable().Union(contacts2.AsEnumerable(),
DataRowComparer.Default);

Console.WriteLine("Union of contacts tables:");


foreach (DataRow row in contacts)
{
Console.WriteLine("Id: {0} {1} {2} {3}",
row["ContactID"], row["Title"], row["FirstName"], row["LastName"]);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contactTable As DataTable = ds.Tables("Contact")

Dim query1 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("Title") = "Ms." _
Select contact

Dim query2 = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Sandra" _
Select contact

Dim contacts1 = query1.CopyToDataTable()


Dim contacts2 = query2.CopyToDataTable()

Dim contacts = contacts1.AsEnumerable().Union(contacts2.AsEnumerable(), _


DataRowComparer.Default)

Console.WriteLine("Union of employees tables")


For Each row In contacts
Console.WriteLine("Id: {0} {1} {2} {3}", _
row("ContactID"), row("Title"), row("FirstName"), row("LastName"))
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
Operadores de conversión (LINQ to DataSet)
23/10/2019 • 3 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos ToArray, ToDictionary y ToList para ejecutar
inmediatamente una expresión de consulta.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, vea Cómo: Cree un proyecto de LINQ to DataSet en VisualStudio.

ToArray
Ejemplo
En este ejemplo se utiliza el método ToArray para evaluar de forma inmediata una secuencia en una matriz.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> productsArray = products.AsEnumerable().ToArray();

IEnumerable<DataRow> query =
from product in productsArray
orderby product.Field<Decimal>("ListPrice") descending
select product;

Console.WriteLine("Every price from highest to lowest:");


foreach (DataRow product in query)
{
Console.WriteLine(product.Field<Decimal>("ListPrice"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim productsArray = products.AsEnumerable().ToArray()

Dim query = _
From product In productsArray _
Select product _
Order By product.Field(Of Decimal)("ListPrice") Descending

Console.WriteLine("Every price From highest to lowest:")


For Each product In query
Console.WriteLine(product.Field(Of Decimal)("ListPrice"))
Next

ToDictionary
Ejemplo
En este ejemplo se utiliza el método ToDictionary para evaluar de forma inmediata una secuencia y una expresión
de clave relacionada en un diccionario.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var scoreRecordsDict = products.AsEnumerable().


ToDictionary(record => record.Field<string>("Name"));
Console.WriteLine("Top Tube's ProductID: {0}",
scoreRecordsDict["Top Tube"]["ProductID"]);
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim scoreRecordsDict = products.AsEnumerable(). _


ToDictionary(Function(record) record.Field(Of String)("Name"))

Console.WriteLine("Top Tube's ProductID: {0}", _


scoreRecordsDict("Top Tube")("ProductID"))

ToList
Ejemplo
En este ejemplo se utiliza el método ToList para evaluar inmediatamente una secuencia en List<T>, donde T es de
tipo DataRow.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> productList = products.AsEnumerable().ToList();

IEnumerable<DataRow> query =
from product in productList
orderby product.Field<string>("Name")
select product;

Console.WriteLine("The product list, ordered by product name:");


foreach (DataRow product in query)
{
Console.WriteLine(product.Field<string>("Name").ToLower(CultureInfo.InvariantCulture));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim productList = products.AsEnumerable().ToList()

Dim query = _
From product In productList _
Select product _
Order By product.Field(Of String)("Name")

Console.WriteLine("The sorted name list:")

For Each product In query


Console.WriteLine(product.Field(Of String)("Name").ToLower(CultureInfo.InvariantCulture))
Next
Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
operadores de elementos (LINQ to DataSet)
21/03/2020 • 2 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos First y ElementAt para obtener elementos DataRow
de un DataSet usando la sintaxis de expresiones de consulta.
El FillDataSet método utilizado en estos ejemplos se especifica en Carga de datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
Los ejemplos de este using / Imports tema utilizan las siguientes instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, vea Cómo: crear un proyecto LINQ to DataSet en Visual Studio.

ElementAt
Ejemplo
Este ejemplo usa el método ElementAt para recuperar la quinta dirección donde PostalCode == "M4B 1V7".
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable addresses = ds.Tables["Address"];

var fifthAddress = (
from address in addresses.AsEnumerable()
where address.Field<string>("PostalCode") == "M4B 1V7"
select address.Field<string>("AddressLine1"))
.ElementAt(5);

Console.WriteLine("Fifth address where PostalCode = 'M4B 1V7': {0}",


fifthAddress);

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim addresses As DataTable = ds.Tables("Address")

Dim fifthAddress = ( _
From address In addresses.AsEnumerable() _
Where address.Field(Of String)("PostalCode") = "M4B 1V7" _
Select address.Field(Of String)("AddressLine1")).ElementAt(5)

Console.WriteLine("Fifth address where PostalCode = 'M4B 1V7': " & _


fifthAddress)

Primero
Ejemplo
Este ejemplo usar el método First para devolver el primer contacto cuyo nombre es 'Brooke'.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

DataRow query = (
from contact in contacts.AsEnumerable()
where (string)contact["FirstName"] == "Brooke"
select contact)
.First();

Console.WriteLine("ContactID: " + query.Field<int>("ContactID"));


Console.WriteLine("FirstName: " + query.Field<string>("FirstName"));
Console.WriteLine("LastName: " + query.Field<string>("LastName"));
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query = ( _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Brooke" _
Select contact).First()

Console.WriteLine("ContactID: " & query.Field(Of Integer)("ContactID"))


Console.WriteLine("FirstName: " & query.Field(Of String)("FirstName"))
Console.WriteLine("LastName: " & query.Field(Of String)("LastName"))

Consulte también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Información general sobre operadores de consulta estándar (C#)
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
Operadores de agregado (LINQ to DataSet)
23/10/2019 • 15 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo utilizar los operadores Aggregate, Average, Count, LongCount, Max,
Min y Sum para consultar DataSet y agregar datos utilizando sintaxis de consulta basada en métodos.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Agregar
Ejemplo
En este ejemplo se utiliza el método Aggregate para obtener los cinco primeros contactos de la tabla Contact y
compilar una lista de apellidos delimitada por comas.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

IEnumerable<DataRow> contacts = ds.Tables["Contact"].AsEnumerable();

string nameList =
contacts.Take(5).Select(contact => contact.Field<string>("LastName")).Aggregate((workingList, next) =>
workingList + "," + next);

Console.WriteLine(nameList);
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As IEnumerable(Of DataRow) = _


ds.Tables("Contact").AsEnumerable()

Dim nameList As String = _


contacts.Take(5).Select(Function(contact) contact.Field(Of String)("LastName")). _
Aggregate(Function(workingList, next1) workingList + "," + next1)

Console.WriteLine(nameList)

Average
Ejemplo
En este ejemplo se utiliza el método Average para encontrar el precio de venta promedio de los productos.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var products = ds.Tables["Product"].AsEnumerable();

Decimal averageListPrice =
products.Average(product => product.Field<Decimal>("ListPrice"));

Console.WriteLine("The average list price of all the products is ${0}",


averageListPrice);

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As IEnumerable(Of DataRow) = _


ds.Tables("Product").AsEnumerable()

Dim averageListPrice As Decimal = _


products.Average(Function(product) product. _
Field(Of Decimal)("ListPrice"))

Console.WriteLine("The average list price of all the products is $" & _


averageListPrice)

Ejemplo
En este ejemplo se utiliza el método Average para encontrar el precio de venta promedio de cada estilo de
productos.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var products = ds.Tables["Product"].AsEnumerable();

var query = from product in products


group product by product.Field<string>("Style") into g
select new
{
Style = g.Key,
AverageListPrice =
g.Average(product => product.Field<Decimal>("ListPrice"))
};

foreach (var product in query)


{
Console.WriteLine("Product style: {0} Average list price: {1}",
product.Style, product.AverageListPrice);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As IEnumerable(Of DataRow) = _


ds.Tables("Product").AsEnumerable()

Dim query = _
From product In products _
Group product By style = product.Field(Of String)("Style") Into g = Group _
Select New With _
{ _
.Style = style, _
.AverageListPrice = g.Average(Function(product) _
product.Field(Of Decimal)("ListPrice")) _
}

For Each product In query


Console.WriteLine("Product style: {0} Average list price: {1}", _
product.Style, product.AverageListPrice)
Next

Ejemplo
En este ejemplo se utiliza el método Average para encontrar el importe total a pagar promedio.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

Decimal averageTotalDue = orders.AsEnumerable().


Average(order => order.Field<decimal>("TotalDue"));
Console.WriteLine("The average TotalDue is {0}.",
averageTotalDue);
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim averageTotalDue As Decimal = orders.AsEnumerable(). _


Average(Function(order) order.Field(Of Decimal)("TotalDue"))
Console.WriteLine("The average TotalDue is {0}.", _
averageTotalDue)

Ejemplo
En este ejemplo se utiliza el método Average para obtener el importe total a pagar promedio de cada id. de
contacto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
averageTotalDue =
g.Average(order => order.Field<decimal>("TotalDue"))
};

foreach (var order in query)


{
Console.WriteLine("ContactID = {0} \t Average TotalDue = {1}",
order.Category,
order.averageTotalDue);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.averageTotalDue = g.Average(Function(order) order. _
Field(Of Decimal)("TotalDue")) _
}

For Each order In query


Console.WriteLine("ContactID = {0} " & vbTab & _
" Average TotalDue = {1}", order.Category, _
order.averageTotalDue)
Next

Ejemplo
En este ejemplo se utiliza el método Average para obtener los pedidos con el TotalDue promedio de cada
contacto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
let averageTotalDue = g.Average(order => order.Field<decimal>("TotalDue"))
select new
{
Category = g.Key,
CheapestProducts =
g.Where(order => order.Field<decimal>("TotalDue") ==
averageTotalDue)
};

foreach (var orderGroup in query)


{
Console.WriteLine("ContactID: {0}", orderGroup.Category);
foreach (var order in orderGroup.CheapestProducts)
{
Console.WriteLine("Average total due for SalesOrderID {1} is: {0}",
order.Field<decimal>("TotalDue"),
order.Field<Int32>("SalesOrderID"));
}
Console.WriteLine("");
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Let averageTotalDue = g.Average(Function(order) order.Field(Of Decimal)("TotalDue")) _
Select New With _
{ _
.Category = contactID, _
.CheapestProducts = g.Where(Function(order) order. _
Field(Of Decimal)("TotalDue") = averageTotalDue) _
}

For Each orderGroup In query


Console.WriteLine("ContactID: " & orderGroup.Category)
For Each order In orderGroup.CheapestProducts
Console.WriteLine("Average total due for SalesOrderID {1} is: {0}", _
order.Field(Of Decimal)("TotalDue"), _
order.Field(Of Int32)("SalesOrderID"))
Next
Console.WriteLine("")
Next

Número
Ejemplo
En este ejemplo se utiliza el método Count para devolver el número de productos de la tabla Product .

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

var products = ds.Tables["Product"].AsEnumerable();

int numProducts = products.Count();

Console.WriteLine("There are {0} products.", numProducts);

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim numProducts = products.AsEnumerable().Count()

Console.WriteLine("There are " & numProducts & " products.")

Ejemplo
En este ejemplo se utiliza el método Count para devolver una lista de id. de contactos y el número de pedidos que
tiene cada uno de ellos.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

var query = from contact in contacts.AsEnumerable()


select new
{
CustomerID = contact.Field<int>("ContactID"),
OrderCount =
contact.GetChildRows("SalesOrderContact").Count()
};

foreach (var contact in query)


{
Console.WriteLine("CustomerID = {0} \t OrderCount = {1}",
contact.CustomerID,
contact.OrderCount);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim query = _
From contact In contacts.AsEnumerable() _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.OrderCount = contact.GetChildRows("SalesOrderContact").Count() _
}

For Each contact In query


Console.Write("CustomerID = " & contact.ContactID)
Console.WriteLine(vbTab & "OrderCount = " & contact.OrderCount)
Next

Ejemplo
En este ejemplo se agrupan los productos por colores y se utiliza el método Count para devolver el número de
productos de cada grupo de color.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query =
from product in products.AsEnumerable()
group product by product.Field<string>("Color") into g
select new { Color = g.Key, ProductCount = g.Count() };

foreach (var product in query)


{
Console.WriteLine("Color = {0} \t ProductCount = {1}",
product.Color,
product.ProductCount);
}

Dim ds As New DataSet()


ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
From product In products.AsEnumerable() _
Group product By color = product.Field(Of String)("Color") Into g = Group _
Select New With {.Color = color, .ProductCount = g.Count()}

For Each product In query


Console.WriteLine("Color = {0} " & vbTab & "ProductCount = {1}", _
product.Color, _
product.ProductCount)
Next

LongCount
Ejemplo
En este ejemplo se obtiene el recuento de contactos como un entero largo.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];

long numberOfContacts = contacts.AsEnumerable().LongCount();


Console.WriteLine("There are {0} Contacts", numberOfContacts);
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")

Dim numberOfContacts = contacts.AsEnumerable().LongCount()

Console.WriteLine("There are {0} Contacts", numberOfContacts)

Máx.
Ejemplo
En este ejemplo se utiliza el método Max para obtener el mayor importe total a pagar.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

Decimal maxTotalDue = orders.AsEnumerable().


Max(w => w.Field<decimal>("TotalDue"));
Console.WriteLine("The maximum TotalDue is {0}.",
maxTotalDue);

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim maxTotalDue As Decimal = orders.AsEnumerable(). _


Max(Function(w) w.Field(Of Decimal)("TotalDue"))
Console.WriteLine("The maximum TotalDue is {0}.", maxTotalDue)

Ejemplo
En este ejemplo se utiliza el método Max para obtener el mayor importe total a pagar de cada id. de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
maxTotalDue =
g.Max(order => order.Field<decimal>("TotalDue"))
};

foreach (var order in query)


{
Console.WriteLine("ContactID = {0} \t Maximum TotalDue = {1}",
order.Category, order.maxTotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.maxTotalDue = _
g.Max(Function(order) order.Field(Of Decimal)("TotalDue")) _
}
For Each order In query
Console.WriteLine("ContactID = {0} " & vbTab & _
" Maximum TotalDue = {1}", _
order.Category, order.maxTotalDue)
Next

Ejemplo
En este ejemplo se utiliza el método Max para obtener los pedidos con el TotalDue mayor de cada id. contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
let maxTotalDue = g.Max(order => order.Field<decimal>("TotalDue"))
select new
{
Category = g.Key,
CheapestProducts =
g.Where(order => order.Field<decimal>("TotalDue") ==
maxTotalDue)
};

foreach (var orderGroup in query)


{
Console.WriteLine("ContactID: {0}", orderGroup.Category);
foreach (var order in orderGroup.CheapestProducts)
{
Console.WriteLine("MaxTotalDue {0} for SalesOrderID {1}: ",
order.Field<decimal>("TotalDue"),
order.Field<Int32>("SalesOrderID"));
}
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Let maxTotalDue = g.Max(Function(order) order.Field(Of Decimal)("TotalDue")) _
Select New With _
{ _
.Category = contactID, _
.CheapestProducts = _
g.Where(Function(order) order. _
Field(Of Decimal)("TotalDue") = maxTotalDue) _
}

For Each orderGroup In query


Console.WriteLine("ContactID: " & orderGroup.Category)
For Each order In orderGroup.CheapestProducts
Console.WriteLine("MaxTotalDue {0} for SalesOrderID {1} ", _
order.Field(Of Decimal)("TotalDue"), _
order.Field(Of Int32)("SalesOrderID"))
Next
Next

Mín.
Ejemplo
En este ejemplo se utiliza el método Min para obtener el menor importe total a pagar.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

Decimal smallestTotalDue = orders.AsEnumerable().


Min(totalDue => totalDue.Field<decimal>("TotalDue"));
Console.WriteLine("The smallest TotalDue is {0}.",
smallestTotalDue);

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim smallestTotalDue As Decimal = orders.AsEnumerable(). _


Min(Function(totalDue) totalDue.Field(Of Decimal)("TotalDue"))

Console.WriteLine("The smallest TotalDue is {0}.", smallestTotalDue)

Ejemplo
En este ejemplo se utiliza el método Min para obtener el menor importe total a pagar de cada id. de contacto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
smallestTotalDue =
g.Min(order => order.Field<decimal>("TotalDue"))
};

foreach (var order in query)


{
Console.WriteLine("ContactID = {0} \t Minimum TotalDue = {1}",
order.Category, order.smallestTotalDue);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.smallestTotalDue = g.Min(Function(order) _
order.Field(Of Decimal)("TotalDue")) _
}

For Each order In query


Console.WriteLine("ContactID = {0} " & vbTab & _
"Minimum TotalDue = {1}", order.Category, order.smallestTotalDue)
Next

Ejemplo
En este ejemplo se utiliza el método Min para obtener el pedido con el menor importe total a pagar de cada
contacto.

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
let minTotalDue = g.Min(order => order.Field<decimal>("TotalDue"))
select new
{
Category = g.Key,
smallestTotalDue =
g.Where(order => order.Field<decimal>("TotalDue") ==
minTotalDue)
};

foreach (var orderGroup in query)


{
Console.WriteLine("ContactID: {0}", orderGroup.Category);
foreach (var order in orderGroup.smallestTotalDue)
{
Console.WriteLine("Mininum TotalDue {0} for SalesOrderID {1}: ",
order.Field<decimal>("TotalDue"),
order.Field<Int32>("SalesOrderID"));
}
Console.WriteLine("");
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Let minTotalDue = g.Min(Function(order) order.Field(Of Decimal)("TotalDue")) _
Select New With _
{ _
.Category = contactID, _
.smallestTotalDue = g.Where(Function(order) order. _
Field(Of Decimal)("TotalDue") = minTotalDue) _
}

For Each orderGroup In query


Console.WriteLine("ContactID: " & orderGroup.Category)
For Each order In orderGroup.smallestTotalDue
Console.WriteLine("Mininum TotalDue {0} for SalesOrderID {1} ", _
order.Field(Of Decimal)("TotalDue"), _
order.Field(Of Int32)("SalesOrderID"))
Next
Console.WriteLine("")
Next

Sum
Ejemplo
En este ejemplo se utiliza el método Sum para obtener el número total de cantidades de pedido de la tabla
SalesOrderDetail .

// Fill the DataSet.


DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderDetail"];

double totalOrderQty = orders.AsEnumerable().


Sum(o => o.Field<Int16>("OrderQty"));
Console.WriteLine("There are a total of {0} OrderQty.",
totalOrderQty);

Ejemplo
En este ejemplo se utiliza el método Sum para obtener el importe total a pagar de cada id. de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
from order in orders.AsEnumerable()
group order by order.Field<Int32>("ContactID") into g
select new
{
Category = g.Key,
TotalDue = g.Sum(order => order.Field<decimal>("TotalDue")),
};
foreach (var order in query)
{
Console.WriteLine("ContactID = {0} \t TotalDue sum = {1}",
order.Category, order.TotalDue);
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
From order In orders.AsEnumerable() _
Group order By contactID = order.Field(Of Int32)("ContactID") Into g = Group _
Select New With _
{ _
.Category = contactID, _
.TotalDue = g.Sum(Function(order) order. _
Field(Of Decimal)("TotalDue")) _
}

For Each order In query


Console.WriteLine("ContactID = {0} " & vbTab & "TotalDue sum = {1}", _
order.Category, order.TotalDue)
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de sintaxis de consulta basada en métodos:
Join (LINQ to DataSet)
23/10/2019 • 4 minutes to read • Edit Online

La combinación es una operación importante de las consultas dirigidas a orígenes de datos que no tienen
relaciones navegables entre ellos, como las tablas de bases de datos relacionales. Una combinación de dos
orígenes de datos es la asociación de objetos en un origen de datos con objetos que comparten un atributo común
en el otro origen de datos. Para obtener más información, vea información general sobre operadoresC#de consulta
estándar () o información general sobre operadores de consulta estándar (Visual Basic).
Los ejemplos de este tema muestran cómo usar el métodoJoin para consultar DataSet usando la sintaxis de
consulta basada en métodos.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

Unir
Ejemplo
En este ejemplo se realiza una combinación en las tablas Contact y SalesOrderHeader .
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
contacts.AsEnumerable().Join(orders.AsEnumerable(),
order => order.Field<Int32>("ContactID"),
contact => contact.Field<Int32>("ContactID"),
(contact, order) => new
{
ContactID = contact.Field<Int32>("ContactID"),
SalesOrderID = order.Field<Int32>("SalesOrderID"),
FirstName = contact.Field<string>("FirstName"),
Lastname = contact.Field<string>("Lastname"),
TotalDue = order.Field<decimal>("TotalDue")
});

foreach (var contact_order in query)


{
Console.WriteLine("ContactID: {0} "
+ "SalesOrderID: {1} "
+ "FirstName: {2} "
+ "Lastname: {3} "
+ "TotalDue: {4}",
contact_order.ContactID,
contact_order.SalesOrderID,
contact_order.FirstName,
contact_order.Lastname,
contact_order.TotalDue);
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
contacts.AsEnumerable().Join(orders.AsEnumerable(), _
Function(order) order.Field(Of Int32)("ContactID"), _
Function(contact) contact.Field(Of Int32)("ContactID"), _
Function(contact, order) New With _
{ _
.ContactID = contact.Field(Of Int32)("ContactID"), _
.SalesOrderID = order.Field(Of Int32)("SalesOrderID"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.Lastname = contact.Field(Of String)("Lastname"), _
.TotalDue = order.Field(Of Decimal)("TotalDue") _
})

For Each contact_order In query


Console.WriteLine("ContactID: {0} " _
& "SalesOrderID: {1} " _
& "FirstName: {2} " _
& "Lastname: {3} " _
& "TotalDue: {4}", _
contact_order.ContactID, _
contact_order.SalesOrderID, _
contact_order.FirstName, _
contact_order.Lastname, _
contact_order.TotalDue)
Next

Ejemplo
En este ejemplo se realiza una combinación en las tablas Contact y SalesOrderHeader , agrupando los resultados
por id. de contacto.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts = ds.Tables["Contact"];


DataTable orders = ds.Tables["SalesOrderHeader"];

var query = contacts.AsEnumerable().Join(orders.AsEnumerable(),


order => order.Field<Int32>("ContactID"),
contact => contact.Field<Int32>("ContactID"),
(contact, order) => new
{
ContactID = contact.Field<Int32>("ContactID"),
SalesOrderID = order.Field<Int32>("SalesOrderID"),
FirstName = contact.Field<string>("FirstName"),
Lastname = contact.Field<string>("Lastname"),
TotalDue = order.Field<decimal>("TotalDue")
})
.GroupBy(record => record.ContactID);

foreach (var group in query)


{
foreach (var contact_order in group)
{
Console.WriteLine("ContactID: {0} "
+ "SalesOrderID: {1} "
+ "FirstName: {2} "
+ "Lastname: {3} "
+ "TotalDue: {4}",
contact_order.ContactID,
contact_order.SalesOrderID,
contact_order.FirstName,
contact_order.Lastname,
contact_order.TotalDue);
}
}
' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contacts As DataTable = ds.Tables("Contact")


Dim orders As DataTable = ds.Tables("SalesOrderHeader")

Dim query = _
contacts.AsEnumerable().Join(orders.AsEnumerable(), _
Function(order) order.Field(Of Int32)("ContactID"), _
Function(contact) contact.Field(Of Int32)("ContactID"), _
Function(contact, order) New With _
{ _
.ContactID = contact.Field(Of Int32)("ContactID"), _
.SalesOrderID = order.Field(Of Int32)("SalesOrderID"), _
.FirstName = contact.Field(Of String)("FirstName"), _
.Lastname = contact.Field(Of String)("Lastname"), _
.TotalDue = order.Field(Of Decimal)("TotalDue") _
}) _
.GroupBy(Function(record) record.ContactID)

For Each group In query


For Each contact_order In group
Console.WriteLine("ContactID: {0} " _
& "SalesOrderID: {1} " _
& "FirstName: {2} " _
& "Lastname: {3} " _
& "TotalDue: {4}", _
contact_order.ContactID, _
contact_order.SalesOrderID, _
contact_order.FirstName, _
contact_order.Lastname, _
contact_order.TotalDue)
Next
Next

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Standard Query Operators Overview (C#) (Información general sobre operadores de consulta estándar (C#))
Información general sobre operadores de consulta estándar (Visual Basic)
Ejemplos de combinación
Ejemplos de conjuntos de DataSet
Ejemplos de operadores específicos de conjuntos de
datos (LINQ to DataSet)
23/10/2019 • 2 minutes to read • Edit Online

Los ejemplos de este tema muestran cómo usar los métodos CopyToDataTable y la clase DataRowComparer.
El FillDataSet método utilizado en estos ejemplos se especifica en cargar datos en un conjunto de datos.
Los ejemplos de este tema utilizan las tablas Contact, Address, Product, SalesOrderHeader y SalesOrderDetail en la
base de datos de ejemplo de AdventureWorks.
En los ejemplos de este tema se usan using las siguientes / Imports instrucciones:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Globalization;

Option Explicit On

Imports System.Linq
Imports System.Linq.Expressions
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Common
Imports System.Globalization

Para obtener más información, consulte Cómo Cree un proyecto de LINQ to DataSet en VisualStudio.

CopyToDataTable
Ejemplo
En este ejemplo se carga un DataTable con resultados de consulta utilizando el método CopyToDataTable.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable contacts1 = ds.Tables["Contact"];

IEnumerable<DataRow> query =
from contact in contacts1.AsEnumerable()
where contact.Field<string>("Title") == "Ms."
&& contact.Field<string>("FirstName") == "Carla"
select contact;

DataTable contacts2 = query.CopyToDataTable();

foreach (DataRow contact in contacts2.AsEnumerable())


{
Console.WriteLine("ID:{0} Name: {1}, {2}",
contact.Field<Int32>("ContactID"),
contact.Field<string>("LastName"),
contact.Field<string>("FirstName"));
}

' Fill the DataSet.


Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim contactTable As DataTable = ds.Tables("Contact")

Dim query = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("Title") = "Ms." _
And contact.Field(Of String)("FirstName") = "Carla" _
Select contact

Dim contacts = query.CopyToDataTable().AsEnumerable()

For Each contact In contacts


Console.Write("ID: " & contact.Field(Of Integer)("ContactID"))
Console.WriteLine(" Name: " & contact.Field(Of String)("LastName") & _
", " & contact.Field(Of String)("FirstName"))
Next

DataRowComparer
Ejemplo
En este ejemplo se comparan dos filas distintas de datos utilizando DataRowComparer.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

// Get two rows from the SalesOrderHeader table.


DataTable table = ds.Tables["SalesOrderHeader"];
DataRow left = (DataRow)table.Rows[0];
DataRow right = (DataRow)table.Rows[1];

// Compare the two different rows.


IEqualityComparer<DataRow> comparer = DataRowComparer.Default;

bool bEqual = comparer.Equals(left, right);


if (bEqual)
Console.WriteLine("The two rows are equal");
else
Console.WriteLine("The two rows are not equal");

// Get the hash codes of the two rows.


Console.WriteLine("The hashcodes for the two rows are {0}, {1}",
comparer.GetHashCode(left),
comparer.GetHashCode(right));

Vea también
Carga de datos en un conjunto de datos
Ejemplos de LINQ to DataSet
Entity Data Model
08/11/2019 • 5 minutes to read • Edit Online

Entity Data Model (EDM) es un conjunto de conceptos que describen la estructura de los datos,
independientemente del formato en el que estén almacenados. EDM se basa en el modelo entidad-relación
(Entity-Relationship Model) descrito por Peter Chen en 1976, pero también incorpora nuevas funciones y amplía
sus usos tradicionales.
EDM soluciona los desafíos que plantea el tener datos almacenados en muchos formatos. Considere, por
ejemplo, un negocio que almacena los datos en bases de datos relacionales, archivos de texto, archivos XML,
hojas de cálculo e informes. Esto presenta importantes desafíos en el modelado de datos, el diseño de
aplicaciones y el acceso a los datos. Al diseñar una aplicación orientada a datos, el desafío consiste en escribir un
código eficaz y que se pueda mantener sin sacrificar la eficacia del acceso a los datos, el almacenamiento y la
escalabilidad. Cuando los datos tienen una estructura relacional, el acceso a los datos, el almacenamiento y la
escalabilidad resultan muy eficaces, pero es más difícil escribir un código eficaz y que se pueda mantener.
Cuando los datos tienen una estructura de objeto, se invierten las ventajas; es decir, la escritura de un código
eficaz y que se pueda mantener se consigue a costa de la eficacia en el acceso a los datos, el almacenamiento y la
escalabilidad. Aunque es posible encontrar el equilibrio adecuado entre ambos métodos, surgen nuevos desafíos
cuando se mueven los datos de un formato a otro. Entity Data Model resuelve estos desafíos describiendo la
estructura de los datos en forma de entidades y relaciones que son independientes de cualquier esquema de
almacenamiento. Esto hace que el formato en el que están almacenados los datos sea irrelevante a la hora de
diseñar y desarrollar las aplicaciones. Y, dado que las entidades y las relaciones describen la estructura de los
datos tal como se usan en una aplicación (no el formato en el que están almacenados), pueden evolucionar al
mismo tiempo que la aplicación.
Un modelo conceptual ( conceptual model ) es una representación específica de la estructura de los datos en
forma de entidades y relaciones, y normalmente se define mediante un lenguaje específico de dominio (DSL)
que implementa los conceptos de EDM. El lenguaje de definición de esquemas conceptuales (CSDL) es un
ejemplo de este tipo de lenguaje específico de dominio. Las entidades y relaciones descritas en un modelo
conceptual se pueden considerar como abstracciones de objetos y asociaciones en una aplicación. Esto permite a
los desarrolladores centrarse en el modelo conceptual sin tener que preocuparse por el esquema de
almacenamiento, así como escribir el código teniendo en cuenta la eficacia y el mantenimiento. Mientras tanto,
los diseñadores del esquema de almacenamiento pueden centrarse en la eficacia en el acceso a los datos, el
almacenamiento y la escalabilidad.

En esta sección
Los temas de esta sección describen los conceptos de Entity Data Model. Cualquier ADSL que implemente EDM
debe incluir los conceptos descritos a continuación. Tenga en cuenta que el Entity Framework ADO.net utiliza
CSDL para definir los modelos conceptuales. Para obtener más información, consulta CSDL Specification.
Conceptos clave de Entity Data Model
Entity Data Model: Espacios de nombres
Entity Data Model: Tipos de datos primitivos
Entity Data Model: Herencia
extremo de asociación
multiplicidad de extremo de asociación
conjunto de asociaciones
extremo del conjunto de asociaciones
tipo de asociación
tipo complejo
contenedor de entidades
clave de entidad
conjunto de entidades
tipo de entidad
facet
propiedad de clave externa
función declarada por el modelo
función definida por el modelo
propiedad de navegación
propiedad
restricción de integridad referencial

Vea también
ADO.NET Entity Data Model herramientas
Información general sobre el archivo. edmx
Especificación CSDL
Conceptos clave de Entity Data Model
08/11/2019 • 7 minutes to read • Edit Online

El Entity Data Model (EDM) usa tres conceptos clave para describir la estructura de los datos: el tipo de entidad,
el tipo de asociacióny la propiedad. Estos son los conceptos más importantes para describir la estructura de
datos en cualquier implementación de EDM.

Tipo de entidad
El tipo de entidad es el bloque de creación fundamental para describir la estructura de los datos con el Entity
Data Model. En un modelo conceptual, los tipos de entidad se construyen a partir de propiedades y describen la
estructura de conceptos de nivel superior, como clientes y pedidos en una aplicación empresarial. Del mismo
modo que una definición de clase en un programa es una plantilla para las instancias de la clase, un tipo de
entidad es una plantilla para las entidades. Una entidad representa un objeto concreto (como un cliente o pedido
concreto). Cada entidad debe tener una clave de entidad única dentro de un conjunto de entidades. Un conjunto
de entidades es una colección de instancias de un tipo de entidad concreto. Los conjuntos de entidades (y los
conjuntos de asociaciones) se agrupan de forma lógica en un contenedor de entidades.
Los tipos de entidad admiten la herencia: es decir, un tipo de entidad se puede derivar de otro. Para obtener más
información, vea Entity Data Model: herencia.

Tipo de asociación
Un tipo de asociación (también denominado Asociación) es el bloque de creación fundamental para describir las
relaciones en el Entity Data Model. En un modelo conceptual, una asociación representa una relación entre dos
tipos de entidad (como Customer y Order). Cada asociación tiene dos extremos de asociación que especifican los
tipos de entidad implicados en la asociación. Cada extremo de la asociación también especifica una multiplicidad
de extremo de asociación que indica el número de entidades que pueden estar en ese extremo de la asociación.
Una multiplicidad de extremo de asociación puede tener un valor de uno (1), cero o uno (0.. 1) o varios (*). Se
puede tener acceso a las entidades de un extremo de una asociación a través de las propiedades de navegacióno
a través de claves externas si se exponen en un tipo de entidad. Para obtener más información, consulte Foreign
Key Property.
En una aplicación, una instancia de una asociación representa una asociación concreta (como por ejemplo una
asociación entre una instancia Customer y una instancia Order). Las instancias de asociación se agrupan
lógicamente en un conjunto de asociaciones. Los conjuntos de asociaciones (y los conjuntos de entidades) se
agrupan de forma lógica en un contenedor de entidades.

Propiedad.
Los tipos de entidad contienen propiedades que definen su estructura y sus características. Por ejemplo, un tipo
de entidad Customer puede tener propiedades como CustomerId, Name y Address.
Las propiedades en un modelo conceptual son análogas a las propiedades definidas en una clase en un
programa. Del mismo modo que las propiedades en una clase definen la forma de la clase y proporcionan
información sobre los objetos, las propiedades en un modelo conceptual definen la forma de un tipo de entidad
y proporcionan información sobre las instancias del tipo de entidad.
Una propiedad puede contener datos primitivos (como una cadena, un entero o un valor booleano) o estructura
los datos (como un tipo complejo). Para obtener más información, vea Entity Data Model: tipos de datos
primitivos.
Representaciones de un modelo conceptual
Un modelo conceptual es una representación específica de la estructura de algunos datos como entidades y
relaciones. Una manera de representar un modelo conceptual es con un diagrama. El siguiente diagrama
representa un modelo conceptual con tres tipos de entidad ( Book , Publisher y Author ) y dos asociaciones (
PublishedBy y WrittenBy ):

Esta representación, sin embargo, tiene algunas limitaciones a la hora de representar algunos detalles acerca del
modelo. Por ejemplo, la información sobre el tipo de propiedad y el conjunto de entidades no se representa en el
diagrama. La riqueza de un modelo conceptual se puede representar con mayor claridad mediante un lenguaje
específico de dominio (ADSL). El Entity Framework ADO.net usa un DSL basado en XML denominado lenguaje de
definición de esquemas conceptuales (CSDL) para definir los modelos conceptuales. A continuación se muestra
la definición CSDL del modelo conceptual del diagrama anterior:
<Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
Namespace="BooksModel" Alias="Self">
<EntityContainer Name="BooksContainer" >
<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>
<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="String" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="BooksModel.PublishedBy"
FromRole="Publisher" ToRole="Book" />
</EntityType>
<EntityType Name="Author">
<Key>
<PropertyRef Name="Name" />
<PropertyRef Name="Address" />
</Key>
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="String" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="BooksModel.WrittenBy"
FromRole="Author" ToRole="Book" />
</EntityType>
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
</Association>
<Association Name="WrittenBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Author" Role="Author" Multiplicity="*" />
</Association>
</Schema>

Vea también
Entity Data Model
Entity Data Model: Espacios de nombres
08/11/2019 • 2 minutes to read • Edit Online

Un espacio de nombres en el Entity Data Model (EDM) es un contenedor abstracto para tipos de entidad, tipos
complejosy asociaciones. Los espacios de nombres en EDM son similares a los de un lenguaje de programación:
proporcionan el contexto para los objetos que contienen, así como una manera de eliminar la ambigüedad de
objetos que tienen el mismo nombre (pero que contienen espacios de nombres diferentes).

Ejemplo
El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. En el siguiente código CSDL se utiliza un
espacio de nombres para identificar un tipo que se define en un modelo conceptual diferente. En el ejemplo se
define un tipo de entidad ( Publisher ) con una propiedad de tipo complejo ( Address ) que se importa del espacio
de nombres ExtendedBooksModel . Observe que el elemento Using indica que se ha importado un espacio de
nombres. Observe también que el tipo de la propiedad Address se define utilizando su nombre completo (
ExtendedBooksModel.Address ), lo que indica que este tipo se define en el espacio de nombres ExtendedBooksModel .

<Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
Namespace="BooksModel" Alias="Self">

<Using Namespace="BooksModel.Extended" Alias="BMExt" />

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
</EntityContainer>

<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="BMExt.Address" Name="Address" Nullable="false" />
</EntityType>

</Schema>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
Entity Data Model: tipos de datos primitivos
23/10/2019 • 3 minutes to read • Edit Online

El Entity Data Model (EDM) admite un conjunto de tipos de datos primitivos abstractos (como String, Boolean,
Int32, etc.) que se usan para definir propiedades en un modelo conceptual. Estos tipos de datos primitivos son
representantes de los tipos de datos primitivos reales compatibles con el entorno de almacenamiento o de
hospedaje, como una base de datos de SQL Server o Common Language Runtime (CLR). EDM no define la
semántica de las operaciones o conversiones sobre los tipos de datos primitivos; es el propio entorno de
almacenamiento o de hospedaje el que lo hace. Normalmente, los tipos de datos primitivos de EDM se asignan a
los tipos de datos primitivos correspondientes del entorno de almacenamiento o de hospedaje. Para obtener
información sobre cómo asigna el Entity Framework tipos primitivos en el EDM a SQL Server tipos de datos, vea
SqlClient para Entity Framework.

NOTE
EDM no admite colecciones de tipos de datos primitivos.

Para obtener información sobre los tipos de datos estructurados en el EDM, vea tipo de entidad y tipo complejo.

Tipos de datos primitivos admitidos en Entity Data Model


En la tabla siguiente se enumeran los tipos de datos primitivos admitidos por EDM. En la tabla también se
enumeran las caras que se pueden aplicar a cada tipo de datos primitivo.

T IP O DE DATO S P RIM IT IVO DESC RIP C IÓ N FA C ETA S A P L IC A B L ES

Binary Contiene datos binarios. MaxLength, FixedLength, Nullable,


Default

Boolean Contiene el valor true o false . Nullable, Default

Byte Contiene un valor entero de 8 bits sin Precision, Nullable, Default


signo.

DateTime Representa una fecha y hora. Precision, Nullable, Default

DateTimeOffset Contiene una fecha y hora como un Precision, Nullable, Default


desplazamiento en minutos con
respecto a GMT.

Decimal Contiene un valor numérico con una Precision, Nullable, Default


precisión y escala fijas.

Double Contiene un número de punto flotante Precision, Nullable, Default


con una precisión de 15 dígitos.

Float Contiene un número de punto flotante Precision, Nullable, Default


con una precisión de siete dígitos.
T IP O DE DATO S P RIM IT IVO DESC RIP C IÓ N FA C ETA S A P L IC A B L ES

Guid Contiene un identificador único de 16 Precision, Nullable, Default


bytes.

Int16 Contiene un valor entero de 16 bits Precision, Nullable, Default


con signo.

Int32 Contiene un valor entero de 32 bits Precision, Nullable, Default


con signo.

Int64 Contiene un valor entero de 64 bits Precision, Nullable, Default


con signo.

SByte Contiene un valor entero de 8 bits con Precision, Nullable, Default


signo.

string Contiene datos de caracteres. Unicode, FixedLength, MaxLength,


Collation, Precision, Nullable, Default

Hora Contiene una hora del día. Precision, Nullable, Default

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
Entity Data Model: Herencia
08/11/2019 • 3 minutes to read • Edit Online

El Entity Data Model (EDM) admite la herencia para los tipos de entidad. La herencia en EDM es similar a la
herencia para las clases en los lenguajes de programación orientados a objetos. Al igual que sucede con las clases
de los lenguajes orientados a objetos, en un modelo conceptual se puede definir un tipo de entidad (un tipo
derivado) que herede de otro tipo de entidad (el tipo base). Sin embargo, a diferencia de las clases de la
programación orientada a objetos, en un modelo conceptual, el tipo derivado hereda siempre todas las
propiedades y propiedades de navegación del tipo base. No se pueden reemplazar las propiedades heredadas en
un tipo derivado.
En un modelo conceptual, se pueden crear jerarquías de herencia en las que un tipo derivado hereda de otro tipo
derivado. El tipo situado en la parte superior de la jerarquía (el tipo de la jerarquía que no es un tipo derivado) se
denomina tipo raíz. En una jerarquía de herencia, la clave de entidad debe definirse en el tipo raíz.
No se pueden crear jerarquías de herencia en las que un tipo derivado hereda de más de un tipo. Por ejemplo, en
un modelo conceptual con un tipo de entidad Book , se podrían definir tipos derivados FictionBook y
NonFictionBook que hereden de Book . Sin embargo, no se podría definir un tipo que herede de los tipos
FictionBook y NonFictionBook .

Ejemplo
En el diagrama siguiente se muestra un modelo conceptual con cuatro tipos de entidad: Book , FictionBook ,
Publisher y Author . El tipo de entidad FictionBook es un tipo derivado, que hereda del tipo de entidad Book . El
tipo FictionBook hereda las propiedades ISBN (Key) , Title y Revision , y define una propiedad adicional
denominada Genre .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define un tipo de
entidad, FictionBook , que hereda del tipo Book (como en el diagrama anterior):

<EntityType Name="FictionBook" BaseType="BooksModel.Book" >


<Property Type="String" Name="Genre" Nullable="false" />
</EntityType>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
extremo de asociación
08/11/2019 • 3 minutes to read • Edit Online

Un extremo de asociación identifica el tipo de entidad en un extremo de una Asociación y el número de instancias
de tipo de entidad que pueden existir en ese extremo de una asociación. Los extremos de asociación se definen
como parte de una asociación, y esta debe tener exactamente dos extremos. Las propiedades de navegación
permiten la navegación de un extremo de asociación al otro.
Una definición de extremo de asociación contiene la información siguiente:
Uno de los tipos de entidad implicados en la asociación. (Necesario)

NOTE
En una asociación determinada, el tipo de entidad especificado para cada extremo de la asociación puede ser el
mismo. Esto crea una auto-asociación.

Una multiplicidad de extremo de asociación que indica el número de instancias de tipo de entidad que
pueden estar en un extremo de la asociación. Una multiplicidad de extremo de asociación puede tener un
valor de uno (1), cero o uno (0.. 1) o varios (*).
Nombre para el extremo de la asociación. (Opcional)
Información sobre las operaciones que se realizan en el extremo de la asociación, como por ejemplo la
eliminación en cascada. (Opcional)

Ejemplo
El diagrama siguiente muestra un modelo conceptual con dos asociaciones: PublishedBy y WrittenBy . Los
extremos de asociación para la asociación PublishedBy son los tipos de entidad Book y Publisher . La
multiplicidad del extremo de Publisher es uno (1) y la multiplicidad del extremo de Book es muchos (*), lo que
indica que un publicador publica muchos libros y un libro publicado por un publicador.

El Entity Framework ADO.NET usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El CSDL siguiente define la asociación
PublishedBy mostrada en el diagrama anterior. Tenga en cuenta que el tipo, el nombre y la multiplicidad de cada
extremo de la asociación se especifican mediante atributos XML (los atributos Type , Role y Multiplicity ,
respectivamente). La información adicional sobre las operaciones realizadas en un extremo se especifica
mediante un elemento XML (el elemento OnDelete -). En este caso, si se elimina un editor, también se eliminan
todos los libros asociados.
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
</Association>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
multiplicidad de extremo de asociación
08/11/2019 • 2 minutes to read • Edit Online

La multiplicidad de extremo de asociación define el número de instancias de tipo de entidad que pueden estar en
un extremo de una Asociación.
Una multiplicidad de extremo de asociación puede tener uno de los valores siguientes:
uno (1): indica que existe exactamente una instancia de tipo de entidad en el extremo de la asociación.
cero o uno (0..1): indica que pueden existir cero o una instancia de tipo de entidad en el extremo de la
asociación.
Many (*): indica que no existe ninguna instancia de tipo de entidad, o más, en el extremo de la asociación.
Normalmente, una asociación se caracteriza por sus multiplicidades de extremo de asociación. Por ejemplo, si los
extremos de una asociación tienen multiplicidades uno (1) y muchos (*), la asociación se denomina una
asociación uno a varios. En el ejemplo siguiente, la asociación PublishedBy es una asociación uno a varios (un
publicador publica muchos libros y un libro solo puede ser publicado por un publicador). La asociación
WrittenBy es una asociación varios a varios (un libro puede tener varios autores y un autor puede escribir varios
libros).

Ejemplo
El diagrama siguiente muestra un modelo conceptual con dos asociaciones: PublishedBy y WrittenBy . Los
extremos de asociación para la asociación PublishedBy son los tipos de entidad Book y Publisher . La
multiplicidad del extremo Publisher es uno (1) y la multiplicidad del extremo Book es muchos (*).

El Entity Framework ADO.NET usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define la
asociación PublishedBy mostrada en el diagrama anterior:

<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
</Association>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
conjunto de asociaciones
08/11/2019 • 4 minutes to read • Edit Online

Un conjunto de asociaciones es un contenedor lógico para las instancias de Asociación del mismo tipo. Un
conjunto de asociaciones no es una construcción del modelado de datos; es decir, no describe la estructura de
datos o relaciones. En su lugar, un conjunto de asociaciones proporciona una construcción para que un entorno
de hospedaje o de almacenamiento (como Common Language Runtime o una base de datos de SQL Server)
agrupe las instancias de asociaciones a fin de que se puedan asignar a un almacén de datos.
Un conjunto de asociaciones se define dentro de un contenedor de entidades, que es una agrupación lógica de
conjuntos de entidades y conjuntos de asociaciones.
Una definición de un conjunto de asociaciones contiene la información siguiente:
El nombre del conjunto de asociaciones. (Necesario)
La asociación cuyas instancias contendrá. (Necesario)
Dos extremos del conjunto de asociaciones.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con dos asociaciones: PublishedBy y WrittenBy . Aunque
la información sobre los conjuntos de asociaciones no se muestra en el diagrama, el diagrama siguiente muestra
un ejemplo de conjuntos de asociaciones y conjuntos de entidades basados en este modelo.

El siguiente ejemplo muestra un conjunto de asociaciones ( PublishedBy y dos conjuntos de entidades ( Books y
Publishers ) basados en el modelo conceptual mostrado anteriormente. BI en el conjunto de entidades Books
representa una instancia del tipo de entidad Book en tiempo de ejecución. De forma similar, PJ representa una
instancia de Publisher en el conjunto de entidades Publishers . BiPj representa una instancia de la Asociación
PublishedBy en el conjunto de asociaciones PublishedBy .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El siguiente CSDL define un contenedor
de entidad con un conjunto de asociaciones para cada asociación en el diagrama anterior. Observe que el
nombre y asociación para cada conjunto de asociaciones se definen utilizando los atributos XML.

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

Es posible definir varios conjuntos de asociaciones por asociación, siempre y cuando no haya dos conjuntos de
asociaciones que compartan un extremo del conjunto de asociaciones. En el siguiente CSDL se define un
contenedor de entidades con dos conjuntos de asociaciones para la asociación WrittenBy . Observe que se han
definido varios conjuntos de entidades para los tipos de entidad Book y Author y que ningún conjunto de
asociaciones comparte un extremo del conjunto de asociaciones.

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="FictionBooks" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<EntitySet Name="FictionAuthors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
<AssociationSet Name="FictionWrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="FictionBooks" />
<End Role="Author" EntitySet="FictionAuthors" />
</AssociationSet>
</EntityContainer>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
propiedad de clave externa
extremo del conjunto de asociaciones
08/11/2019 • 2 minutes to read • Edit Online

Un extremo del conjunto de asociaciones identifica el tipo de entidad y el conjunto de entidades al final de un
conjunto de asociaciones. Los extremos de conjuntos de asociaciones se definen como parte de un conjunto de
asociaciones, que debe tener exactamente dos extremos.
Una definición de extremo de conjunto de asociaciones contiene la información siguiente:
Uno de los tipos de entidad implicados en el conjunto de asociaciones. (Necesario)
El conjunto de entidades para el tipo de entidad implicado en el conjunto de asociaciones. (Necesario)

Ejemplo
El diagrama siguiente muestra un modelo conceptual con dos asociaciones: WrittenBy y PublishedBy .

El diagrama siguiente muestra un conjunto de asociaciones ( PublishedBy y dos conjuntos de entidades ( Books y
Publishers ) basados en el modelo conceptual mostrado anteriormente. Los extremos del conjunto de
asociaciones son los conjuntos de entidades Books y Publishers . BI en el conjunto de entidades Books
representa una instancia del tipo de entidad Book en tiempo de ejecución. De forma similar, PJ representa una
instancia de Publisher en el conjunto de entidades Publishers . BiPj representa una instancia de la Asociación
PublishedBy en el conjunto de asociaciones PublishedBy .

El Entity Framework ADO.net usa un DSL denominado lenguaje de definición de esquemas conceptuales (CSDL)
para definir los modelos conceptuales. El siguiente CSDL define un contenedor de entidad con un conjunto de
asociaciones para cada asociación en el diagrama anterior. Observe que los extremos del conjunto de asociaciones
se definen como parte de cada definición del conjunto de asociaciones.
<EntityContainer Name="BooksContainer" >
<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
tipo de asociación
08/11/2019 • 3 minutes to read • Edit Online

Un tipo de asociación (también denominado Asociación) es el bloque de creación fundamental para describir
las relaciones en el Entity Data Model (EDM). En un modelo conceptual, una asociación representa una relación
entre dos tipos de entidad (como Customer y Order ). En una aplicación, una instancia de una asociación
representa una asociación concreta (como por ejemplo una asociación entre una instancia de Customer y una
instancia de Order ). Las instancias de asociación se agrupan lógicamente en un conjunto de asociaciones.
Una definición de asociación contiene la siguiente información:
Un nombre único. (Necesario)
Dos extremosde la asociación, uno para cada tipo de entidad de la relación. (Necesario)

NOTE
Una asociación no puede representar una relación entre más de dos tipos de entidad. Sin embargo, una
asociación sí puede definir una auto-relación especificando el mismo tipo de entidad para cada uno de sus
extremos de asociación.

Restricción de integridad referencial. (Opcional)


Cada extremo de la asociación debe especificar una multiplicidad de extremo de asociación que indica el
número de instancias de tipo de entidad que pueden estar en un extremo de la asociación. Una multiplicidad de
extremo de asociación puede tener un valor de uno (1), cero o uno (0.. 1) o varios (*). Se puede tener acceso a
las instancias de tipo de entidad en un extremo de una asociación a través de las propiedades de navegación o
las claves externas si se exponen en un tipo de entidad. Para obtener más información, vea Entity Data Model:
claves externas.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con dos asociaciones: PublishedBy y WrittenBy . Los
extremos de asociación para la asociación PublishedBy son los tipos de entidad Book y Publisher . La
multiplicidad del extremo de Publisher es uno (1) y la multiplicidad del extremo de Book es muchos (*), lo
que indica que un publicador publica muchos libros y un libro publicado por un publicador.

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición
de esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define la
asociación PublishedBy mostrada en el diagrama anterior:
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
</Association>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
tipo complejo
08/11/2019 • 3 minutes to read • Edit Online

Un tipo complejo es una plantilla para definir propiedades enriquecidas y estructuradas en tipos de entidad o en
otros tipos complejos. Cada plantilla contiene lo siguiente:
Un nombre único. (Necesario)

NOTE
El nombre de un tipo complejo no puede coincidir con el nombre de un tipo de entidad dentro del mismo espacio
de nombres.

Datos en forma de una o más propiedades. (Opcional)

NOTE
Una propiedad de un tipo complejo puede ser otro tipo complejo.

Un tipo complejo es similar a un tipo de entidad, ya que un tipo complejo puede llevar una carga de datos en
forma de propiedades de tipo primitivo u otros tipos complejos. Sin embargo, existen algunas diferencias clave
entre los tipos complejos y los tipos de entidad:
Los tipos complejos no tienen identidades y, por consiguiente, no pueden existir de forma independiente.
Los tipos complejos solo pueden existir como propiedades en tipos de entidad u otros tipos complejos.
Los tipos complejos no pueden participar en asociaciones. Ninguno de los extremos de una asociación
puede ser un tipo complejo y, por lo tanto, las propiedades de navegación no se pueden definir en tipos
complejos.

Ejemplo
El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define un tipo
complejo, Address, con las propiedades de tipo primitivo StreetAddress , City , StateOrProvince , Country y
PostalCode .

<ComplexType Name="Address" >


<Property Type="String" Name="StreetAddress" Nullable="false" />
<Property Type="String" Name="City" Nullable="false" />
<Property Type="String" Name="StateOrProvince" Nullable="false" />
<Property Type="String" Name="Country" Nullable="false" />
<Property Type="String" Name="PostalCode" Nullable="false" />
</ComplexType>

Para definir el tipo complejo Address (arriba) como una propiedad en un tipo de entidad, debe declarar el tipo de
propiedad en la definición del tipo de entidad. El código CSDL siguiente declara la propiedad Address como un
tipo complejo en un tipo de entidad (Publisher):
<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="BooksModel.Address" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="BooksModel.PublishedBy"
FromRole="Publisher" ToRole="Book" />
</EntityType>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
contenedor de entidades
08/11/2019 • 2 minutes to read • Edit Online

Un contenedor de entidades es una agrupación lógica de conjuntos de entidades, conjuntos de asociacionese


importaciones de funciones.
Un contenedor de entidades definido en un modelo conceptual debe cumplir las condiciones siguientes:
Se debe definir al menos un contenedor de entidades en cada modelo conceptual.
El contenedor de entidades debe tener un nombre único en cada modelo conceptual.
Un contenedor de entidades puede definir conjuntos de entidades o de asociaciones que usan tipos de entidad o
asociaciones definidos en uno o varios espacios de nombres. Para obtener más información, vea Entity Data
Model: espacios de nombres.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author .
Para obtener más información, vea el ejemplo siguiente.

Aunque el diagrama no proporciona información sobre el contenedor de entidades, el modelo conceptual debe
definir un contenedor de entidades. El Entity Framework ADO.net usa un DSL denominado lenguaje de definición
de esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define un
contenedor de entidades para el modelo conceptual mostrado en el diagrama anterior. Tenga en cuenta que el
nombre del contenedor de entidades se define en un atributo XML.

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
clave de entidad
08/11/2019 • 5 minutes to read • Edit Online

Una clave de entidad es una propiedad o un conjunto de propiedades de un tipo de entidad que se usan para
determinar la identidad. Las propiedades que constituyen una entidad se eligen en tiempo de diseño. Los valores
de las propiedades de clave de entidad deben identificar de forma única una instancia de tipo de entidad dentro
de un conjunto de entidades en tiempo de ejecución. Las propiedades que constituyen una clave de entidad se
deben elegir de tal forma que garanticen la unicidad de las instancias de un conjunto de entidades.
Los requisitos para que un conjunto de propiedades sea una clave de entidad son los siguientes:
No puede haber dos claves de entidad idénticas en un conjunto de entidades. Es decir, para dos entidades
cualesquiera dentro de un conjunto de entidades, los valores de todas las propiedades que constituyen
una clave no pueden ser idénticos. Sin embargo, algunos (pero no todos) los valores que constituyen una
clave de entidad pueden ser idénticos.
Una clave de entidad debe constar de un conjunto de propiedades de tipo primitivoque no aceptan
valores NULL e inmutables.
Las propiedades que constituyen una clave de entidad para un tipo de entidad determinado no pueden
cambiar. No se puede designar más de una clave de entidad posible para un tipo de entidad determinado;
no se admiten las claves suplentes.
Cuando una entidad está implicada en una jerarquía de herencia, la entidad raíz debe contener todas las
propiedades que constituyen la clave de entidad, y esta se debe definir en el tipo de entidad raíz. Para
obtener más información, vea Entity Data Model: herencia.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author .
Las propiedades de cada tipo de entidad que constituyen su clave de entidad se denotan con "(Key)". Tenga en
cuenta que el tipo de entidad Author tiene una clave de entidad que consta de dos propiedades, Name y
Address .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición
de esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define el tipo
de entidad Book mostrado en el diagrama anterior. Observe que la clave de entidad se define haciendo
referencia a la propiedad ISBN del tipo de entidad.
<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>

La propiedad ISBN es una opción adecuada para la clave de entidad, ya que el International Standard Book
Number (ISBN) permite identificar un libro de manera inequívoca.
El código CSDL siguiente define el tipo de entidad Author mostrado en el diagrama anterior. Observe que la
clave de entidad consta de dos propiedades, Name y Address .

<EntityType Name="Author">
<Key>
<PropertyRef Name="Name" />
<PropertyRef Name="Address" />
</Key>
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="String" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="BooksModel.WrittenBy"
FromRole="Author" ToRole="Book" />
</EntityType>

El uso de Name y Address para la clave de entidad es una opción razonable, ya que no es probable que dos
autores con el mismo nombre vivan en la misma dirección. Sin embargo, esta opción no garantiza por completo
la existencia de claves de entidad únicas en un conjunto de entidades. En este caso, se recomienda la adición de
una propiedad, como AuthorId , que se podría usar para identificar un autor de forma inequívoca.

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
conjunto de entidades
08/11/2019 • 5 minutes to read • Edit Online

Un conjunto de entidades es un contenedor lógico para las instancias de un tipo de entidad y las instancias de
cualquier tipo derivado de ese tipo de entidad. (Para obtener información sobre los tipos derivados, vea Entity
Data Model: herencia). La relación entre un tipo de entidad y un conjunto de entidades es análoga a la relación
entre una fila y una tabla en una base de datos relacional: como una fila, un tipo de entidad describe la
estructura de datos y, como una tabla, un conjunto de entidades contiene instancias de una estructura
determinada. Un conjunto de entidades no es una construcción de modelado de datos, ya que no describe la
estructura de los datos. En su lugar, un conjunto de entidades proporciona una construcción para que un
entorno de hospedaje o de almacenamiento (como Common Language Runtime o una base de datos de SQL
Server) agrupe las instancias del tipo de entidad y se puedan asignar a un almacén de datos.
Un conjunto de entidades se define dentro de un contenedor de entidades, que es una agrupación lógica de
conjuntos de entidades y conjuntos de asociaciones.
Para que una instancia de tipo de entidad exista en un conjunto de entidades, deben cumplirse las condiciones
siguientes:
El tipo de la instancia puede ser el mismo que el tipo de entidad en el que se basa el conjunto de
entidades, o un subtipo del tipo de entidad.
La clave de entidad para la instancia es única en el conjunto de entidades.
La instancia no existe en ningún otro conjunto de entidades.

NOTE
Se pueden definir varios conjuntos de entidades usando el mismo tipo de entidad, pero una instancia de un tipo
de entidad determinado solo puede existir en un conjunto de entidades.

No es necesario definir un conjunto de entidades para cada tipo de entidad de un modelo conceptual.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author .

El diagrama siguiente muestra dos conjuntos de entidades ( Books y Publishers ) y un conjunto de


asociaciones ( PublishedBy ) basados en el modelo conceptual mostrado anteriormente. BI en el conjunto de
entidades Books representa una instancia del tipo de entidad Book en tiempo de ejecución. De forma similar,
PJ representa una instancia de Publisher en el conjunto de entidades Publishers . BiPj representa una
instancia de la Asociación PublishedBy en el conjunto de asociaciones PublishedBy .
El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición
de esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define un
contenedor de entidades con un conjunto de entidades para cada tipo de entidad del modelo conceptual
mostrado anteriormente. Tenga en cuenta que el nombre y el tipo de entidad de cada conjunto de entidades se
definen mediante atributos XML.

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

Es posible definir varios conjuntos de entidades por tipo (MEST). El código CSDL siguiente define un contenedor
de entidades con dos conjuntos de entidades para el tipo de entidad Book :

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="BooksModel.Book" />
<EntitySet Name="FictionBooks" EntityType="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="BookAuthor" Association="BooksModel.BookAuthor">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
tipo de entidad
08/11/2019 • 2 minutes to read • Edit Online

El tipo de entidad es el bloque de creación fundamental para describir la estructura de los datos con el
Entity Data Model (EDM). En un modelo conceptual, un tipo de entidad representa la estructura de
conceptos de nivel superior, como clientes o pedidos. Un tipo de entidad es una plantilla para las instancias
de tipo de entidad. Cada plantilla contiene la información siguiente:
Un nombre único. (Requerido)
Una clave de entidad definida por una o más propiedades. (Requerido)
Datos en forma de propiedades. (Opcional)
Propiedades de navegación que permiten la navegación desde un extremo de una Asociación al otro
extremo. (Opcional)
En una aplicación, una instancia de un tipo de entidad representa un objeto específico (como un cliente o un
pedido concreto). Cada instancia de un tipo de entidad debe tener una clave de entidad única dentro de un
conjunto de entidades.
Dos instancias de tipo de entidad se consideran iguales solo si son del mismo tipo y los valores de sus
claves de entidad son idénticos.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidad: Book , Publisher y Author :

Tenga en cuenta que las propiedades de cada tipo de entidad que constituyen su clave de entidad se
denotan con "(Key)".
El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de
definición de esquemas conceptuales (CSDL) para definir los modelos conceptuales. El siguiente CSDL
define el tipo de entidad Book mostrado en el diagrama anterior.
<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
facet
facet
08/11/2019 • 3 minutes to read • Edit Online

Una faceta se usa para agregar detalles a una definición de propiedad de tipo primitivo. Una definición de
propiedad contiene información sobre el tipo de propiedad, pero a menudo es necesario más detalles. Por
ejemplo, un tipo de entidad en un modelo conceptual podría tener una propiedad de tipo String cuyo valor no
se puede establecer en NULL. Las facetas permiten especificar este nivel de detalle.
En la siguiente tabla se describen las facetas que se admiten en EDM.

NOTE
El entorno de tiempo de ejecución que utiliza una implementación de EDM determina los valores exactos y los
comportamientos de las facetas.

FA C ETA DESC RIP C IÓ N SE A P L IC A A

Collation Especifica la secuencia de intercalación String


(o secuencia de orden) que se va a usar
cuando se realicen las operaciones de
comparación y ordenación sobre los
valores de la propiedad.

ConcurrencyMode Indica que el valor de propiedad se Todas las propiedades de tipo primitivo
debería utilizar para las
comprobaciones de la simultaneidad
optimista.

Default Especifica el valor predeterminado de la Todas las propiedades de tipo primitivo


propiedad si no se proporciona ningún
valor al crear las instancias.

FixedLength Especifica si la longitud del valor de Binary , String


propiedad puede variar.

MaxLength Especifica la longitud máxima del valor Binary , String


de propiedad.

Nullable Especifica si la propiedad puede tener Todas las propiedades de tipo primitivo
un valor NULL.

Precision Para las propiedades de tipo Decimal , DateTime , DateTimeOffset ,


especifica el número de dígitos que Decimal , Time ,
puede tener un valor de propiedad.
Para las propiedades de tipo Time ,
DateTime y DateTimeOffset ,
especifica el número de dígitos para la
parte fraccionaria de los segundos del
valor de propiedad.
FA C ETA DESC RIP C IÓ N SE A P L IC A A

Scale Especifica el número de dígitos que Decimal


puede haber a la derecha del separador
decimal para el valor de propiedad.

Unicode Indica si el valor de propiedad está String


almacenado como Unicode.

Ejemplo
El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. En el ejemplo siguiente CSDL, se define un
tipo de entidad Book . Observe que las facetas se implementan como atributos XML. Los valores de faceta indican
que ninguna propiedad puede estar establecida en NULL, y que los valores Scale y Precision de la propiedad
Revision están establecidos ambos en 29.

<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
propiedad de clave externa
08/11/2019 • 2 minutes to read • Edit Online

Una propiedad de clave externa en el Entity Data Model (EDM) es una propiedad de tipo primitivo (o un conjunto
de propiedades de tipo primitivo) en un tipo de entidad que contiene la clave de entidad de otro tipo de entidad.
Una propiedad de clave externa es análoga a una columna de clave externa de una base de datos relacional. Del
mismo modo que las columnas de clave externa se utilizan en una base de datos relacional para crear relaciones
entre las filas de las tablas, las propiedades de clave externa en un modelo conceptual se utilizan para establecer
asociaciones entre tipos de entidad. Una restricción de integridad referencial se utiliza para definir una asociación
entre dos tipos de entidad cuando uno de los tipos tiene una propiedad de clave externa.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author . El
tipo de entidad Book tiene una propiedad, PublisherId , que hace referencia a la clave de entidad del tipo de
entidad Publisher cuando se define una restricción de integridad referencial en la asociación PublishedBy .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente usa la propiedad
de clave externa PublisherId para definir una restricción de integridad referencial en la asociación PublishedBy
incluida en el modelo conceptual mostrado anteriormente.

<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" >
</End>
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
función declarada por el modelo
23/10/2019 • 2 minutes to read • Edit Online

Una función declarada por el modelo es una función que se declara en un modelo conceptual, pero que no está
definida en ese modelo conceptual. La función se podría definir en el entorno de almacenamiento u hospedaje. Por
ejemplo, una función declarada por modelo se podría asignar a una función definida en una base de datos,
exponiendo así la funcionalidad de servidor en el modelo conceptual.
La declaración de una función declarada por modelo contiene la siguiente información:
Nombre de la función. (Necesario)
El tipo del valor devuelto. (Opcional)

NOTE
Si no se especifica ningún valor devuelto, el tipo de valor devuelto es void.

Información de parámetro, incluidos el tipo y el nombre de parámetro. (Opcional)

Ejemplo
El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. En CSDL, una implementación de una
función declarada por modelo es una importación de función (mediante el elemento FunctionImport). El siguiente
CSDL define un contenedor de la entidad con una definición de importación de función. Tenga en cuenta que el
tipo de valor devuelto es void porque no se especifica ningún tipo de valor devuelto.

<FunctionImport Name="UpdatePublisher">
<Parameter Name="PublisherId" Mode="In" Type="Int32" />
<Parameter Name="PublisherName" Mode="In" Type="String" />
</FunctionImport>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
función definida por el modelo
08/11/2019 • 2 minutes to read • Edit Online

Una función definida por el modelo es una función que se define en un modelo conceptual. El cuerpo de una
función definida por el modelo se expresa en Entity SQL, lo que permite expresar la función independientemente
de las reglas o los lenguajes admitidos en el origen de datos.
La definición para una función definida por el modelo contiene la información siguiente:
El nombre de la función. (Necesario)
El tipo del valor devuelto. (Opcional)

NOTE
Si no se especifica ningún tipo de valor devuelto, este es void.

Información de parámetros. (Opcional)


Expresión Entity SQL que define el cuerpo de la función.
Tenga en cuenta que las funciones definidas por el modelo no admiten parámetros de salida. Esta restricción se
aplica para que las funciones definidas por el modelo puedan ser compuestas.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define una función
en el modelo conceptual que devuelve el número de años transcurridos desde la publicación de una instancia de
Book (en el diagrama anterior).

<Function Name="GetYearsInPrint" ReturnType="Edm.Int32" >


<Parameter Name="book" Type="BooksModel.Book" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(cast(book.PublishedDate as DateTime))
</DefiningExpression>
</Function>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
Entity Data Model: Tipos de datos primitivos
Propiedad de navegación
10/02/2020 • 3 minutes to read • Edit Online

Una propiedad de navegación es una propiedad opcional en un tipo de entidad que permite la navegación
desde un extremo de una Asociación al otro extremo. A diferencia de otras propiedades, las propiedades de
navegación no contienen datos.
Una definición de propiedad de desplazamiento incluye lo siguiente:
Un nombre. (Requerido)
La asociación que navega. (Requerido)
Los extremos de la asociación que navega. (Requerido)
Las propiedades de navegación son opcionales en ambos tipos de entidad de los extremos de una asociación. Si
define una propiedad de navegación en un tipo de entidad del extremo de una asociación, no tiene que definir
una propiedad de navegación en el tipo de entidad del otro extremo de la asociación.
El tipo de datos de una propiedad de navegación viene determinado por la multiplicidad de su extremode la
Asociación remota. Por ejemplo, supongamos que una propiedad de navegación, OrdersNavProp , existe en un
tipo de entidad Customer y navega a una asociación uno a varios entre Customer y Order . Dado que el
extremo remoto de la Asociación para la propiedad de navegación tiene la multiplicidad de muchos (*), su tipo
de datos es una colección (de Order ). De igual forma, si una propiedad de navegación, CustomerNavProp , existe
en el tipo de entidad Order , su tipo de datos sería Customer , porque la multiplicidad del extremo remoto es
uno (1).

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author .
Las propiedades de navegación Publisher y Authors se definen en el tipo de entidad Book. La propiedad de
navegación Books se define en el tipo de entidad Publisher y el tipo de entidad Author .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición
de esquemas conceptuales (CSDL) para definir los modelos conceptuales. El siguiente CSDL define el tipo de
entidad Book mostrado en el diagrama anterior.
<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>

Los atributos XML se utilizan para comunicar la información necesaria para definir una propiedad de
navegación: el atributo Name contiene el nombre de la propiedad, Relationship contiene el nombre de la
asociación a la que navega y FromRole y ToRole contienen los extremos de la asociación.

Consulte también
Conceptos clave de Entity Data Model
Entity Data Model
Relaciones, propiedades de navegación y claves externas
Propiedad
08/11/2019 • 4 minutes to read • Edit Online

Las propiedades son los bloques de creación fundamentales de tipos de entidad y tipos complejos. Las
propiedades definen la forma y características de datos que una instancia del tipo de entidad o la instancia del
tipo complejo contendrá. Las propiedades en un modelo conceptual son análogas a las propiedades definidas
en una clase. Del mismo modo que las propiedades en una clase definen la forma de la clase y proporcionan
información sobre los objetos, las propiedades en un modelo conceptual definen la forma de un tipo de
entidad y proporcionan información sobre las instancias del tipo de entidad.

NOTE
Las propiedades, tal y como se describen en este tema, son diferentes de las propiedades de navegación. Para obtener
más información, vea propiedades de navegación.

Una definición de propiedad contiene la siguiente información:


nombre de propiedad. (Necesario)
Un tipo de propiedad. (Necesario)
Un conjunto de aspectos. (Opcional)
Una propiedad puede contener datos primitivos (como una cadena, un entero o un valor booleano) o
estructura los datos (como un tipo complejo). Las propiedades de tipo primitivo también se denominan
propiedades escalares. Para obtener más información, vea Entity Data Model: tipos de datos primitivos.

NOTE
Un tipo complejo puede, por sí mismo, tener propiedades complejas.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con tres tipos de entidades: Book , Publisher y Author
. Cada tipo de entidad tiene varias propiedades, aunque la información de tipo para cada propiedad no se
muestra en el diagrama. Las propiedades que son claves de entidad se denotan con (Key).

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición
de esquemas conceptuales (CSDL) para definir los modelos conceptuales. En el siguiente CSDL se define el
tipo de entidad Book (como se muestra en el diagrama anterior) y se indica el tipo y nombre de cada
propiedad utilizando atributos XML. Una faceta opcional, Nullable , también se define utilizando un atributo
de XML.
<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>

Es posible que una de las propiedades mostradas en el diagrama sea una propiedad de tipo complejo. Por
ejemplo, la propiedad Address en el tipo de entidad Publisher podría ser una propiedad de tipo complejo
compuesta de varias propiedades escalares, como StreetAddress , City , StateOrProvince , Country y
PostalCode . La representación CSDL de un tipo complejo como el anterior sería como sigue:

<ComplexType Name="Address" >


<Property Type="String" Name="StreetAddress" Nullable="false" />
<Property Type="String" Name="City" Nullable="false" />
<Property Type="String" Name="StateOrProvince" Nullable="false" />
<Property Type="String" Name="Country" Nullable="false" />
<Property Type="String" Name="PostalCode" Nullable="false" />
</ComplexType>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
restricción de integridad referencial
08/11/2019 • 3 minutes to read • Edit Online

Una restricción de integridad referencial en el Entity Data Model (EDM) es similar a una restricción de integridad
referencial en una base de datos relacional. Del mismo modo que una columna (o columnas) de una tabla de base
de datos puede hacer referencia a la clave principal de otra tabla, una propiedad (o propiedades) de un tipo de
entidad puede hacer referencia a la clave de entidad de otro tipo de entidad. El tipo de entidad al que se hace
referencia se denomina extremo principal de la restricción. El tipo de entidad que hace referencia al extremo
principal se denomina extremo dependiente de la restricción.
Una restricción de integridad referencial se define como parte de una Asociación entre dos tipos de entidad. La
definición para una restricción de integridad referencial especifica la siguiente información:
El extremo principal de la restricción. Es un tipo de entidad a cuya clave de entidad hace referencia el
extremo dependiente.
La clave de entidad del extremo principal.
El extremo dependiente de la restricción. Es un tipo de entidad que tiene una o varias propiedades que
hacen referencia a la clave de entidad del extremo principal.
La propiedad o propiedades que hacen la referencia del extremo dependiente.
El propósito de las restricciones de integridad referencial de EDM es garantizar la existencia de asociaciones
válidas. Para obtener más información, consulte Foreign Key Property.

Ejemplo
El diagrama siguiente muestra un modelo conceptual con dos asociaciones: WrittenBy y PublishedBy . El tipo de
entidad Book tiene una propiedad, PublisherId , que hace referencia a la clave de entidad del tipo de entidad
Publisher cuando se define una restricción de integridad referencial en la asociación PublishedBy .

El Entity Framework ADO.net usa un lenguaje específico de dominio (DSL) denominado lenguaje de definición de
esquemas conceptuales (CSDL) para definir los modelos conceptuales. El código CSDL siguiente define una
restricción de integridad referencial en la asociación PublishedBy mostrada en el modelo conceptual
anteriormente citado.
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" >
</End>
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>

Vea también
Conceptos clave de Entity Data Model
Entity Data Model
Oracle y ADO.NET
05/02/2020 • 3 minutes to read • Edit Online

NOTE
Los tipos de System.Data.OracleClient están en desuso. Los tipos seguirán estando admitidos en la versión actual de .NET
Framework, pero se quitarán en una versión posterior. Microsoft recomienda usar un proveedor de Oracle de otro
fabricante.

En esta sección se describen características y comportamientos específicos del proveedor de datos de .NET
Framework para Oracle.
El proveedor de datos de .NET Framework para Oracle proporciona acceso a una base de datos de Oracle
mediante Oracle Call Interface (OCI), tal y como lo proporciona el software cliente de Oracle. La funcionalidad del
proveedor de datos está diseñada para ser similar a la de los .NET Framework proveedores de datos para SQL
Server, OLE DB y ODBC.
Para utilizar el proveedor de datos de .NET Framework para Oracle, una aplicación debe hacer referencia al
espacio de nombres System.Data.OracleClient como se indica a continuación:

Imports System.Data.OracleClient

using System.Data.OracleClient;

También debe incluir una referencia a la DLL cuando compile el código. Por ejemplo, si compila un programa C#,
la línea de comandos debe incluir:

csc /r:System.Data.OracleClient.dll

Esta sección
Requisitos del sistema
Describe los requisitos para usar el proveedor de datos de .NET Framework para Oracle y describe una serie de
problemas que se deben tener en cuenta al utilizarlos.
Objetos BFILE de Oracle
Describe la clase OracleBFile, que se utiliza para trabajar con el tipo de datos BFILE de Oracle.
Objetos LOB de Oracle
Describe la clase OracleLob, que se utiliza para trabajar con tipos de datos LOB de Oracle.
Parámetros REF CURSOR de Oracle
Describe la compatibilidad con el tipo de datos REF CURSOR de Oracle.
Tipos de Oracle
Describe las estructuras que puede utilizar para trabajar con tipos de datos de Oracle, como OracleNumber y
OracleString.
Secuencias de Oracle
Describe la compatibilidad para recuperar los valores de clave de secuencia de Oracle que genera el servidor.
Asignaciones de tipos de datos de Oracle
Enumera los tipos de datos de Oracle y sus asignaciones al OracleDataReader.
Transacciones distribuidas de Oracle
Describe cómo se inscribe automáticamente el objeto OracleConnection en una transacción distribuida si se
determina que hay una transacción activa.

Secciones relacionadas
Proteger aplicaciones de ADO.NET
Describe algunas recomendaciones de codificación segura para utilizar ADO.NET.
Objetos DataSet, DataTable y DataView
Describe cómo crear y usar DataSets , DataSets con establecimiento de tipos, DataTables y DataViews .
Recuperar y modificar datos en ADO.NET
Describe cómo trabajar con datos XML en ADO.NET.
SQL Server y ADO.NET
Describe cómo trabajar con las características y la funcionalidad específicas de SQL Server.
Objetos DbProviderFactory
Describe clases genéricas que permiten escribir código independiente del proveedor en ADO.NET.

Vea también
ADO.NET
Información general sobre ADO.NET
Requisitos del sistema para el proveedor de datos
.NET Framework para Oracle
21/03/2020 • 5 minutes to read • Edit Online

El proveedor de datos .NET Framework para Oracle requiere Microsoft Data Access Components (MDAC), versión
2.6 o posterior. Se recomienda MDAC 2.8 SP1.
También debe tener instalado Oracle 8i Release 3 (8.1.7) Client o posterior.
El software Oracle Client anterior a la versión Oracle 9i no puede tener acceso a las bases de datos UTF16 dado que
UTF16 es una nueva característica en Oracle 9i. Para utilizar esta característica, deberá actualizar el software de
cliente a Oracle 9i o posterior.

Trabajo con el proveedor de datos para Oracle y datos Unicode


A continuación se muestra una lista de problemas relacionados con Unicode que debe tener en cuenta al trabajar
con el proveedor de datos de .NET Framework para las bibliotecas de cliente de Oracle y Oracle. Para obtener más
información, vea la documentación de Oracle.
Definición de un valor Unicode en un atributo de cadena de conexión
Al trabajar con Oracle, puede utilizar el atributo de cadena de conexión
Unicode=True

para inicializar las bibliotecas de cliente de Oracle en modo UTF-16. De esta manera, las bibliotecas de cliente de
Oracle aceptarán UTF-16 (que es muy parecido a UCS-2) en lugar de cadenas de varios bytes. Como consecuencia,
el proveedor de datos para Oracle puede trabajar siempre con cualquier página de códigos Oracle sin necesidad de
trabajo de conversión adicional. Esta configuración sólo funciona si utiliza clientes Oracle 9i para comunicarse con
una base de datos Oracle 9i con el juego de caracteres alternativo de AL16UTF16. Cuando un cliente De Oracle 9i
se comunica con un servidor Oracle 9i, se requieren recursos adicionales para convertir los valores de
CommandText Unicode en el conjunto de caracteres multibyte adecuado que utiliza el servidor Oracle9i. Esto se
puede evitar si sabe que tiene la configuración segura agregando Unicode=True a la cadena de conexión.
Mezcla de versiones de Oracle Client y Oracle Server
Los clientes de Oracle 8i no pueden acceder a datos NCHAR, NVARCHAR2 o NCLOB en bases de datos Oracle 9i
cuando el juego de caracteres nacional del servidor se especifica como AL16UTF16 (la configuración
predeterminada para Oracle 9i). Como hasta Oracle 9i no se introdujo la compatibilidad con el juego de caracteres
UTF-16, los clientes Oracle 8i no pueden leerlo.
Trabajo con datos UTF -8
Para definir el juego de caracteres alternativo, establezca la clave del Registro
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMEID\NLS_LANG en UTF8. Para obtener más información, vea
las notas de instalación de Oracle correspondientes a su plataforma. La opción predeterminada es el juego de
caracteres principal del idioma con el que va a instalar el software Oracle Client. Si no establece el idioma para que
coincida con el juego de caracteres del idioma nacional de la base de datos a la que se va a conectar, ocasionará
que los enlaces de parámetros y columnas envíen o reciban datos con el juego de caracteres principal de la base de
datos, y no con el juego de caracteres nacional.
OracleLob sólo puede actualizar caracteres completos.
Por motivos de OracleLob usabilidad, el objeto hereda de la clase Stream de .NET Framework y proporciona
métodos ReadByte y WriteByte. También implementa métodos, como CopyTo y Erase, que funcionan en
secciones de objetos LOB de Oracle. Por el contrario, el software cliente de Oracle proporciona una serie de API
para trabajar con caracteres LOB s (CLOB y NCLOB ). No obstante, estas API sólo funcionan en caracteres
completos. Debido a esta diferencia, el proveedor de datos para Oracle implementa la compatibilidad con Read y
ReadByte para trabajar con datos UTF-16 de una manera byte. Sin embargo, los otros métodos del objeto
OracleLob solo permiten operaciones de caracteres completos.

Consulte también
Oracle y ADO.NET
Información general de ADO.NET
Objetos BFILE de Oracle
21/03/2020 • 4 minutes to read • Edit Online

El proveedor de datos .NET Framework para Oracle incluye la clase OracleBFile, que se utiliza para trabajar con el
tipo de datos BFile de Oracle.
El tipo de datos BFILE de Oracle es un tipo de datos LOB de Oracle que contiene una referencia a datos binarios
con un tamaño máximo de 4 gigabytes. Un BFILE de Oracle difiere de otros tipos de datos LOB de Oracle en que
sus datos se almacenan en un archivo físico en el sistema operativo en lugar de en el servidor. Tenga en cuenta que
el tipo de datos BFILE proporciona acceso de solo lectura a los datos.
Otras características de un tipo de datos BFILE que lo distinguen de un tipo de datos LOB son que:
contiene datos no estructurados.
admite el troceo en el servidor.
utiliza semántica de copia de referencia. Por ejemplo, si realiza una operación de copia en un archivo BFILE ,
solo se copia el localizador BFILE (que es una referencia al archivo). Los datos del archivo no se copian.
El tipo de datos BFILE se debe usar para hacer referencia a LOB que son de gran tamaño y, por lo tanto, no es
práctico almacenar en la base de datos. Más sobrecarga de cliente, servidor y comunicación implica cuando se usa
un tipo de datos BFILE en comparación con el tipo de datos LOB. Es más eficaz acceder a un Archivo BFILE si
solo necesita obtener una pequeña cantidad de datos. En cambio, si necesita obtener el objeto entero, es más
eficiente tener acceso a los LOB residentes en la base de datos.
Cada objeto OracleBFile no NULL está asociado a dos entidades que definen la ubicación del archivo físico
subyacente:
1. Un objeto DIRECTORY de Oracle, que es un alias de base de datos de un directorio del sistema de archivos, y
2. el nombre de archivo del archivo físico subyacente, que se encuentra en el directorio asociado con el objeto
DIRECTORY.

Ejemplo
En el siguiente ejemplo de C- se muestra cómo crear un archivo BFILE en una tabla de Oracle y, a continuación,
recuperarlo en forma de un OracleBFile objeto. En el ejemplo OracleDataReader se muestra cómo utilizar el
objeto y los métodos OracleBFile Seek y Read. Tenga en cuenta que para utilizar este ejemplo, primero\debe
crear un directorio denominado "c: .bfiles" y el archivo denominado "MyFile.jpg" en el servidor de Oracle.
using System;
using System.IO;
using System.Data;
using System.Data.OracleClient;

public class Sample


{
public static void Main(string[] args)
{
OracleConnection connection = new OracleConnection(
"Data Source=Oracle8i;Integrated Security=yes");
connection.Open();

OracleCommand command = connection.CreateCommand();


command.CommandText =
"CREATE or REPLACE DIRECTORY MyDir as 'c:\\bfiles'";
command.ExecuteNonQuery();
command.CommandText =
"DROP TABLE MyBFileTable";
try {
command.ExecuteNonQuery();
}
catch {
}
command.CommandText =
"CREATE TABLE MyBFileTable(col1 number, col2 BFILE)";
command.ExecuteNonQuery();
command.CommandText =
"INSERT INTO MyBFileTable values ('2', BFILENAME('MyDir', " +
"'MyFile.jpg'))";
command.ExecuteNonQuery();
command.CommandText = "SELECT * FROM MyBFileTable";

byte[] buffer = new byte[100];

OracleDataReader reader = command.ExecuteReader();


using (reader) {
if (reader.Read()) {
OracleBFile bFile = reader.GetOracleBFile(1);
using (bFile) {
bFile.Seek(0, SeekOrigin.Begin);
bFile.Read(buffer, 0, 100);
}
}
}

connection.Close();
}

Consulte también
Oracle y ADO.NET
Información general de ADO.NET
Objetos LOB de Oracle
21/03/2020 • 6 minutes to read • Edit Online

El proveedor de datos de OracleLob .NET Framework para Oracle incluye la clase, que se utiliza para trabajar con
tipos de datos LOB de Oracle.
Un OracleLob puede ser OracleType uno de estos tipos de datos:

T IP O DE DATO S DESC RIP C IÓ N

Blob Un tipo de datos BLOB de Oracle que contiene datos binarios


con un tamaño máximo de 4 gigabytes. Esto se asigna a una
matriz de tipo Byte .

Clob Un tipo de datos CLOB de Oracle que contiene datos de


caracteres, basados en el juego de caracteres predeterminado
en el servidor, con un tamaño máximo de 4 gigabytes. Esto se
asigna a String .

NClob Un tipo de datos NCLOB de Oracle que contiene datos de


caracteres, basado según el juego de caracteres nacional en el
servidor con un tamaño máximo de 4 gigabytes. Esto se
asigna a String .

Un OracleLob difiere OracleBFile de un en que los datos se almacenan en el servidor en lugar de en un archivo
físico en el sistema operativo. También puede ser un objeto de lectura y escritura, a diferencia de un OracleBFile ,
que siempre es de solo lectura.

Creación y recuperación de LOB y forma de escribir en él


En el siguiente ejemplo de C-C se muestra cómo crear LOB en una tabla de Oracle y, a continuación, recuperar y
escribir en ellos en forma de OracleLob objetos. En el ejemplo OracleDataReader se muestra cómo utilizar el
objeto y los métodos OracleLob Read and Write. En el ejemplo se utilizan los tipos de datos BLOB , CLOB y
NCLOB de Oracle.

using System;
using System.IO;
using System.Text;
using System.Data;
using System.Data.OracleClient;

// LobExample
public class LobExample
{
public static int Main(string[] args)
{
//Create a connection.
OracleConnection conn = new OracleConnection(
"Data Source=Oracle8i;Integrated Security=yes");
using(conn)
{
//Open a connection.
conn.Open();
OracleCommand cmd = conn.CreateCommand();

//Create the table and schema.


CreateTable(cmd);

//Read example.
ReadLobExample(cmd);

//Write example
WriteLobExample(cmd);
}

return 1;
}

// ReadLobExample
public static void ReadLobExample(OracleCommand cmd)
{
int actual = 0;

// Table Schema:
// "CREATE TABLE tablewithlobs (a int, b BLOB, c CLOB, d NCLOB)";
// "INSERT INTO tablewithlobs values (1, 'AA', 'AAA', N'AAAA')";
// Select some data.
cmd.CommandText = "SELECT * FROM tablewithlobs";
OracleDataReader reader = cmd.ExecuteReader();
using(reader)
{
//Obtain the first row of data.
reader.Read();

//Obtain the LOBs (all 3 varieties).


OracleLob blob = reader.GetOracleLob(1);
OracleLob clob = reader.GetOracleLob(2);
OracleLob nclob = reader.GetOracleLob(3);

//Example - Reading binary data (in chunks).


byte[] buffer = new byte[100];
while((actual = blob.Read(buffer, 0, buffer.Length)) >0)
Console.WriteLine(blob.LobType + ".Read(" + buffer + ", " +
buffer.Length + ") => " + actual);

// Example - Reading CLOB/NCLOB data (in chunks).


// Note: You can read character data as raw Unicode bytes
// (using OracleLob.Read as in the above example).
// However, because the OracleLob object inherits directly
// from the .NET stream object,
// all the existing classes that manipluate streams can
// also be used. For example, the
// .NET StreamReader makes it easier to convert the raw bytes
// into actual characters.
StreamReader streamreader =
new StreamReader(clob, Encoding.Unicode);
char[] cbuffer = new char[100];
while((actual = streamreader.Read(cbuffer,
0, cbuffer.Length)) >0)
Console.WriteLine(clob.LobType + ".Read(
" + new string(cbuffer, 0, actual) + ", " +
cbuffer.Length + ") => " + actual);

// Example - Reading data (all at once).


// You could use StreamReader.ReadToEnd to obtain
// all the string data, or simply
// call OracleLob.Value to obtain a contiguous allocation
// of all the data.
Console.WriteLine(nclob.LobType + ".Value => " + nclob.Value);
}
}

// WriteLobExample
public static void WriteLobExample(OracleCommand cmd)
{
//Note: Updating LOB data requires a transaction.
cmd.Transaction = cmd.Connection.BeginTransaction();

// Select some data.


// Table Schema:
// "CREATE TABLE tablewithlobs (a int, b BLOB, c CLOB, d NCLOB)";
// "INSERT INTO tablewithlobs values (1, 'AA', 'AAA', N'AAAA')";
cmd.CommandText = "SELECT * FROM tablewithlobs FOR UPDATE";
OracleDataReader reader = cmd.ExecuteReader();
using(reader)
{
// Obtain the first row of data.
reader.Read();

// Obtain a LOB.
OracleLob blob = reader.GetOracleLob(1/*0:based ordinal*/);

// Perform any desired operations on the LOB


// (read, position, and so on).

// Example - Writing binary data (directly to the backend).


// To write, you can use any of the stream classes, or write
// raw binary data using
// the OracleLob write method. Writing character vs. binary
// is the same;
// however note that character is always in terms of
// Unicode byte counts
// (for example, even number of bytes - 2 bytes for every
// Unicode character).
byte[] buffer = new byte[100];
buffer[0] = 0xCC;
buffer[1] = 0xDD;
blob.Write(buffer, 0, 2);
blob.Position = 0;
Console.WriteLine(blob.LobType + ".Write(
" + buffer + ", 0, 2) => " + blob.Value);

// Example - Obtaining a temp LOB and copying data


// into it from another LOB.
OracleLob templob = CreateTempLob(cmd, blob.LobType);
long actual = blob.CopyTo(templob);
Console.WriteLine(blob.LobType + ".CopyTo(
" + templob.Value + ") => " + actual);

// Commit the transaction now that everything succeeded.


// Note: On error, Transaction.Dispose is called
// (from the using statement)
// and will automatically roll back the pending transaction.
cmd.Transaction.Commit();
}
}

// CreateTempLob
public static OracleLob CreateTempLob(
OracleCommand cmd, OracleType lobtype)
{
//Oracle server syntax to obtain a temporary LOB.
cmd.CommandText = "DECLARE A " + lobtype + "; "+
"BEGIN "+
"DBMS_LOB.CREATETEMPORARY(A, FALSE); "+
":LOC := A; "+
"END;";

//Bind the LOB as an output parameter.


OracleParameter p = cmd.Parameters.Add("LOC", lobtype);
p.Direction = ParameterDirection.Output;

//Execute (to receive the output temporary LOB).


cmd.ExecuteNonQuery();
cmd.ExecuteNonQuery();

//Return the temporary LOB.


return (OracleLob)p.Value;
}

// CreateTable
public static void CreateTable(OracleCommand cmd)
{
// Table Schema:
// "CREATE TABLE tablewithlobs (a int, b BLOB, c CLOB, d NCLOB)";
// "INSERT INTO tablewithlobs VALUES (1, 'AA', 'AAA', N'AAAA')";
try
{
cmd.CommandText = "DROP TABLE tablewithlobs";
cmd.ExecuteNonQuery();
}
catch(Exception)
{
}

cmd.CommandText =
"CREATE TABLE tablewithlobs (a int, b BLOB, c CLOB, d NCLOB)";
cmd.ExecuteNonQuery();
cmd.CommandText =
"INSERT INTO tablewithlobs VALUES (1, 'AA', 'AAA', N'AAAA')";
cmd.ExecuteNonQuery();
}
}

Creación de un LOB temporal


En el siguiente ejemplo con C# se muestra cómo crear un LOB temporal.

OracleConnection conn = new OracleConnection(


"server=test8172; integrated security=yes;");
conn.Open();

OracleTransaction tx = conn.BeginTransaction();

OracleCommand cmd = conn.CreateCommand();


cmd.Transaction = tx;
cmd.CommandText =
"declare xx blob; begin dbms_lob.createtemporary(
xx, false, 0); :tempblob := xx; end;";
cmd.Parameters.Add(new OracleParameter("tempblob",
OracleType.Blob)).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
OracleLob tempLob = (OracleLob)cmd.Parameters[0].Value;
tempLob.BeginBatch(OracleLobOpenMode.ReadWrite);
tempLob.Write(tempbuff,0,tempbuff.Length);
tempLob.EndBatch();
cmd.Parameters.Clear();
cmd.CommandText = "myTable.myProc";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter(
"ImportDoc", OracleType.Blob)).Value = tempLob;
cmd.ExecuteNonQuery();

tx.Commit();

Consulte también
Oracle y ADO.NET
Información general de ADO.NET
Parámetros REF CURSOR de Oracle
23/10/2019 • 3 minutes to read • Edit Online

El proveedor de datos de .NET Framework para Oracle admite el tipo de datos ref cursor de Oracle. Cuando
utilice el proveedor de datos para trabajar con cursores REF CURSOR de Oracle, debe tener en cuenta los
siguientes comportamientos.

NOTE
Algunos de ellos difieren de los del proveedor Microsoft OLE DB para Oracle (MSDAORA).

Por motivos de rendimiento, el proveedor de datos para Oracle no enlaza automáticamente los tipos de
datos ref cursor , como hace MSDAORA, a menos que se especifiquen explícitamente.
El proveedor de datos no admite ninguna secuencia de escape ODBC, lo que incluye el escape {resultset}
usado para especificar parámetros REF CURSOR.
Para ejecutar un procedimiento almacenado que devuelve los cursores REF cursor, debe definir los
parámetros de OracleParameterCollection con un OracleType de cursor y un Direction de Output . El
proveedor de datos admite el enlace de cursores REF CURSOR sólo como parámetros de salida; no los
admite como parámetros de entrada.
No se permite la obtención de un OracleDataReader del valor del parámetro. Los valores son del tipo
DBNull después de la ejecución del comando.
El único valor de enumeración de CommandBehavior que funciona con cursores REF cursor (por
ExecuteReaderejemplo, al llamar a) es CloseConnection ; se omiten todos los demás.
El orden de los CURSOres REF CURSOR en el objeto OracleDataReader depende del orden de los
parámetros de OracleParameterCollection . Se omite la propiedad ParameterName.
No se admite el tipo de datos de la tabla PL/SQL. No obstante, los cursores REF CURSOR resultan más
eficientes. Si debe utilizar un tipo de datos de tabla , use el proveedor de datos OLE DB .net con
MSDAORA.

En esta sección
Ejemplos de REF CURSOR
Contiene tres ejemplos en los que se muestra el uso de cursores REF CURSOR.
Parámetros REF CURSOR en un objeto OracleDataReader
Muestra cómo ejecutar un procedimiento almacenado PL/SQL que devuelve un parámetro REF CURSOR y lee el
valor como OracleDataReader .
Recuperación de datos desde varios parámetros REF CURSOR utilizando un objeto OracleDataReader
Muestra cómo ejecutar un procedimiento almacenado PL/SQL que devuelve dos parámetros REF CURSOR y Lee
los valores mediante un OracleDataReader .
Relleno de un conjunto de datos utilizando uno o varios parámetros REF CURSOR
Muestra cómo ejecutar un procedimiento almacenado PL/SQL que devuelve dos parámetros REF CURSOR y llena
un DataSet con las filas que se devuelven.
Vea también
Oracle y ADO.NET
Información general sobre ADO.NET
Ejemplos de REF CURSOR
21/03/2020 • 3 minutes to read • Edit Online

Los ejemplos de cursores REF CURSOR constan de los siguientes tres ejemplos de Microsoft Visual Basic que
muestran el uso de los cursores REF CURSOR.

M UEST RA DESC RIP C IÓ N

Parámetros REF CURSOR en un objeto OracleDataReader En este ejemplo se ejecuta un procedimiento almacenado
PL/SQL que devuelve un parámetro REF CURSOR y lee el valor
como un OracleDataReader.

Recuperar datos desde varios parámetros REF CURSOR En este ejemplo se ejecuta un procedimiento almacenado
utilizando un objeto OracleDataReader PL/SQL que devuelve dos parámetros REF CURSOR y lee los
valores mediante OracleDataReader .

Rellenar un conjunto de datos utilizando uno o varios En este ejemplo se ejecuta un procedimiento almacenado
parámetros REF CURSOR PL/SQL que devuelve dos parámetros REF CURSOR y llena un
DataSet con las filas que se devuelven.

Para utilizar estos ejemplos, puede que tenga que crear las tablas de Oracle, y deberá crear un paquete PL/SQL y el
cuerpo del paquete.

Creación de las tablas de Oracle


En estos ejemplos se utilizan tablas que se definen en el esquema Scott/Tiger de Oracle. El esquema Scott/Tiger de
Oracle se incluye en la mayoría de las instalaciones de Oracle. Si no existe este esquema, puede utilizar el archivo
de comandos SQL situado en {OracleHome}\rdbms\admin\scott.sql para crear las tablas y los índices necesarios
para estos ejemplos.

Creación del paquete de Oracle y el cuerpo del paquete


En estos ejemplos es necesario el siguiente paquete PL/SQL y cuerpo del paquete en el servidor. Cree el siguiente
paquete de Oracle en el servidor de Oracle.

CREATE OR REPLACE PACKAGE CURSPKG AS


TYPE T_CURSOR IS REF CURSOR;
PROCEDURE OPEN_ONE_CURSOR (N_EMPNO IN NUMBER,
IO_CURSOR IN OUT T_CURSOR);
PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
DEPTCURSOR OUT T_CURSOR);
END CURSPKG;
/

Cree el siguiente cuerpo del paquete de Oracle en el servidor Oracle.


CREATE OR REPLACE PACKAGE BODY CURSPKG AS
PROCEDURE OPEN_ONE_CURSOR (N_EMPNO IN NUMBER,
IO_CURSOR IN OUT T_CURSOR)
IS
V_CURSOR T_CURSOR;
BEGIN
IF N_EMPNO <> 0
THEN
OPEN V_CURSOR FOR
SELECT EMP.EMPNO, EMP.ENAME, DEPT.DEPTNO, DEPT.DNAME
FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO
AND EMP.EMPNO = N_EMPNO;

ELSE
OPEN V_CURSOR FOR
SELECT EMP.EMPNO, EMP.ENAME, DEPT.DEPTNO, DEPT.DNAME
FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO;

END IF;
IO_CURSOR := V_CURSOR;
END OPEN_ONE_CURSOR;

PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,


DEPTCURSOR OUT T_CURSOR)
IS
V_CURSOR1 T_CURSOR;
V_CURSOR2 T_CURSOR;
BEGIN
OPEN V_CURSOR1 FOR SELECT * FROM EMP;
OPEN V_CURSOR2 FOR SELECT * FROM DEPT;
EMPCURSOR := V_CURSOR1;
DEPTCURSOR := V_CURSOR2;
END OPEN_TWO_CURSORS;
END CURSPKG;
/

Consulte también
Parámetros REF CURSOR de Oracle
Información general de ADO.NET
Parámetros REF CURSOR en un objeto
OracleDataReader
23/10/2019 • 2 minutes to read • Edit Online

En este ejemplo de Microsoft Visual Basic se ejecuta un procedimiento almacenado PL/SQL que devuelve un
parámetro REF CURSOR, y se lee el valor mediante un OracleDataReader.

Private Sub Button1_Click(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles Button1.Click

Dim connString As New String(_


"Data Source=Oracle9i;User ID=scott;Password=tiger;")
Using conn As New OracleConnection(connString)
Dim cmd As New OracleCommand()
Dim rdr As OracleDataReader

conn.Open()
cmd.Connection = conn
cmd.CommandText = "CURSPKG.OPEN_ONE_CURSOR"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New OracleParameter(
"N_EMPNO", OracleType.Number)).Value = 7369
cmd.Parameters.Add(New OracleParameter(
"IO_CURSOR", OracleType.Cursor)).Direction = ParameterDirection.Output

rdr = cmd.ExecuteReader()
While (rdr.Read())
REM do something with the values
End While

rdr.Close()
End Using
End Sub

Vea también
Parámetros REF CURSOR de Oracle
Información general sobre ADO.NET
Recuperar datos desde varios parámetros REF
CURSOR utilizando un objeto OracleDataReader
21/03/2020 • 2 minutes to read • Edit Online

En este ejemplo de Microsoft Visual Basic se ejecuta un procedimiento almacenado PL/SQL que devuelve dos
parámetros REF CURSOR, y se leen los valores mediante un OracleDataReader.

Private Sub Button1_Click( _


ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Button1.Click

Dim connString As New String( _


"Data Source=Oracle9i;User ID=scott;Password=tiger;")
Using conn As New OracleConnection(connString)
Dim cmd As New OracleCommand()
Dim rdr As OracleDataReader

conn.Open()
cmd.Connection = conn
cmd.CommandText = "CURSPKG.OPEN_TWO_CURSORS"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New OracleParameter( _
"EMPCURSOR", OracleType.Cursor)).Direction = _
ParameterDirection.Output
cmd.Parameters.Add(New OracleParameter(_
"DEPTCURSOR", OracleType.Cursor)).Direction = _
ParameterDirection.Output

rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While (rdr.Read())
REM do something with the values from the EMP table
End While

rdr.NextResult()
While (rdr.Read())
REM do something with the values from the DEPT table
End While
rdr.Close()
End Using
End Sub

Consulte también
Parámetros REF CURSOR de Oracle
Información general de ADO.NET
Rellenar un conjunto de datos utilizando uno o varios
parámetros REF CURSOR
23/10/2019 • 2 minutes to read • Edit Online

En este ejemplo de Microsoft Visual Basic se ejecuta un procedimiento almacenado PL/SQL que devuelve dos
parámetros REF CURSOR y se rellena un DataSet con las filas que se devuelven.

Private Sub Button1_Click(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles Button1.Click

Dim connString As New String(_


"Data Source=Oracle9i;User ID=scott;Password=tiger;")
Dim ds As New DataSet()
Using conn As New OracleConnection(connString)
Dim cmd As New OracleCommand()

cmd.Connection = conn
cmd.CommandText = "CURSPKG.OPEN_TWO_CURSORS"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New OracleParameter( _
"EMPCURSOR", OracleType.Cursor)).Direction = _
ParameterDirection.Output
cmd.Parameters.Add(New OracleParameter( _
"DEPTCURSOR", OracleType.Cursor)).Direction = _
ParameterDirection.Output

Dim da As New OracleDataAdapter(cmd)


da.TableMappings.Add("Table", "Emp")
da.TableMappings.Add("Table1", "Dept")
da.Fill(ds)

ds.Relations.Add("EmpDept", ds.Tables("Dept").Columns("Deptno"), _
ds.Tables("Emp").Columns("Deptno"), False)

DataGrid1.DataSource = ds.Tables("Dept")
End Using

Vea también
Parámetros REF CURSOR de Oracle
Información general sobre ADO.NET
Tipos de Oracle
21/03/2020 • 2 minutes to read • Edit Online

El proveedor de datos .NET Framework para Oracle incluye varias estructuras que se pueden utilizar para trabajar
con tipos de datos de Oracle. Por ejemplo, OracleNumber y OracleString.

NOTE
Para ver una lista completa de estas estructuras, vea System.Data.OracleClient.

En los siguientes ejemplos con C#:


se crea una tabla de Oracle y se carga de datos.
se utiliza un OracleDataReader para tener acceso a los datos y se emplean varias estructuras OracleType
para mostrarlos.

Creación de una tabla de Oracle


En este ejemplo se crea una tabla de Oracle y se carga de datos. Este ejemplo debe ejecutarse antes que el ejemplo
siguiente.

public void Setup(string connectionString)


{
OracleConnection conn = new OracleConnection(connectionString);
try
{
conn.Open();
OracleCommand cmd = conn.CreateCommand();
cmd.CommandText ="CREATE TABLE OracleTypesTable " +
"(MyVarchar2 varchar2(3000),MyNumber number(28,4) " +
"PRIMARY KEY ,MyDate date, MyRaw raw(255))";
cmd.ExecuteNonQuery();
cmd.CommandText ="INSERT INTO OracleTypesTable VALUES " +
"( 'test', 2, to_date('2000-01-11 12:54:01','yyyy-mm-dd " +
"hh24:mi:ss'), '0001020304' )";
cmd.ExecuteNonQuery();
}
catch(Exception)
{
}
finally
{
conn.Close();
}
}

Recuperación de datos de la tabla de Oracle


En este ejemplo se utiliza un OracleDataReader para tener acceso a los datos y se utilizan varias estructuras
OracleType para mostrar los datos.
public void ReadOracleTypesExample(string connectionString)
{
OracleConnection myConnection =
new OracleConnection(connectionString);
myConnection.Open();
OracleCommand myCommand = myConnection.CreateCommand();

try
{
myCommand.CommandText = "SELECT * from OracleTypesTable";
OracleDataReader oracledatareader1 = myCommand.ExecuteReader();
oracledatareader1.Read();

//Using the oracle specific getters for each type is faster than
//using GetOracleValue.

//First column, MyVarchar2, is a VARCHAR2 data type in Oracle


//Server and maps to OracleString.
OracleString oraclestring1 =
oracledatareader1.GetOracleString(0);
Console.WriteLine("OracleString " + oraclestring1.ToString());

//Second column, MyNumber, is a NUMBER data type in Oracle Server


//and maps to OracleNumber.
OracleNumber oraclenumber1 =
oracledatareader1.GetOracleNumber(1);
Console.WriteLine("OracleNumber " + oraclenumber1.ToString());

//Third column, MyDate, is a DATA data type in Oracle Server


//and maps to OracleDateTime.
OracleDateTime oracledatetime1 =
oracledatareader1.GetOracleDateTime(2);
Console.WriteLine("OracleDateTime " + oracledatetime1.ToString());

//Fourth column, MyRaw, is a RAW data type in Oracle Server and


//maps to OracleBinary.
OracleBinary oraclebinary1 =
oracledatareader1.GetOracleBinary(3);
//Calling value on a null OracleBinary throws
//OracleNullValueException; therefore, check for a null value.
if (oraclebinary1.IsNull==false)
{
foreach(byte b in oraclebinary1.Value)
{
Console.WriteLine("byte " + b.ToString());
}
}
oracledatareader1.Close();
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
}

Consulte también
Oracle y ADO.NET
Información general de ADO.NET
Secuencias de Oracle
21/03/2020 • 5 minutes to read • Edit Online

El proveedor de datos .NET Framework para Oracle proporciona compatibilidad para recuperar los valores clave de
secuencia de Oracle que genera el servidor después de realizar inserciones con OracleDataAdapter.
SQL Server y Oracle admiten la creación de columnas de incremento automático que pueden designarse como
claves principales. Estos valores los genera el servidor cuando se agregan filas a una tabla. En SQL Server se
establece la propiedad Identity de una columna; en Oracle se crea una secuencia. La diferencia entre las columnas
de incremento automático de SQL Server y las secuencias de Oracle es la siguiente:
En SQL Server, marca una columna como columna de incremento automático y SQL Server genera de
forma automática nuevos valores para la columna cuando se inserta una nueva fila.
En Oracle, para generar nuevos valores en una columna de la tabla crea una secuencia, pero no existe
vínculo directo entre la secuencia y la tabla o la columna. Las secuencias de Oracle son objetos, de la misma
forma que las tablas o los procedimientos almacenados.
Cuando crea una secuencia en una base de datos de Oracle, puede definir su valor inicial y el incremento entre los
valores. También puede consultar si existen nuevos valores en la secuencia antes de enviar nuevas filas. Esto
implica que el código puede reconocer los valores clave de las nuevas filas antes de insertarlas en la base de datos.
Para obtener más información sobre la creación de columnas de incremento automático mediante SQL Server y
ADO.NET, vea Recuperar valores de identidad o numeración automática y Crear columnas de incremento
automático.

Ejemplo
En el siguiente ejemplo de C# se muestra cómo puede recuperar nuevos valores de secuencia de una base de datos
de Oracle. El ejemplo hace referencia a la secuencia de la consulta INSERT INTO utilizada para enviar nuevas filas y,
a continuación, devuelve el valor de secuencia que se genera mediante la cláusula RETURNING introducida en
Oracle10g. El ejemplo agrega una serie de filas nuevas pendientes en un objeto DataTable mediante el uso de la
funcionalidad de incremento automático de ADO.NET para generar "marcadores de posición" de valores clave
principales. Tenga en cuenta que el valor de incremento que genera ADO.NET para la nueva fila es sólo un
"marcador de posición". Esto significa que la base de datos puede generar valores distintos de los que genera
ADO.NET.
Antes de enviar las inserciones pendientes a la base de datos, el ejemplo muestra el contenido de las filas. A
continuación, el código crea un nuevo objeto OracleDataAdapter y establece sus propiedades InsertCommand y
UpdateBatchSize. El ejemplo también proporciona la lógica para devolver los valores que genera el servidor
mediante parámetros de salida. A continuación, ejecuta la actualización para enviar las filas pendientes y muestra el
contenido del objeto DataTable.

public void OracleSequence(String connectionString)


{
String insertString =
"INSERT INTO SequenceTest_Table (ID, OtherColumn)" +
"VALUES (SequenceTest_Sequence.NEXTVAL, :OtherColumn)" +
"RETURNING ID INTO :ID";

using (OracleConnection conn = new OracleConnection(connectionString))


{
//Open a connection.
conn.Open();
conn.Open();
OracleCommand cmd = conn.CreateCommand();

// Prepare the database.


cmd.CommandText = "DROP SEQUENCE SequenceTest_Sequence";
try { cmd.ExecuteNonQuery(); } catch { }

cmd.CommandText = "DROP TABLE SequenceTest_Table";


try { cmd.ExecuteNonQuery(); } catch { }

cmd.CommandText = "CREATE TABLE SequenceTest_Table " +


"(ID int PRIMARY KEY, OtherColumn varchar(255))";
cmd.ExecuteNonQuery();

cmd.CommandText = "CREATE SEQUENCE SequenceTest_Sequence " +


"START WITH 100 INCREMENT BY 5";
cmd.ExecuteNonQuery();

DataTable testTable = new DataTable();


DataColumn column = testTable.Columns.Add("ID", typeof(int));
column.AutoIncrement = true;
column.AutoIncrementSeed = -1;
column.AutoIncrementStep = -1;
testTable.PrimaryKey = new DataColumn[] { column };
testTable.Columns.Add("OtherColumn", typeof(string));
for (int rowCounter = 1; rowCounter <= 15; rowCounter++)
{
testTable.Rows.Add(null, "Row #" + rowCounter.ToString());
}

Console.WriteLine("Before Update => ");


foreach (DataRow row in testTable.Rows)
{
Console.WriteLine(" {0} - {1}", row["ID"], row["OtherColumn"]);
}
Console.WriteLine();

cmd.CommandText =
"SELECT ID, OtherColumn FROM SequenceTest_Table";
OracleDataAdapter da = new OracleDataAdapter(cmd);
da.InsertCommand = new OracleCommand(insertString, conn);
da.InsertCommand.Parameters.Add(":ID", OracleType.Int32, 0, "ID");
da.InsertCommand.Parameters[0].Direction = ParameterDirection.Output;
da.InsertCommand.Parameters.Add(":OtherColumn", OracleType.VarChar, 255, "OtherColumn");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
da.UpdateBatchSize = 10;

da.Update(testTable);

Console.WriteLine("After Update => ");


foreach (DataRow row in testTable.Rows)
{
Console.WriteLine(" {0} - {1}", row["ID"], row["OtherColumn"]);
}
// Close the connection.
conn.Close();
}
}

Consulte también
Oracle y ADO.NET
Información general de ADO.NET
Asignaciones de tipos de datos de Oracle
23/10/2019 • 6 minutes to read • Edit Online

En la siguiente tabla se enumeran los tipos de datos de Oracle y sus asignaciones al OracleDataReader.

T IP O DE DATO S DE . N ET T IP O DE DATO S
F RA M EW O RK DEVUELTO O RA C L EC L IEN T DEVUELTO
POR POR
O RA C L EDATA REA DER. GET VA O RA C L EDATA REA DER. GETO R
T IP O DE DATO S DE O RA C L E L UE A C L EVA L UE C O M EN TA RIO S

SOBRE Byte[] OracleBFile

BLOB Byte[] OracleLob

CHAR String OracleString

CLOB String OracleLob

DATE DateTime OracleDateTime

FLOAT Decimal OracleNumber Este tipo de datos es un


alias para el tipo de datos
Number y está diseñado
para que OracleDataReader
devuelva un valor System.
decimal o OracleNumber
en lugar de un valor de
punto flotante. El uso del
tipo de datos de .NET
Framework puede ocasionar
un desbordamiento.

INTEGER Decimal OracleNumber Este tipo de datos es un


alias para el tipo de datos
Number (38) y está
diseñado para que
OracleDataReader devuelva
un valor System. decimal
o OracleNumber en lugar de
un valor entero. El uso del
tipo de datos de .NET
Framework puede ocasionar
un desbordamiento.

INTERVALO AÑO A MES Int32 OracleMonthSpan

INTERVALO DE DÍA A TimeSpan OracleTimeSpan


SEGUNDO

LONG String OracleString

LONG RAW Byte[] OracleBinary


T IP O DE DATO S DE . N ET T IP O DE DATO S
F RA M EW O RK DEVUELTO O RA C L EC L IEN T DEVUELTO
POR POR
O RA C L EDATA REA DER. GET VA O RA C L EDATA REA DER. GETO R
T IP O DE DATO S DE O RA C L E L UE A C L EVA L UE C O M EN TA RIO S

NCHAR String OracleString

NCLOB String OracleLob

NÚMEROS Decimal OracleNumber El uso del tipo de datos de


.NET Framework puede
ocasionar un
desbordamiento.

NVARCHAR2 String OracleString

RAW Byte[] OracleBinary

REF CURSOR El tipo de datos ref cursor


de Oracle no es compatible
OracleDataReader con el
objeto.

ROWID String OracleString

INDICACIONES DateTime OracleDateTime

MARCA DE TIEMPO CON DateTime OracleDateTime


ZONA HORARIA LOCAL

MARCA DE TIEMPO CON DateTime OracleDateTime


ZONA HORARIA

ENTERO SIN SIGNO Número OracleNumber Este tipo de datos es un


alias para el tipo de datos
Number (38) y está
diseñado para que
OracleDataReader devuelva
un valor System. decimal
o OracleNumber en lugar de
un valor entero sin signo. El
uso del tipo de datos de
.NET Framework puede
ocasionar un
desbordamiento.

VARCHAR2 String OracleString

En la tabla siguiente se enumeran los tipos de datos de Oracle y los tipos de datos .NET Framework
OracleType(System. Data. DbType y) que se van a usar al enlazarlos como parámetros.

EN UM ERA C IÓ N
EN UM ERA C IÓ N DBT Y P E O RA C L ET Y P E PA RA
PA RA EN L A Z A R C O M O UN EN L A Z A R C O M O UN
T IP O DE DATO S DE O RA C L E PA RÁ M ET RO PA RÁ M ET RO C O M EN TA RIO S
EN UM ERA C IÓ N
EN UM ERA C IÓ N DBT Y P E O RA C L ET Y P E PA RA
PA RA EN L A Z A R C O M O UN EN L A Z A R C O M O UN
T IP O DE DATO S DE O RA C L E PA RÁ M ET RO PA RÁ M ET RO C O M EN TA RIO S

SOBRE BFile Oracle solo permite enlazar


un BFILE como parámetro
BFILE . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor noBFILE ,
como Byte [] o
OracleBinary.

BLOB Blob Oracle solo permite enlazar


un BLOB como parámetro
de BLOB . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor que no
esBLOB , como Byte [] o
OracleBinary.

CHAR AnsiStringFixedLength Char

CLOB Clob Oracle solo permite enlazar


un CLOB como parámetro
CLOB . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor que no
esCLOB , como System.
String o OracleString.

DATE DateTime DateTime

FLOAT Single, Double, decimal Float, Double, número Sizedetermina los datos
System. Data. DBType y
OracleType.

INTEGER SByte, Int16, Int32, SByte, Int16, Int32, Sizedetermina los datos
Int64, Decimal Number System. Data. DBType y
OracleType.

INTERVALO AÑO A MES Int32 Inter valYearToMonth OracleType solo está


disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

INTERVALO DE DÍA A Objeto Inter valDayToSecond OracleType solo está


SEGUNDO disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

LONG AnsiString LongVarChar


EN UM ERA C IÓ N
EN UM ERA C IÓ N DBT Y P E O RA C L ET Y P E PA RA
PA RA EN L A Z A R C O M O UN EN L A Z A R C O M O UN
T IP O DE DATO S DE O RA C L E PA RÁ M ET RO PA RÁ M ET RO C O M EN TA RIO S

LONG RAW Binar y LongRaw

NCHAR StringFixedLength NChar

NCLOB NClob Oracle solo permite enlazar


un NCLOB como parámetro
NCLOB . El proveedor de
datos .NET para Oracle no
construye uno
automáticamente si intenta
enlazar un valor que no
seaNCLOB , como System.
String o OracleString.

NÚMEROS VarNumeric Número

NVARCHAR2 String NVarChar

RAW Binar y Socket

REF CURSOR Cursor Para obtener más


información, vea cursores
REF cursor de Oracle.

ROWID AnsiString Rowid

INDICACIONES DateTime Marca de tiempo OracleType solo está


disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

MARCA DE TIEMPO CON DateTime TimestampLocal OracleType solo está


ZONA HORARIA LOCAL disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

MARCA DE TIEMPO CON DateTime TimestampWithTz OracleType solo está


ZONA HORARIA disponible cuando se usa el
software de cliente y
servidor de Oracle 9i.

ENTERO SIN SIGNO Byte, UInt16, UInt32, Byte, UInt16, Uint32, Sizedetermina los datos
UInt64, Decimal número System. Data. DBType y
OracleType.

VARCHAR2 AnsiString VarChar

Los Valuevaloresde ParameterDirection , Output y ReturnValue que usa la propiedad del


objetoson.NETFrameworktiposdedatos,amenosqueelvalordeentradaseauntipodedatosdeOracle(paraOracleParame
ter ejemplo, OracleNumber o OracleString). Esto no se aplica a los tipos de datos ref cursor , BFILE o LOB .

Vea también
Oracle y ADO.NET
Información general sobre ADO.NET
Transacciones distribuidas de Oracle
29/10/2019 • 2 minutes to read • Edit Online

El objeto OracleConnection se inscribe automáticamente en una transacción distribuida si determina que hay una
transacción activa. La inscripción automática en transacciones tiene lugar cuando se abre la conexión o se recupera
del grupo de conexiones. Para deshabilitar la inscripción automática en transacciones existentes, especifique

Enlist=false

como un parámetro de cadena de conexión de una OracleConnection.

Vea también
Oracle y ADO.NET
Información general sobre ADO.NET
ADO.NET Entity Framework
23/10/2019 • 2 minutes to read • Edit Online

El docs.microsoft.com/ef/ sitio ahora es la ubicación principal para el contenido de Entity Framework.


El contenido de este tema ahora está disponible en la página siguiente: Introducción a Entity
Framework.
SQL Server y ADO.NET
05/02/2020 • 2 minutes to read • Edit Online

En esta sección se describen características y comportamientos específicos del proveedor de datos .NET
Framework para SQL Server (System.Data.SqlClient).
System.Data.SqlClient proporciona acceso a versiones de SQL Server, que encapsula los protocolos específicos de
base de datos. La funcionalidad del proveedor de datos se ha diseñado para que sea similar a la de los
proveedores de datos .NET Framework para OLE DB, ODBC y Oracle. System.Data.SqlClient incluye un analizador
de flujo TDS para comunicarse directamente con SQL Server.

NOTE
Para utilizar el proveedor de datos .NET Framework para SQL Server, la aplicación debe hacer referencia al espacio de
nombres System.Data.SqlClient.

Esta sección
Seguridad de SQL Server
Proporciona una introducción general a las características de seguridad de SQL Server y casos de creación de
aplicaciones ADO.NET seguras dirigidas a SQL Server.
Tipos de datos de SQL Server y ADO.NET
Describe cómo trabajar con tipos de datos de SQL Server y cómo interactúan con los tipos de datos de .NET
Framework.
Datos binarios y datos de valores grandes de SQL Server
Describe cómo trabajar con datos de valores grandes en SQL Server.
Operaciones de datos de SQL Server en ADO.NET
Describe cómo trabajar con datos XML en SQL Server. Contiene secciones acerca de operaciones de copia masiva,
MARS, operaciones asincrónicas y parámetros con valores de tabla.
Características de SQL Server y ADO.NET
Describe las características de SQL Server que resultan de utilidad para los desarrolladores de aplicaciones
ADO.NET.
LINQ to SQL
Describe los bloques de creación, procesos y técnicas básicos para crear aplicaciones LINQ to SQL.
Para obtener documentación completa del Motor de bases de datos de SQL Server, busque la versión de SQL
Server que utiliza en los Libros en pantalla de SQL Server.
Libros en pantalla de SQL Server

Vea también
Proteger aplicaciones de ADO.NET
Asignaciones de tipos de datos en ADO.NET
Objetos DataSet, DataTable y DataView
Recuperar y modificar datos en ADO.NET
Información general sobre ADO.NET
Objetos DataSet, DataTable y DataView
23/10/2019 • 5 minutes to read • Edit Online

El DataSet de ADO.NET es una representación de datos residente en memoria que proporciona un modelo de
programación relacional coherente independientemente del origen de datos que contiene. Un DataSet
representa un conjunto completo de datos, incluyendo las tablas que contienen, ordenan y restringen los datos,
así como las relaciones entre las tablas.
Hay varias maneras de trabajar con un DataSet, que se pueden aplicar de forma independiente o
conjuntamente. Puede:
Crear mediante programación una DataTable, DataRelation y una Constraint en un DataSet y rellenar las
tablas con datos.
Llenar el DataSet con tablas de datos de un origen de datos relacional existente mediante DataAdapter .
Cargar y hacer persistente el contenido de DataSet mediante XML. Para obtener más información, vea
Using XML in a DataSet (Usar XML en un conjunto de datos).
También se puede transportar un DataSet fuertemente tipado mediante un servicio Web XML. El diseño del
DataSet lo convierte en idóneo para el transporte de datos mediante servicios Web XML. Para obtener
información general sobre servicios Web XML, vea Información general de servicios Web XML. Para obtener un
ejemplo sobre cómo usar un objeto DataSet desde un servicio Web XML, vea Consuming a DataSet from an
XML Web Service (Usar un conjunto de datos desde un servicio Web XML).

En esta sección
Crear un objeto DataSet
Describe la sintaxis para crear una instancia de DataSet.
Agregar un objeto DataTable a un objeto DataSet
Describe cómo crear tablas y columnas y cómo agregarlas a un DataSet.
Agregar objetos DataRelation
Describe cómo se crean las relaciones entre tablas en un DataSet.
Navegar por objetos DataRelation
Describe cómo se utilizan las relaciones entre tablas en un DataSet para devolver la filas secundarias o
primarias de una relación primaria-secundaria.
Combinar contenido de DataSet
Describe cómo se fusiona mediante combinación el contenido de una matriz de DataSet, DataTable o DataRow
con otro DataSet.
Copiar el contenido de DataSet
Describe cómo se crea una copia de un DataSet que puede contener datos de esquema y datos especificados.
Controlar eventos de DataSet
Describe los eventos de un DataSet y cómo utilizarlos.
Objetos DataSet con tipo
Explica lo que es un DataSet con información de tipos y cómo crearlo y utilizarlo.
Objetos DataTable
Describe cómo se crea una DataTable, cómo se define el esquema y cómo se manipulan los datos.
Objetos DataTableReader
Describe cómo crear y utilizar un objeto DataTableReader.
Objetos DataView
Describe cómo se crean DataViews y cómo se trabaja con ellas, así como con eventos DataView.
Usar XML en un conjunto de datos
Describe cómo interactúa el DataSet con XML como origen de datos, incluyendo cómo cargar y hacer
persistente el contenido de un DataSet como datos XML.
Usar un conjunto de datos desde un servicio Web XML
Describe cómo crear un servicio Web XML que utilice un DataSet para transportar los datos.

Secciones relacionadas
Novedades de ADO.NET
Presenta características nuevas en ADO.NET.
Información general sobre ADO.NET
Proporciona una introducción al diseño y a los componentes de ADO.NET.
Rellenar un conjunto de datos desde un objeto DataAdapter
Describe cómo se carga un objeto DataSet con datos desde un origen de datos.
Actualizar orígenes de datos con objetos DataAdapter
Describe cómo se resuelven los cambios relacionados con los datos de un DataSet en el origen de datos.
Agregar restricciones existentes a un conjunto de datos
Describe cómo se rellena un objeto DataSet con información de clave principal de un origen de datos.

Vea también
ADO.NET
Información general sobre ADO.NET

Вам также может понравиться