ADO.Net
ADO.Net
ADO.Net
ADO.NET
What's New in ADO.NET
ADO.NET Overview
ADO.NET Architecture
ADO.NET Technology Options and Guidelines
LINQ and ADO.NET
.NET Framework Data Providers
ADO.NET DataSets
Side-by-Side Execution in ADO.NET
ADO.NET Code Examples
Securing ADO.NET Applications
Security Overview
Secure Data Access
Secure Client Applications
Code Access Security and ADO.NET
Privacy and Data Security
Data Type Mappings in ADO.NET
SQL Server Data Type Mappings
OLE DB Data Type Mappings
ODBC Data Type Mappings
Oracle Data Type Mappings
Floating-Point Numbers
Retrieving and Modifying Data in ADO.NET
Connecting to a Data Source
Establishing the Connection
Connection Events
Connection Strings
Connection String Builders
Connection Strings and Configuration Files
Connection String Syntax
Protecting Connection Information
Connection Pooling
SQL Server Connection Pooling (ADO.NET)
OLE DB, ODBC, and Oracle Connection Pooling
Commands and Parameters
Executing a Command
Configuring Parameters and Parameter Data Types
Generating Commands with CommandBuilders
Obtaining a Single Value from a Database
Using Commands to Modify Data
Updating Data in a Data Source
Performing Catalog Operations
DataAdapters and DataReaders
Retrieving Data Using a DataReader
Populating a DataSet from a DataAdapter
DataAdapter Parameters
Adding Existing Constraints to a DataSet
DataAdapter DataTable and DataColumn Mappings
Paging Through a Query Result
Updating Data Sources with DataAdapters
Handling DataAdapter Events
Performing Batch Operations Using DataAdapters
Transactions and Concurrency
Local Transactions
Distributed Transactions
System.Transactions Integration with SQL Server
Optimistic Concurrency
Retrieving Identity or Autonumber Values
Retrieving Binary Data
Modifying Data with Stored Procedures
Retrieving Database Schema Information
GetSchema and Schema Collections
GetSchema and Schema Collections
Schema Restrictions
Common Schema Collections
SQL Server Schema Collections
Oracle Schema Collections
ODBC Schema Collections
OLE DB Schema Collections
DbProviderFactories
Factory Model Overview
Obtaining a DbProviderFactory
DbConnection, DbCommand and DbException
Modifying Data with a DbDataAdapter
Data Tracing in ADO.NET
Performance Counters
Asynchronous Programming
SqlClient Streaming Support
LINQ to DataSet
Getting Started
LINQ to DataSet Overview
Loading Data Into a DataSet
Downloading Sample Databases
How to: Create a LINQ to DataSet Project In Visual Studio
Programming Guide
Queries in LINQ to DataSet
Querying DataSets
Single-Table Queries
Cross-Table Queries
Querying Typed DataSets
Comparing DataRows
Creating a DataTable From a Query
How to: Implement CopyToDataTable<T> Where the Generic Type T Is Not a
DataRow
Generic Field and SetField Methods
Data Binding and LINQ to DataSet
Creating a DataView Object
Filtering with DataView
Sorting with DataView
Querying the DataRowView Collection in a DataView
DataView Performance
How to: Bind a DataView Object to a Windows Forms DataGridView Control
Debugging LINQ to DataSet Queries
Security
LINQ to DataSet Examples
Query Expression Examples
Projection
Restriction
Partitioning
Ordering
Element Operators
Aggregate Operators
Join Operators
Method-Based Query Examples
Projection
Partitioning
Ordering
Set Operators
Conversion Operators
Element Operators
Aggregate Operators
Join
DataSet-Specific Operator Examples
Entity Data Model
Entity Data Model Key Concepts
Entity Data Model: Namespaces
Entity Data Model: Primitive Data Types
Entity Data Model: Inheritance
association end
association end multiplicity
association set
association set end
association type
complex type
entity container
entity key
entity set
entity type
facet
foreign key property
model-declared function
model-defined function
navigation property
property
referential integrity constraint
Oracle and ADO.NET
System Requirements
Oracle BFILEs
Oracle LOBs
Oracle REF CURSORs
REF CURSOR Examples
REF CURSOR Parameters in an OracleDataReader
Retrieving Data from Multiple REF CURSORs Using an OracleDataReader
Filling a DataSet Using One or More REF CURSORs
OracleTypes
Oracle Sequences
Oracle Data Type Mappings
Oracle Distributed Transactions
ADO.NET Entity Framework
SQL Server and ADO.NET
DataSets, DataTables, and DataViews
ADO.NET
4/8/2019 • 2 minutes to read • Edit Online
ADO.NET is a set of classes that expose data access services for .NET Framework programmers. ADO.NET
provides a rich set of components for creating distributed, data-sharing applications. It is an integral part of the
.NET Framework, providing access to relational, XML, and application data. ADO.NET supports a variety of
development needs, including the creation of front-end database clients and middle-tier business objects used by
applications, tools, languages, or Internet browsers.
In This Section
What's New in ADO.NET
Introduces features that are new in ADO.NET.
ADO.NET Overview
Provides an introduction to the design and components of ADO.NET.
Entity Framework
Describes how to create applications using the Entity Framework.
Securing ADO.NET Applications
Describes secure coding practices when using ADO.NET.
Data Type Mappings in ADO.NET
Describes data type mappings between .NET Framework data types and the .NET Framework data providers.
DataSets, DataTables, and DataViews
Describes how to create and use DataSets , typed DataSets , DataTables , and DataViews .
LINQ to DataSet
Provides information about LINQ to DataSet, including programming examples.
Retrieving and Modifying Data in ADO.NET
Describes how to connect to a data source and how to retrieve and modify data using Commands , DataReaders and
DataAdapters .
Related Sections
Language-Integrated Query (LINQ ) - C#
Provides links to LINQ topics and samples using C#.
Language-Integrated Query (LINQ ) - Visual Basic
Provides links to LINQ topics and samples using Visual Basic.
WCF Data Services 4.5
Describes how to use WCF Data Services to deploy data services on the Web or an intranet that implement the
Open Data Protocol (OData).
.NET Framework Development Guide
Provides links to information about standard development tasks in the .NET Framework.
Samples and tutorials
Provides a list of .NET samples and tutorials.
See also
Accessing data in Visual Studio
ADO.NET Managed Providers and DataSet Developer Center
What's New in ADO.NET
5/30/2019 • 2 minutes to read • Edit Online
The following features are new in ADO.NET in the .NET Framework 4.5.
SqlClient provides additional support for sparse columns, a feature that was added in SQL Server 2008. If
your application already accesses data in a table that uses sparse columns, you should see an increase in
performance. The IsColumnSet column of GetSchemaTable indicates if a column is a sparse column that is
a member of a column set. GetSchema indicates if a column is a sparse column (see SQL Server Schema
Collections for more information). For more information about sparse columns, see Using Sparse Columns.
The assembly Microsoft.SqlServer.Types.dll, which contains the spatial data types, has been upgraded from
version 10.0 to version 11.0. Applications that reference this assembly may fail. For more information, see
Breaking Changes to Database Engine Features.
See also
ADO.NET
ADO.NET Overview
SQL Server and ADO.NET
What's New in WCF Data Services 5.0
ADO.NET Managed Providers and DataSet Developer Center
ADO.NET Overview
5/18/2019 • 2 minutes to read • Edit Online
ADO.NET provides consistent access to data sources such as SQL Server and XML, and to data sources exposed
through OLE DB and ODBC. Data-sharing consumer applications can use ADO.NET to connect to these data
sources and retrieve, handle, and update the data that they contain.
ADO.NET separates data access from data manipulation into discrete components that can be used separately or
in tandem. ADO.NET includes .NET Framework data providers for connecting to a database, executing
commands, and retrieving results. Those results are either processed directly, placed in an ADO.NET DataSet
object in order to be exposed to the user in an ad hoc manner, combined with data from multiple sources, or
passed between tiers. The DataSet object can also be used independently of a .NET Framework data provider to
manage data local to the application or sourced from XML.
The ADO.NET classes are found in System.Data.dll, and are integrated with the XML classes found in
System.Xml.dll. For sample code that connects to a database, retrieves data from it, and then displays that data in
a console window, see ADO.NET Code Examples.
ADO.NET provides functionality to developers who write managed code similar to the functionality provided to
native component object model (COM ) developers by ActiveX Data Objects (ADO ). We recommend that you use
ADO.NET, not ADO, for accessing data in your .NET applications.
ADO.NET provides the most direct method of data access within the .NET Framework. For a higher-level
abstraction that allows applications to work against a conceptual model instead of the underlying storage model,
see the ADO.NET Entity Framework.
Privacy Statement: The System.Data.dll, System.Data.Design.dll, System.Data.OracleClient.dll,
System.Data.SqlXml.dll, System.Data.Linq.dll, System.Data.SqlServerCe.dll, and
System.Data.DataSetExtensions.dll assemblies do not distinguish between a user's private data and non-private
data. These assemblies do not collect, store, or transport any user's private data. However, third-party
applications might collect, store, or transport a user's private data using these assemblies.
In This Section
ADO.NET Architecture
Provides an overview of the architecture and components of ADO.NET.
ADO.NET Technology Options and Guidelines
Describes the products and technologies included with the Entity Data Platform.
LINQ and ADO.NET
Describes how Language-Integrated Query (LINQ ) is implemented in ADO.NET and provides links to relevant
topics.
.NET Framework Data Providers
Provides an overview of the design of the .NET Framework data provider and of the .NET Framework data
providers that are included with ADO.NET.
ADO.NET DataSets
Provides an overview of the DataSet design and components.
Side-by-Side Execution in ADO.NET
Discusses differences in ADO.NET versions and their effect on side-by-side execution and application
compatibility.
ADO.NET Code Examples
Provides code samples that retrieve data using the ADO.NET data providers.
Related Sections
What's New in ADO.NET
Introduces features that are new in ADO.NET.
Securing ADO.NET Applications
Describes secure coding practices when using ADO.NET.
Data Type Mappings in ADO.NET
Describes data type mappings between .NET Framework data types and the .NET Framework data providers.
Retrieving and Modifying Data in ADO.NET
Describes how to connect to a data source, retrieve data, and modify data. This includes DataReaders and
DataAdapters .
See also
ADO.NET
Accessing data in Visual Studio
ADO.NET Managed Providers and DataSet Developer Center
ADO.NET Architecture
6/20/2019 • 4 minutes to read • Edit Online
Data processing has traditionally relied primarily on a connection-based, two-tier model. As data processing
increasingly uses multi-tier architectures, programmers are switching to a disconnected approach to provide better
scalability for their applications.
ADO.NET Components
The two main components of ADO.NET for accessing and manipulating data are the .NET Framework data
providers and the DataSet.
.NET Framework Data Providers
The .NET Framework Data Providers are components that have been explicitly designed for data manipulation and
fast, forward-only, read-only access to data. The Connection object provides connectivity to a data source. The
Command object enables access to database commands to return data, modify data, run stored procedures, and
send or retrieve parameter information. The DataReader provides a high-performance stream of data from the
data source. Finally, the DataAdapter provides the bridge between the DataSet object and the data source. The
DataAdapter uses Command objects to execute SQL commands at the data source to both load the DataSet with
data and reconcile changes that were made to the data in the DataSet back to the data source. For more
information, see .NET Framework Data Providers and Retrieving and Modifying Data in ADO.NET.
The DataSet
The ADO.NET DataSet is explicitly designed for data access independent of any data source. As a result, it can be
used with multiple and differing data sources, used with XML data, or used to manage data local to the application.
The DataSet contains a collection of one or more DataTable objects consisting of rows and columns of data, and
also primary key, foreign key, constraint, and relation information about the data in the DataTable objects. For
more information, see DataSets, DataTables, and DataViews.
The following diagram illustrates the relationship between a .NET Framework data provider and a DataSet .
ADO.NET architecture
Choosing a DataReader or a DataSet
When you decide whether your application should use a DataReader (see Retrieving Data Using a DataReader) or
a DataSet (see DataSets, DataTables, and DataViews), consider the type of functionality that your application
requires. Use a DataSet to do the following:
Cache data locally in your application so that you can manipulate it. If you only need to read the results of a
query, the DataReader is the better choice.
Remote data between tiers or from an XML Web service.
Interact with data dynamically such as binding to a Windows Forms control or combining and relating data
from multiple sources.
Perform extensive processing on data without requiring an open connection to the data source, which frees
the connection to be used by other clients.
If you do not require the functionality provided by the DataSet , you can improve the performance of your
application by using the DataReader to return your data in a forward-only, read-only manner. Although the
DataAdapter uses the DataReader to fill the contents of a DataSet (see Populating a DataSet from a DataAdapter ),
by using the DataReader , you can boost performance because you will save memory that would be consumed by
the DataSet , and avoid the processing that is required to create and fill the contents of the DataSet .
LINQ to DataSet
LINQ to DataSet provides query capabilities and compile-time type checking over data cached in a DataSet object.
It allows you to write queries in one of the .NET Framework development language, such as C# or Visual Basic. For
more information, see LINQ to DataSet.
LINQ to SQL
LINQ to SQL supports queries against an object model that is mapped to the data structures of a relational
database without using an intermediate conceptual model. Each table is represented by a separate class, tightly
coupling the object model to the relational database schema. LINQ to SQL translates language-integrated queries
in the object model into Transact-SQL and sends them to the database for execution. When the database returns
the results, LINQ to SQL translates the results back into objects. For more information, see LINQ to SQL.
See also
ADO.NET Overview
ADO.NET Managed Providers and DataSet Developer Center
ADO.NET Technology Options and Guidelines
4/8/2019 • 3 minutes to read • Edit Online
The ADO.NET Data Platform is a multi-release strategy to decrease the amount of coding and maintenance
required for developers by enabling them to program against conceptual entity data models. This platform
includes the ADO.NET Entity Framework and related technologies.
Entity Framework
The ADO.NET Entity Framework is designed to enable developers to create data access applications by
programming against a conceptual application model instead of programming directly against a relational storage
schema. The goal is to decrease the amount of code and maintenance required for data-oriented applications. For
more information, see ADO.NET Entity Framework.
Entity Data Model (EDM )
An Entity Data Model (EDM ) is a design specification that defines application data as sets of entities and
relationships. Data in this model supports object-relational mapping and data programmability across application
boundaries.
Object Services
Object Services allows programmers to interact with the conceptual model through a set of common language
runtime (CLR ) classes. These classes can be automatically generated from the conceptual model or can be
developed independently to reflect the structure of the conceptual model. Object Services also provides
infrastructure support for the Entity Framework, including services such as state management, change tracking,
identity resolution, loading and navigating relationships, propagating object changes to database modifications,
and query building support for Entity SQL. For more information, see Object Services Overview (Entity
Framework).
LINQ to Entities
LINQ to Entities is a language-integrated query (LINQ ) implementation that allows developers to create strongly-
typed queries against the Entity Framework object context by using LINQ expressions and LINQ standard query
operators. LINQ to Entities allows developers to work against a conceptual model with a very flexible object-
relational mapping across Microsoft SQL Server and third-party databases. For more information, see LINQ to
Entities.
Entity SQL
Entity SQL is a text-based query language designed to interact with an Entity Data Model. Entity SQL is an SQL
dialect that contains constructs for querying in terms of higher-level modeling concepts, such as inheritance,
complex types, and explicit relationships. Developers can also use Entity SQL directly with Object Services. For
more information, see Entity SQL Language.
EntityClient
EntityClient is a new .NET Framework data provider used for interacting with an Entity Data Model. EntityClient
follows the .NET Framework data provider pattern of exposing EntityConnection and EntityCommand objects that
return an EntityDataReader. EntityClient works with the Entity SQL language, providing flexible mapping to
storage-specific data providers. For more information, see EntityClient Provider for the Entity Framework.
Entity Data Model Tools
The Entity Framework provides command-line tools, wizards, and designers to facilitate building EDM
applications. The EntityDataSource control supports data binding scenarios based on the EDM. The programming
surface of the EntityDataSource control is similar to other data source controls in Visual Studio. For more
information, see ADO.NET Entity Data Model Tools.
LINQ to SQL
LINQ to SQL is an object relational mapping (OR/M ) implementation that allows you to model a SQL Server
database by using .NET Framework classes. LINQ to SQL allows you to query your database by using LINQ, as
well as update, insert and delete data from it. LINQ to SQL supports transactions, views, and stored procedures,
providing an easy way to integrate data validation and business logic rules into your data model. You can use the
Object Relational Designer (O/R Designer) to model the entity classes and associations that are based on objects
in a database. For more information, see LINQ to SQL Tools in Visual Studio.
See also
ADO.NET Overview
What's New in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
LINQ and ADO.NET
6/21/2019 • 3 minutes to read • Edit Online
Today, many business developers must use two (or more) programming languages: a high-level language for the
business logic and presentation layers (such as Visual C# or Visual Basic), and a query language to interact with
the database (such as Transact-SQL ). This requires the developer to be proficient in several languages to be
effective, and also causes language mismatches in the development environment. For example, an application that
uses a data access API to execute a query against a database specifies the query as a string literal by using
quotation marks. This query string is un-readable to the compiler and is not checked for errors, such as invalid
syntax or whether the columns or rows it references actually exist. There is no type checking of the query
parameters and no IntelliSense support, either.
Language-Integrated Query (LINQ ) enables developers to form set-based queries in their application code,
without having to use a separate query language. You can write LINQ queries against various enumerable data
sources (that is, a data source that implements the IEnumerable interface), such as in-memory data structures,
XML documents, SQL databases, and DataSet objects. Although these enumerable data sources are implemented
in various ways, they all expose the same syntax and language constructs. Because queries can be formed in the
programming language itself, you do not have to use another query language that is embedded as string literals
that cannot be understood or verified by the compiler. Integrating queries into the programming language also
enables Visual Studio programmers to be more productive by providing compile-time type and syntax checking,
and IntelliSense . These features reduce the need for query debugging and error fixing.
Transferring data from SQL tables into objects in memory is often tedious and error-prone. The LINQ provider
implemented by LINQ to DataSet and LINQ to SQL converts the source data into IEnumerable-based object
collections. The programmer always views the data as an IEnumerable collection, both when you query and when
you update. Full IntelliSense support is provided for writing queries against those collections.
There are three separate ADO.NET Language-Integrated Query (LINQ ) technologies: LINQ to DataSet, LINQ to
SQL, and LINQ to Entities. LINQ to DataSet provides richer, optimized querying over the DataSet and LINQ to
SQL enables you to directly query SQL Server database schemas, and LINQ to Entities allows you to query an
Entity Data Model.
The following diagram provides an overview of how the ADO.NET LINQ technologies relate to high-level
programming languages and LINQ -enabled data sources.
For more information about LINQ, see Language Integrated Query (LINQ ).
The following sections provide more information about LINQ to DataSet, LINQ to SQL, and LINQ to Entities.
LINQ to DataSet
The DataSet is a key element of the disconnected programming model that ADO.NET is built on, and is widely
used. LINQ to DataSet enables developers to build richer query capabilities into DataSet by using the same query
formulation mechanism that is available for many other data sources. For more information, see LINQ to DataSet.
LINQ to SQL
LINQ to SQL is a useful tool for developers who do not require mapping to a conceptual model. By using LINQ to
SQL, you can use the LINQ programming model directly over existing database schema. LINQ to SQL enables
developers to generate .NET Framework classes that represent data. Rather than mapping to a conceptual data
model, these generated classes map directly to database tables, views, stored procedures, and user-defined
functions.
With LINQ to SQL, developers can write code directly against the storage schema using the same LINQ
programming pattern as in-memory collections and the DataSet, in addition to other data sources such as XML.
For more information, see LINQ to SQL.
LINQ to Entities
Most applications are currently written on top of relational databases. At some point, these applications will need
to interact with the data represented in a relational form. Database schemas are not always ideal for building
applications, and the conceptual models of application are not the same as the logical models of databases. The
Entity Data Model is a conceptual data model that can be used to model the data of a particular domain so that
applications can interact with data as objects. See ADO.NET Entity Framework for more information.
Through the Entity Data Model, relational data is exposed as objects in the .NET environment. This makes the
object layer an ideal target for LINQ support, allowing developers to formulate queries against the database from
the language used to build the business logic. This capability is known as LINQ to Entities. See LINQ to Entities
for more information.
See also
LINQ to DataSet
LINQ to SQL
LINQ to Entities
Language Integrated Query (LINQ )
ADO.NET Overview
.NET Framework Data Providers
5/18/2019 • 8 minutes to read • Edit Online
A .NET Framework data provider is used for connecting to a database, executing commands, and retrieving results.
Those results are either processed directly, placed in a DataSet in order to be exposed to the user as needed,
combined with data from multiple sources, or remoted between tiers. .NET Framework data providers are
lightweight, creating a minimal layer between the data source and code, increasing performance without
sacrificing functionality.
The following table lists the data providers that are included in the .NET Framework.
.NET Framework Data Provider for SQL Server Provides data access for Microsoft SQL Server. Uses the
System.Data.SqlClient namespace.
.NET Framework Data Provider for OLE DB For data sources exposed by using OLE DB. Uses the
System.Data.OleDb namespace.
.NET Framework Data Provider for ODBC For data sources exposed by using ODBC. Uses the
System.Data.Odbc namespace.
.NET Framework Data Provider for Oracle For Oracle data sources. The .NET Framework Data Provider
for Oracle supports Oracle client software version 8.1.7 and
later, and uses the System.Data.OracleClient namespace.
EntityClient Provider Provides data access for Entity Data Model (EDM)
applications. Uses the System.Data.EntityClient namespace.
.NET Framework Data Provider for SQL Server Compact 4.0. Provides data access for Microsoft SQL Server Compact 4.0.
Uses the System.Data.SqlServerCe namespace.
OBJECT DESCRIPTION
In addition to the core classes listed in the table earlier in this document, a .NET Framework data provider also
contains the classes listed in the following table.
OBJECT DESCRIPTION
Comparison of the .NET Framework Data Provider for SQL Server and the .NET Framework Data Provider for
OLE DB
The .NET Framework Data Provider for SQL Server classes are located in the System.Data.SqlClient namespace.
The .NET Framework Data Provider for SQL Server supports both local and distributed transactions. For
distributed transactions, the .NET Framework Data Provider for SQL Server, by default, automatically enlists in a
transaction and obtains transaction details from Windows Component Services or System.Transactions. For more
information, see Transactions and Concurrency.
The following code example shows how to include the System.Data.SqlClient namespace in your applications.
Imports System.Data.SqlClient
using System.Data.SqlClient;
DRIVER PROVIDER
NOTE
Using an Access (Jet) database as a data source for multithreaded applications, such as ASP.NET applications, is not
recommended. If you must use Jet as a data source for an ASP.NET application, realize that ASP.NET applications connecting
to an Access database can encounter connection problems.
The .NET Framework Data Provider for OLE DB does not support OLE DB version 2.5 interfaces. OLE DB
Providers that require support for OLE DB 2.5 interfaces will not function correctly with the .NET Framework Data
Provider for OLE DB. This includes the Microsoft OLE DB provider for Exchange and the Microsoft OLE DB
provider for Internet Publishing.
The .NET Framework Data Provider for OLE DB does not work with the OLE DB provider for ODBC (MSDASQL ).
To access an ODBC data source using ADO.NET, use the .NET Framework Data Provider for ODBC.
.NET Framework Data Provider for OLE DB classes are located in the System.Data.OleDb namespace. The
following code example shows how to include the System.Data.OleDb namespace in your applications.
Imports System.Data.OleDb
using System.Data.OleDb;
DRIVER
SQL Server
.NET Framework Data Provider for ODBC classes are located in the System.Data.Odbc namespace.
The following code example shows how to include the System.Data.Odbc namespace in your applications.
Imports System.Data.Odbc
using System.Data.Odbc;
NOTE
The .NET Framework Data Provider for ODBC requires MDAC 2.6 or a later version, and MDAC 2.8 SP1 is recommended.
You can download MDAC 2.8 SP1 from the Data Access and Storage Developer Center.
The following code example shows how to include the System.Data.OracleClient namespace in your applications.
Imports System.Data
Imports System.Data.OracleClient
using System.Data;
using System.Data.OracleClient;
PROVIDER NOTES
.NET Framework Data Provider for SQL Server Recommended for middle-tier applications that use Microsoft
SQL Server.
.NET Framework Data Provider for OLE DB For SQL Server, the .NET Framework Data Provider for SQL
Server is recommended instead of this provider.
.NET Framework Data Provider for ODBC Recommended for middle and single-tier applications that use
ODBC data sources.
.NET Framework Data Provider for Oracle Recommended for middle and single-tier applications that use
Oracle data sources.
EntityClient Provider
The EntityClient provider is used for accessing data based on an Entity Data Model (EDM ). Unlike the other .NET
Framework data providers, it does not interact directly with a data source. Instead, it uses Entity SQL to
communicate with the underlying data provider. For more information, see EntityClient Provider for the Entity
Framework.
See also
ADO.NET Overview
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
ADO.NET DataSets
5/18/2019 • 3 minutes to read • Edit Online
The DataSet object is central to supporting disconnected, distributed data scenarios with ADO.NET. The DataSet
is a memory-resident representation of data that provides a consistent relational programming model regardless
of the data source. It can be used with multiple and differing data sources, with XML data, or to manage data local
to the application. The DataSet represents a complete set of data, including related tables, constraints, and
relationships among the tables. The following illustration shows the DataSet object model.
The DataTableCollection
An ADO.NET DataSet contains a collection of zero or more tables represented by DataTable objects. The
DataTableCollection contains all the DataTable objects in a DataSet.
A DataTable is defined in the System.Data namespace and represents a single table of memory-resident data. It
contains a collection of columns represented by a DataColumnCollection, and constraints represented by a
ConstraintCollection, which together define the schema of the table. A DataTable also contains a collection of
rows represented by the DataRowCollection, which contains the data in the table. Along with its current state, a
DataRow retains both its current and original versions to identify changes to the values stored in the row.
The DataRelationCollection
A DataSet contains relationships in its DataRelationCollection object. A relationship, represented by the
DataRelation object, associates rows in one DataTable with rows in another DataTable. A relationship is
analogous to a join path that might exist between primary and foreign key columns in a relational database. A
DataRelation identifies matching columns in two tables of a DataSet.
Relationships enable navigation from one table to another in a DataSet. The essential elements of a
DataRelation are the name of the relationship, the name of the tables being related, and the related columns in
each table. Relationships can be built with more than one column per table by specifying an array of DataColumn
objects as the key columns. When you add a relationship to the DataRelationCollection, you can optionally add a
UniqueKeyConstraint and a ForeignKeyConstraint to enforce integrity constraints when changes are made to
related column values.
For more information, see Adding DataRelations.
XML
You can fill a DataSet from an XML stream or document. You can use the XML stream or document to supply to
the DataSet either data, schema information, or both. The information supplied from the XML stream or
document can be combined with existing data or schema information already present in the DataSet. For more
information, see Using XML in a DataSet.
ExtendedProperties
The DataSet, DataTable, and DataColumn all have an ExtendedProperties property. ExtendedProperties is
a PropertyCollection where you can place custom information, such as the SELECT statement that was used to
generate the result set, or the time when the data was generated. The ExtendedProperties collection is persisted
with the schema information for the DataSet.
LINQ to DataSet
LINQ to DataSet provides language-integrated querying capabilities for disconnected data stored in a DataSet.
LINQ to DataSet uses standard LINQ syntax and provides compile-time syntax checking, static typing, and
IntelliSense support when you are using the Visual Studio IDE.
For more information, see LINQ to DataSet.
See also
ADO.NET Overview
DataSets, DataTables, and DataViews
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Side-by-Side Execution in ADO.NET
5/18/2019 • 5 minutes to read • Edit Online
Side-by-side execution in the .NET Framework is the ability to execute an application on a computer that has
multiple versions of the .NET Framework installed, exclusively using the version for which the application was
compiled. For detailed information about configuring side-by-side execution, see Side-by-Side Execution.
An application compiled by using one version of the .NET Framework can run on a different version of the .NET
Framework. However, we recommend that you compile a version of the application for each installed version of
the .NET Framework, and run them separately. In either scenario, you should be aware of changes in ADO.NET
between releases that can affect the forward compatibility or backward compatibility of your application.
SqlCommand Execution
Starting with the .NET Framework version 1.1, the way that ExecuteReader executes commands at the data source
was changed.
In the .NET Framework version 1.0, ExecuteReader executed all commands in the context of the sp_executesql
stored procedure. As a result, commands that affect the state of the connection (for example, SET NOCOUNT ON ),
only apply to the execution of the current command. The state of the connection is not modified for any
subsequent commands executed while the connection is open.
In the .NET Framework version 1.1 and later, ExecuteReader only executes a command in the context of the
sp_executesql stored procedure if the command contains parameters, which provides a performance benefit. As a
result, if a command affecting the state of the connection is included in a non-parameterized command, it modifies
the state of the connection for all subsequent commands executed while the connection is open.
Consider the following batch of commands executed in a call to ExecuteReader.
In the .NET Framework version 1.1 and later, NOCOUNT will remain ON for any subsequent commands executed
while the connection is open. In the .NET Framework version 1.0, NOCOUNT is only ON for the current command
execution.
This change can affect both the forward and backward compatibility of your application if you depend on the
behavior of ExecuteReader for either version of the .NET Framework.
For applications that run on both earlier and later versions of the .NET Framework, you can write your code to
make sure that the behavior is the same regardless of the version you are running on. If you want to make sure
that a command modifies the state of the connection for all subsequent commands, we recommend that you
execute your command using ExecuteNonQuery. If you want to make sure that a command does not modify the
connection for all subsequent commands, we recommend that you include the commands to reset the state of the
connection in your command. For example:
The code listings in this topic demonstrate how to retrieve data from a database by using the following ADO.NET
technologies:
ADO.NET data providers:
SqlClient ( System.Data.SqlClient )
OleDb ( System.Data.OleDb )
Odbc ( System.Data.Odbc )
OracleClient ( System.Data.OracleClient )
ADO.NET Entity Framework:
LINQ to Entities
Typed ObjectQuery
EntityClient ( System.Data.EntityClient )
LINQ to SQL
class Program
{
static void Main()
{
string connectionString =
"Data Source=(local);Initial Catalog=Northwind;"
+ "Integrated Security=true";
Imports System
Imports System.Data
Imports System.Data.SqlClient
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Using
End Sub
End Class
OleDb
The code in this example assumes that you can connect to the Microsoft Access Northwind sample database. The
code creates a OleDbCommand to select rows from the Products table, adding a OleDbParameter to restrict the
results to rows with a UnitPrice greater than the specified parameter value, in this case 5. The OleDbConnection is
opened inside of a using block, which ensures that resources are closed and disposed when the code exits. The
code executes the command by using a OleDbDataReader, and displays the results in the console window.
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=;";
Imports System
Imports System.Data
Imports System.Data.OleDb
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Using
End Sub
End Class
Odbc
The code in this example assumes that you can connect to the Microsoft Access Northwind sample database. The
code creates a OdbcCommand to select rows from the Products table, adding a OdbcParameter to restrict the
results to rows with a UnitPrice greater than the specified parameter value, in this case 5. The OdbcConnection is
opened inside a using block, which ensures that resources are closed and disposed when the code exits. The code
executes the command by using a OdbcDataReader, and displays the results in the console window.
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=;";
Imports System
Imports System.Data
Imports System.Data.Odbc
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Using
End Sub
End Class
OracleClient
The code in this example assumes a connection to DEMO.CUSTOMER on an Oracle server. You must also add a
reference to the System.Data.OracleClient.dll. The code returns the data in an 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();
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
Imports System.Data
Imports System.Data.OracleClient
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Using
End Sub
End Class
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
};
Option Explicit On
Option Strict On
Imports System
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 _
}
Typed ObjectQuery
The code in this example uses an ObjectQuery<T> to return data as Categories objects. For more information, see
Object Queries.
using System;
using System.Data.Objects;
using NorthwindModel;
class ObjectQuerySample
{
public static void ExecuteQuery()
{
using (NorthwindEntities context = new NorthwindEntities())
{
ObjectQuery<Categories> categoryQuery = context.Categories;
Option Explicit On
Option Strict On
Imports System
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
EntityClient
The code in this example uses an EntityCommand to execute an Entity SQL query. This query returns a list of
records that represent instances of the Categories entity type. An EntityDataReader is used to access data records
in the result set. For more information, see EntityClient Provider for the 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";
Imports System
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"
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 to SQL
The code in this example uses a LINQ query to return data as Categories objects, which are projected as an
anonymous type that contains only the CategoryID and CategoryName properties. This example is based on the
Northwind data context. For more information, see Getting Started.
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
};
Option Explicit On
Option Strict On
Imports System
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 _
}
Writing a secure ADO.NET application involves more than avoiding common coding pitfalls such as not
validating user input. An application that accesses data has many potential points of failure that an attacker can
exploit to retrieve, manipulate, or destroy sensitive data. It is therefore important to understand all aspects of
security, from the process of threat modeling during the design phase of your application, to its eventual
deployment and ongoing maintenance.
The .NET Framework provides many useful classes, services, and tools for securing and administering database
applications. The common language runtime (CLR ) provides a type-safe environment for code to run in, with
code access security (CAS ) to restrict further the permissions of managed code. Following secure data access
coding practices limits the damage that can be inflicted by a potential attacker.
Writing secure code does not guard against self-inflicted security holes when working with unmanaged resources
such as databases. Most server databases, such as SQL Server, have their own security systems, which enhance
security when implemented correctly. However, even a data source with a robust security system can be
victimized in an attack if it is not configured appropriately.
In This Section
Security Overview
Provides recommendations for designing secure ADO.NET applications.
Secure Data Access
Describes how to work with data from a secured data source.
Secure Client Applications
Describes security considerations for client applications.
Code Access Security and ADO.NET
Describes how CAS can help protect ADO.NET code. Also discusses how to work with partial trust.
Privacy and Data Security
Describes encryption options for ADO.NET applications.
Related Sections
SQL Server Security
Describes SQL Server security features from a developer's perspective.
Security Considerations
Describes security for Entity Framework applications.
Security
Contains links to topics describing all aspects of security in .NET.
Security Tools
.NET Framework tools for securing and administering security policy.
Resources for Creating Secure Applications
Provides links to topics for creating secure applications.
Security Bibliography
Provides links to external resources available online and in print.
See also
ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Security Overview
4/28/2019 • 5 minutes to read • Edit Online
Securing an application is an ongoing process. There will never be a point where a developer can guarantee that an
application is safe from all attacks, because it is impossible to predict what kinds of future attacks new technologies
will bring about. Conversely, just because nobody has yet discovered (or published) security flaws in a system does
not mean that none exist or could exist. You need to plan for security during the design phase of the project, as well
as plan how security will be maintained over the lifetime of the application.
RESOURCE DESCRIPTION
The Threat Modeling site on the MSDN Security Developer The resources on this page will help you understand the
Center threat modeling process and build threat models that you can
use to secure your own applications
RESOURCE DESCRIPTION
Securing Applications Contains links to general security topics. Also contains links to
topics for securing distributed applications, Web applications,
mobile applications, and desktop applications.
RESOURCE DESCRIPTION
Code Access Security and ADO.NET Describes the interactions between code access security, role-
based security, and partially trusted environments from the
perspective of an ADO.NET application.
Code Access Security Contains links to additional topics describing CAS in the .NET
Framework.
Database Security
The principle of least privilege also applies to your data source. Some general guidelines for database security
include:
Create accounts with the lowest possible privileges.
Do not allow users access to administrative accounts just to get code working.
Do not return server-side error messages to client applications.
Validate all input at both the client and the server.
Use parameterized commands and avoid dynamic SQL statements.
Enable security auditing and logging for the database you are using so that you are alerted to any security
breaches.
For more information, see the following resources.
RESOURCE DESCRIPTION
SQL Server Security Provides an overview of SQL Server security with application
scenarios that provide guidance for creating secure ADO.NET
applications that target SQL Server.
Recommendations for Data Access Strategies Provides recommendations for accessing data and performing
database operations.
RESOURCE DESCRIPTION
Security Policy Best Practices Provides links describing how to administer security policy.
See also
Securing ADO.NET Applications
Security in .NET
SQL Server Security
ADO.NET Managed Providers and DataSet Developer Center
Secure Data Access
4/8/2019 • 5 minutes to read • Edit Online
To write secure ADO.NET code, you have to understand the security mechanisms available in the underlying data
store, or database. You also need to consider the security implications of other features or components that your
application may contain.
RESOURCE DESCRIPTION
Protecting Connection Information Describes security best practices and techniques for protecting
connection information, such as using protected configuration
to encrypt connection strings.
Recommendations for Data Access Strategies Provides recommendations for accessing data and performing
database operations.
Connection String Builders Describes how to build connection strings from user input at
run time.
Overview of SQL Server Security Describes the SQL Server security architecture.
Modifying Data with Stored Procedures Describes how to specify parameters and obtain a return
value.
Managing Permissions with Stored Procedures in SQL Server Describes how to use SQL Server stored procedures to
encapsulate data access.
Script Exploits
A script exploit is another form of injection that uses malicious characters inserted into a Web page. The browser
does not validate the inserted characters and will process them as part of the page.
For more information, see the following resources.
RESOURCE DESCRIPTION
Script Exploits Overview Describes how to guard against scripting and SQL statement
exploits.
Probing Attacks
Attackers often use information from an exception, such as the name of your server, database, or table, to mount
an attack on your system. Because exceptions can contain specific information about your application or data
source, you can help keep your application and data source better protected by only exposing essential information
to the client.
For more information, see the following resources.
RESOURCE DESCRIPTION
Best Practices for Exceptions Describes best practices for handling exceptions.
RESOURCE DESCRIPTION
RESOURCE DESCRIPTION
Security Considerations and Guidance for Access 2007 Describes security techniques for Access 2007 such encrypting
files, administering passwords, converting databases to the
new ACCDB and ACCDE formats, and using other security
options.
Understanding the Role of Workgroup Information Files in Explains the role and relationship of the workgroup
Access Security information file in Access 2003 security.
Frequently Asked Questions About Microsoft Access Security Downloadable version of the Microsoft Access Security FAQ.
for Microsoft Access versions 2.0 through 2000
Enterprise Services
COM+ contains its own security model that relies on Windows NT accounts and process/thread impersonation.
The System.EnterpriseServices namespace provides wrappers that allow .NET applications to integrate managed
code with COM+ security services through the ServicedComponent class.
For more information, see the following resource.
RESOURCE DESCRIPTION
RESOURCE DESCRIPTION
Interoperating with Unmanaged Code Contains topics describing how to expose COM components
to the .NET Framework and how to expose .NET Framework
components to COM.
Advanced COM Interoperability Contains advanced topics such as primary interop assemblies,
threading and custom marshaling.
See also
Securing ADO.NET Applications
SQL Server Security
Recommendations for Data Access Strategies
Protecting Connection Information
Connection String Builders
ADO.NET Managed Providers and DataSet Developer Center
Secure Client Applications
4/8/2019 • 3 minutes to read • Edit Online
Applications typically consist of many parts that must all be protected from vulnerabilities that could result in data
loss or otherwise compromise the system. Creating secure user interfaces can prevent many problems by blocking
attackers before they can access data or system resources.
IMPORTANT
You must also validate user input at the data source as well as in the client application. An attacker may choose to
circumvent your application and attack the data source directly.
Windows Applications
In the past, Windows applications generally ran with full permissions. The .NET Framework provides the
infrastructure to restrict code executing in a Windows application by using code access security (CAS ). However,
CAS alone is not enough to protect your application.
Windows Forms Security
Discusses how to secure Windows Forms applications and provides links to related topics.
Windows Forms and Unmanaged Applications
Describes how to interact with unmanaged applications in a Windows Forms application.
ClickOnce Deployment for Windows Forms
Describes how to use ClickOnce deployment in a Windows Forms application and discusses the security
implications.
RESOURCE DESCRIPTION
Securing XML Web Services Created Using ASP.NET Discusses how to implement security for an ASP.NET Web
Service.
Script Exploits Overview Discusses how to guard against a script exploit attack, which
attempts to insert malicious characters into a Web page.
Basic Security Practices for Web Applications General security information and links to further discussion,
Remoting
.NET remoting enables you to build widely distributed applications easily, whether the application components are
all on one computer or spread out across the entire world. You can build client applications that use objects in
other processes on the same computer or on any other computer that is reachable over its network. You can also
use .NET remoting to communicate with other application domains in the same process.
RESOURCE DESCRIPTION
Security and Remoting Considerations Describes security issues with protected objects and
application domain crossing.
See also
Securing ADO.NET Applications
Recommendations for Data Access Strategies
Securing Applications
Protecting Connection Information
ADO.NET Managed Providers and DataSet Developer Center
Code Access Security and ADO.NET
4/28/2019 • 13 minutes to read • Edit Online
The .NET Framework offers role-based security as well as code access security (CAS ), both of which are
implemented using a common infrastructure supplied by the common language runtime (CLR ). In the world of
unmanaged code, most applications execute with the permissions of the user or principal. As a result, computer
systems can be damaged and private data compromised when malicious or error-filled software is run by a user
with elevated privileges.
By contrast, managed code executing in the .NET Framework includes code access security, which applies to code
alone. Whether the code is allowed to run or not depends on the code's origin or other aspects of the code's
identity, not solely the identity of the principal. This reduces the likelihood that managed code can be misused.
NOTE
Code executing in the CLR cannot grant permissions to itself. For example, code can request and be granted fewer
permissions than a security policy allows, but it will never be granted more permissions. When granting permissions, start
with no permissions at all and then add the narrowest permissions for the particular task being performed. Starting with all
permissions and then denying individual ones leads to insecure applications that may contain unintentional security holes
from granting more permissions than required. For more information, see Configuring Security Policy and Security Policy
Management.
Assemblies
Assemblies form the fundamental unit of deployment, version control, reuse, activation scoping, and security
permissions for a .NET Framework application. An assembly provides a collection of types and resources that are
built to work together and form a logical unit of functionality. To the CLR, a type does not exist outside the context
of an assembly. For more information on creating and deploying assemblies, see Programming with Assemblies.
Strong-naming Assemblies
A strong name, or digital signature, consists of the assembly's identity, which includes its simple text name, version
number, and culture information (if provided), plus a public key and a digital signature. The digital signature is
generated from an assembly file using the corresponding private key. The assembly file contains the assembly
manifest, which contains the names and hashes of all the files that make up the assembly.
Strong naming an assembly gives an application or component a unique identity that other software can use to
refer explicitly to it. Strong naming guards assemblies against being spoofed by an assembly that contains hostile
code. Strong-naming also ensures versioning consistency among different versions of a component. You must
strong name assemblies that will be deployed to the Global Assembly Cache (GAC ). For more information, see
Creating and Using Strong-Named Assemblies.
ConnectionString Syntax
The following example demonstrates how to use the connectionStrings element of a configuration file to allow
only a specific connection string to be used. See Connection Strings for more information on storing and
retrieving connection strings from configuration files.
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;" />
</connectionStrings>
KeyRestrictions Syntax
The following example enables the same connection string, enables the use of the Encrypt and Packet Size
connection string options, but restricts the use of any other connection string options.
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="User Id=;Password=;Persist Security Info=;"
KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>
<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>
<PermissionSet class="System.Security.NamedPermissionSet"
version="1"
Name="CustomLocalIntranet"
Description="Custom permission set given to applications on
the local intranet">
IMPORTANT
When designing CAS permissions for ADO.NET, the correct pattern is to start with the most restrictive case (no permissions
at all) and then add the specific permissions that are needed for the particular task that the code needs to perform. The
opposite pattern, starting with all permissions and then denying a specific permission, is not secure because there are many
ways of expressing the same connection string. For example, if you start with all permissions and then attempt to deny the
use of the connection string "server=someserver", the string "server=someserver.mycompany.com" would still be allowed. By
always starting by granting no permissions at all, you reduce the chances that there are holes in the permission set.
The following code demonstrates how SqlClient performs the security demand, which throws a
SecurityException if the appropriate CAS permissions are not in place. The SecurityException output is displayed
in the console window.
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) {
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");
}
// 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);
Console.WriteLine("Connection opened, unexpected.");
}
catch (System.Security.SecurityException ex) {
Console.WriteLine("Failed, as expected: {0}",
ex.FirstPermissionThatFailed);
permissions.AddPermission(sqlPermission);
Imports System
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
' 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
permissions.AddPermission(sqlPermission)
permissions.AddPermission(sqlPermission)
See also
Securing ADO.NET Applications
Security in Native and .NET Framework Code
Role-Based Security
ADO.NET Managed Providers and DataSet Developer Center
Privacy and Data Security
4/8/2019 • 2 minutes to read • Edit Online
Safeguarding and managing sensitive information in an ADO.NET application is dependent upon the underlying
products and technologies used to create it. ADO.NET does not directly provide services for securing or
encrypting data.
See also
Securing ADO.NET Applications
SQL Server Security
ADO.NET Managed Providers and DataSet Developer Center
Data Type Mappings in ADO.NET
5/14/2019 • 2 minutes to read • Edit Online
The .NET Framework is based on the common type system, which defines how types are declared, used, and
managed in the runtime. It consists of both value types and reference types, which all derive from the Object base
type. When working with a data source, the data type is inferred from the data provider if it is not explicitly
specified. For example, a DataSet object is independent of any specific data source. Data in a DataSet is retrieved
from a data source, and changes are persisted back to the data source by using a DataAdapter . This means that
when a DataAdapter fills a DataTable in a DataSet with values from a data source, the resulting data types of the
columns in the DataTable are .NET Framework types, instead of types specific to the .NET Framework data
provider that is used to connect to the data source.
Likewise, when a DataReader returns a value from a data source, the resulting value is stored in a local variable
that has a .NET Framework type. For both the Fill operations of the DataAdapter and the Get methods of the
DataReader , the .NET Framework type is inferred from the value returned from the .NET Framework data
provider.
Instead of relying on the inferred data type, you can use the typed accessor methods of the DataReader when you
know the specific type of the value being returned. Typed accessor methods give you better performance by
returning a value as a specific .NET Framework type, which eliminates the need for additional type conversion.
NOTE
Null values for .NET Framework data provider data types are represented by DBNull.Value .
In This Section
SQL Server Data Type Mappings
Lists inferred data type mappings and data accessor methods for System.Data.SqlClient.
OLE DB Data Type Mappings
Lists inferred data type mappings and data accessor methods for System.Data.OleDb.
ODBC Data Type Mappings
Lists inferred data type mappings and data accessor methods for System.Data.Odbc.
Oracle Data Type Mappings
Lists inferred data type mappings and data accessor methods for System.Data.OracleClient.
Floating-Point Numbers
Describes issues that developers frequently encounter when working with floating-point numbers.
See also
SQL Server Data Types and ADO.NET
Configuring Parameters and Parameter Data Types
Retrieving Database Schema Information
Common Type System
Converting Types
ADO.NET Managed Providers and DataSet Developer Center
SQL Server Data Type Mappings
5/14/2019 • 2 minutes to read • Edit Online
SQL Server and the .NET Framework are based on different type systems. For example, the .NET Framework
Decimal structure has a maximum scale of 28, whereas the SQL Server decimal and numeric data types have a
maximum scale of 38. To maintain data integrity when reading and writing data, the SqlDataReader exposes SQL
Server–specific typed accessor methods that return objects of System.Data.SqlTypes as well as accessor methods
that return .NET Framework types. Both SQL Server types and .NET Framework types are also represented by
enumerations in the DbType and SqlDbType classes, which you can use when specifying SqlParameter data types.
The following table shows the inferred .NET Framework type, the DbType and SqlDbType enumerations, and the
accessor methods for the SqlDataReader.
Char[] GetChars
Char[] GetChars
Char[] GetChars
Char[] GetChars
See also
SQL Server Data Types and ADO.NET
SQL Server Binary and Large-Value Data
Data Type Mappings in ADO.NET
Configuring Parameters and Parameter Data Types
ADO.NET Managed Providers and DataSet Developer Center
OLE DB Data Type Mappings
5/14/2019 • 2 minutes to read • Edit Online
The following table shows the inferred .NET Framework type for data types from the .NET Framework Data
Provider for ADO and OLE DB (System.Data.OleDb). The typed accessor methods for the OleDbDataReader are
also listed.
* For the OLE DB types DBTYPE_IUNKNOWN and DBTYPE_IDISPATCH , the object reference is a marshaled representation
of the pointer.
See also
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
ODBC Data Type Mappings
5/14/2019 • 2 minutes to read • Edit Online
The following table shows the inferred .NET Framework type for data types from the .NET Framework Data
Provider for ODBC (System.Data.Odbc). The typed accessor methods for the OdbcDataReader are also listed.
Char[] GetChars()
Char[] GetChars()
Char[] GetChars()
ODBC TYPE .NET FRAMEWORK TYPE .NET FRAMEWORK TYPED ACCESSOR
Char[] GetChars()
Char[] GetChars()
See also
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle Data Type Mappings
4/8/2019 • 3 minutes to read • Edit Online
The following table lists Oracle data types and their mappings to the OracleDataReader.
The following table lists Oracle data types and the .NET Framework data types (System.Data.DbType and
OracleType) to use when binding them as parameters.
FLOAT Single, Double, Decimal Float, Double, Number Size determines the
System.Data.DBType and
OracleType.
INTEGER SByte, Int16, Int32, Int64, SByte, Int16, Int32, Size determines the
Decimal Number System.Data.DBType and
OracleType.
UNSIGNED INTEGER Byte, UInt16, UInt32, Byte, UInt16, Uint32, Size determines the
UInt64, Decimal Number System.Data.DBType and
OracleType.
The InputOutput, Output, and ReturnValue ParameterDirection values used by the Value property of the
OracleParameter object are .NET Framework data types, unless the input value is an Oracle data type (for example,
OracleNumber or OracleString). This does not apply to REF CURSOR, BFILE, or LOB data types.
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Floating-Point Numbers
5/18/2019 • 2 minutes to read • Edit Online
This topic describes some of the issues that developers frequently encounter when they work with floating-point
numbers in ADO.NET. These issues are caused by the way that computers store floating-point numbers, and are
not specific to a particular provider such as System.Data.SqlClient or System.Data.OracleClient.
Floating-point numbers generally do not have an exact binary representation. Instead, the computer stores an
approximation of the number. At different times, different numbers of binary digits may be used to represent the
number. When a floating point number is converted from one representation to another representation, the least
significant digits of that number may vary slightly. Conversion typically occurs when the number is cast from one
type to another type. The variation occurs whether the conversion occurs within a database, between types that
represent database values, or between types. Because of these changes, numbers that would logically be equal may
have changes in their least-significant digits that cause them to have different values. The number of digits of
precision in the number may be larger or smaller than expected. When formatted as a string, the number may not
show the expected value.
To minimize these effects, you should use the closest match between numeric types that is available to you. For
example, if you are working with SQL Server, the exact numeric value may change if you convert a Transact-SQL
value of real type to a value of float type. In the .NET Framework, converting a Single to a Double may also
produce unexpected results. In both of these cases, a good strategy is to make all the values in the application use
the same numeric type. You can also use a fixed-precision decimal type, or cast floating-point numbers to a fixed-
precision decimal type before you work with them.
To work around problems with equality comparison, consider coding your application so that variations in the least
significant digits are ignored. For example, instead of comparing to see whether two numbers are equal, subtract
one number from the other number. If the difference is within an acceptable margin of rounding, your application
can treat the numbers as if they are the same.
See also
Why Floating-Point Numbers May Lose Precision
ADO.NET Overview
Retrieving and Modifying Data in ADO.NET
5/18/2019 • 2 minutes to read • Edit Online
A primary function of any database application is connecting to a data source and retrieving the data that it
contains. The .NET Framework data providers of ADO.NET serve as a bridge between an application and a
data source, allowing you to execute commands as well as to retrieve data by using a DataReader or a
DataAdapter. A key function of any database application is the ability to update the data that is stored in the
database. In ADO.NET, updating data involves using the DataAdapter and DataSet, and Command objects;
and it may also involve using transactions.
In This Section
Connecting to a Data Source
Describes how to establish a connection to a data source and how to work with connection events.
Connection Strings
Contains topics describing various aspects of using connection strings, including connection string keywords,
security info, and storing and retrieving them.
Connection Pooling
Describes connection pooling for the .NET Framework data providers.
Commands and Parameters
Contains topics describing how to create commands and command builders, configure parameters, and how to
execute commands to retrieve and modify data.
DataAdapters and DataReaders
Contains topics describing DataReaders, DataAdapters, parameters, handling DataAdapter events and
performing batch operations.
Transactions and Concurrency
Contains topics describing how to perform local transactions, distributed transactions, and work with optimistic
concurrency.
Retrieving Identity or Autonumber Values
Provides an example of mapping the values generated for an identity column in a SQL Server table or for an
Autonumber field in a Microsoft Access table, to a column of an inserted row in a table. Discusses merging
identity values in a DataTable .
Retrieving Binary Data
Describes how to retrieve binary data or large data structures using CommandBehavior . SequentialAccess to
modify the default behavior of a DataReader .
Modifying Data with Stored Procedures
Describes how to use stored procedure input parameters and output parameters to insert a row in a database,
returning a new identity value.
Retrieving Database Schema Information
Describes how to obtain available databases or catalogs, tables and views in a database, constraints that exist
for tables, and other schema information from a data source.
DbProviderFactories
Describes the provider factory model and demonstrates how to use the base classes in the System.Data.Common
namespace.
Data Tracing in ADO.NET
Describes how ADO.NET provides built-in data tracing functionality.
Performance Counters
Describes performance counters available for SqlClient and OracleClient .
Asynchronous Programming
Describes ADO.NET support for asynchronous programming.
SqlClient Streaming Support
Discusses how to write applications that stream data from SQL Server without having it fully loaded in
memory.
See also
Data Type Mappings in ADO.NET
DataSets, DataTables, and DataViews
Securing ADO.NET Applications
SQL Server and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Connecting to a Data Source in ADO.NET
4/8/2019 • 2 minutes to read • Edit Online
In ADO.NET you use a Connection object to connect to a specific data source by supplying necessary
authentication information in a connection string. The Connection object you use depends on the type of data
source.
Each .NET Framework data provider included with the .NET Framework has a DbConnection object: the .NET
Framework Data Provider for OLE DB includes an OleDbConnection object, the .NET Framework Data Provider
for SQL Server includes a SqlConnection object, the .NET Framework Data Provider for ODBC includes an
OdbcConnection object, and the .NET Framework Data Provider for Oracle includes an OracleConnection object.
In This Section
Establishing the Connection
Describes how to use a Connection object to establish a connection to a data source.
Connection Events
Describes how to use an InfoMessage event to retrieve informational messages from a data source.
See also
Connection Strings
Connection Pooling
Commands and Parameters
DataAdapters and DataReaders
Transactions and Concurrency
ADO.NET Managed Providers and DataSet Developer Center
Establishing the Connection
4/28/2019 • 5 minutes to read • Edit Online
To connect to Microsoft SQL Server, use the SqlConnection object of the .NET Framework Data Provider for SQL
Server. To connect to an OLE DB data source, use the OleDbConnection object of the .NET Framework Data
Provider for OLE DB. To connect to an ODBC data source, use the OdbcConnection object of the .NET Framework
Data Provider for ODBC. To connect to an Oracle data source, use the OracleConnection object of the .NET
Framework Data Provider for Oracle. For securely storing and retrieving connection strings, see Protecting
Connection Information.
Closing Connections
We recommend that you always close the connection when you are finished using it, so that the connection can be
returned to the pool. The Using block in Visual Basic or C# automatically disposes of the connection when the
code exits the block, even in the case of an unhandled exception. See using Statement and Using Statement for
more information.
You can also use the Close or Dispose methods of the connection object for the provider that you are using.
Connections that are not explicitly closed might not be added or returned to the pool. For example, a connection
that has gone out of scope but that has not been explicitly closed will only be returned to the connection pool if the
maximum pool size has been reached and the connection is still valid. For more information, see OLE DB, ODBC,
and Oracle Connection Pooling.
NOTE
Do not call Close or Dispose on a Connection, a DataReader, or any other managed object in the Finalize method
of your class. In a finalizer, only release unmanaged resources that your class owns directly. If your class does not own any
unmanaged resources, do not include a Finalize method in your class definition. For more information, see Garbage
Collection.
NOTE
Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection
pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see SQL
Server Connection Pooling (ADO.NET).
NOTE
The OleDbConnection object does not support setting or retrieving dynamic properties specific to an OLE DB provider. Only
properties that can be passed in the connection string for the OLE DB provider are supported.
The following code example demonstrates how to create and open a connection to an OLE DB data source.
See also
Connecting to a Data Source
Connection Strings
OLE DB, ODBC, and Oracle Connection Pooling
ADO.NET Managed Providers and DataSet Developer Center
Connection Events
4/8/2019 • 3 minutes to read • Edit Online
All of the .NET Framework data providers have Connection objects with two events that you can use to retrieve
informational messages from a data source or to determine if the state of a Connection has changed. The
following table describes the events of the Connection object.
EVENT DESCRIPTION
NOTE
An error with a severity level of 17 or above that causes the server to stop processing the command must be handled as an
exception. In this case, an exception is thrown regardless of how the error is handled in the InfoMessage event.
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);
See also
Connecting to a Data Source
ADO.NET Managed Providers and DataSet Developer Center
Connection Strings in ADO.NET
5/18/2019 • 2 minutes to read • Edit Online
A connection string contains initialization information that is passed as a parameter from a data provider to a data
source. The data provider receives the connection string as the value of the DbConnection.ConnectionString
property. The provider parses the connection string and ensures that the syntax is correct and that the keywords
are supported. Then the DbConnection.Open() method passes the parsed connection parameters to the data
source. The data source performs further validation and establishes a connection.
keyword1=value; keyword2=value;
Keywords are not case-sensitive. Values, however, may be case-sensitive, depending on the data source. Both
keywords and values may contain whitespace characters. Leading and trailing white space is ignored in keywords
and unquoted values.
If a value contains the semicolon, Unicode control characters, or leading or trailing white space, it must be
enclosed in single or double quotation marks. For example:
The enclosing character may not occur within the value it encloses. Therefore, a value containing single quotation
marks can be enclosed only in double quotation marks, and vice versa:
Keyword='double"quotation;mark';
Keyword="single'quotation;mark";
The quotation marks themselves, as well as the equals sign, do not require escaping, so the following connection
strings are valid:
Since each value is read till the next semicolon or the end of string, the value in the latter example is a=b=c , and
the final semicolon is optional.
All connection strings share the same basic syntax described above. The set of recognized keywords depends on
the provider, however, and has evolved over the years from earlier APIs such as ODBC. The .NET Framework data
provider for SQL Server ( SqlClient ) supports many keywords from older APIs, but is generally more flexible and
accepts synonyms for many of the common connection string keywords.
Typing mistakes can cause errors. For example, Integrated Security=true is valid, but IntegratedSecurity=true
causes an error.
Connection strings constructed manually at run time from unvalidated user input are vulnerable to string-
injection attacks and jeopardize security at the data source. To address these problems, ADO.NET 2.0 introduced
connection string builders for each .NET Framework data provider. These connection string builders expose
parameters as strongly-typed properties, and make it possible to validate the connection string before it's sent to
the data source.
In This Section
Connection String Builders
Demonstrates how to use the ConnectionStringBuilder classes to construct valid connection strings at run time.
Connection Strings and Configuration Files
Demonstrates how to store and retrieve connection strings in configuration files.
Connection String Syntax
Describes how to configure provider-specific connection strings for SqlClient , OracleClient , OleDb , and Odbc .
Protecting Connection Information
Demonstrates techniques for protecting information used to connect to a data source.
See also
Connecting to a Data Source
ADO.NET Managed Providers and DataSet Developer Center
Connection String Builders
5/18/2019 • 3 minutes to read • Edit Online
In earlier versions of ADO.NET, compile-time checking of connection strings with concatenated string values did
not occur, so that at run time, an incorrect keyword generated an ArgumentException. Each of the .NET
Framework data providers supported different syntax for connection string keywords, which made constructing
valid connection strings difficult if done manually. To address this problem, ADO.NET 2.0 introduced new
connection string builders for each .NET Framework data provider. Each data provider includes a strongly typed
connection string builder class that inherits from DbConnectionStringBuilder. The following table lists the .NET
Framework data providers and their associated connection string builder classes.
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
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);
The output shows that the SqlConnectionStringBuilder handled this correctly by escaping the extra value in
double quotation marks instead of appending it to the connection string as a new key/value pair.
NOTE
The System.Configuration namespace allows programmatic access to configuration files that use the
WebConfigurationManager for Web applications and the ConfigurationManager for Windows applications. For more
information about working with connection strings and configuration files, see Connection Strings and Configuration Files.
Example
This example demonstrates retrieving a partial connection string from a configuration file and completing it by
setting the DataSource, UserID, and Password properties of the SqlConnectionStringBuilder. The configuration
file is defined as follows.
<connectionStrings>
<clear/>
<add name="partialConnectString"
connectionString="Initial Catalog=Northwind;"
providerName="System.Data.SqlClient" />
</connectionStrings>
NOTE
You must set a reference to the System.Configuration.dll in your project for the code to run.
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);
See also
Connection Strings
Privacy and Data Security
ADO.NET Managed Providers and DataSet Developer Center
Connection Strings and Configuration Files
4/8/2019 • 12 minutes to read • Edit Online
Embedding connection strings in your application's code can lead to security vulnerabilities and maintenance
problems. Unencrypted connection strings compiled into an application's source code can be viewed using the
Ildasm.exe (IL Disassembler) tool. Moreover, if the connection string ever changes, your application must be
recompiled. For these reasons, we recommend storing connection strings in an application configuration file.
NOTE
You can save part of a connection string in a configuration file and use the DbConnectionStringBuilder class to complete it at
run time. This is useful in scenarios where you do not know elements of the connection string ahead of time, or when you
do not want to save sensitive information in a configuration file. For more information, see Connection String Builders.
<connectionStrings>
<add name="Name"
providerName="System.Data.ProviderName"
connectionString="Valid Connection String;" />
</connectionStrings>
In the main application configuration file, you use the configSource attribute to specify the fully qualified name
and location of the external file. This example refers to an external configuration file named connections.config .
NOTE
The machine.config file also contains a connectionStrings section, which contains connection strings used by Visual
Studio. When retrieving connection strings by provider name from the app.config file in a Windows application, the
connection strings in machine.config get loaded first, and then the entries from app.config. Adding clear immediately
after the connectionStrings element removes all inherited references from the data structure in memory, so that only the
connection strings defined in the local app.config file are considered.
NOTE
Accessing configuration files at run time requires granting permissions to the caller; the required permissions depend on the
type of application, configuration file, and location. For more information, see Using the Configuration Classes and
WebConfigurationManager for ASP.NET applications, and ConfigurationManager for Windows applications.
You can use the ConnectionStringSettingsCollection to retrieve connection strings from application configuration
files. It contains a collection of ConnectionStringSettings objects, each of which represents a single entry in the
connectionStrings section. Its properties map to connection string attributes, allowing you to retrieve a
connection string by specifying the name or the provider name.
PROPERTY DESCRIPTION
PROPERTY DESCRIPTION
NOTE
System.Configuration.dll is not included in all project types, and you may need to set a reference to it in order to use the
configuration classes. The name and location of a particular application configuration file varies by the type of application
and the hosting process.
using System.Configuration;
class Program
{
static void Main()
{
GetConnectionStrings();
Console.ReadLine();
}
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
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
Return returnValue
End Function
Return returnValue
End Function
<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAH2... </CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
When the encrypted connection string is retrieved at run time, the .NET Framework uses the specified provider to
decrypt the CipherValue and make it available to your application. You do not need to write any additional code
to manage the decryption process.
Protected Configuration Providers
Protected configuration providers are registered in the configProtectedData section of the machine.config file
on the local computer, as shown in the following fragment, which shows the two protected configuration providers
supplied with the .NET Framework. The values shown here have been truncated for readability.
<configProtectedData defaultProvider="RsaProtectedConfigurationProvider">
<providers>
<add name="RsaProtectedConfigurationProvider"
type="System.Configuration.RsaProtectedConfigurationProvider, ... />
<add name="DataProtectionConfigurationProvider"
type="System.Configuration.DpapiProtectedConfigurationProvider, ... />
</providers>
</configProtectedData>
You can configure additional protected configuration providers by adding them to the machine.config file. You
can also create your own protected configuration provider by inheriting from the ProtectedConfigurationProvider
abstract base class. The following table describes the two configuration files included with the .NET Framework.
PROVIDER DESCRIPTION
Both providers offer strong encryption of data. However, if you are planning to use the same encrypted
configuration file on multiple servers, such as a Web farm, only the RsaProtectedConfigurationProvider enables
you to export the encryption keys used to encrypt the data and import them on another server. For more
information, see Importing and Exporting Protected Configuration RSA Key Containers.
Using the Configuration Classes
The System.Configuration namespace provides classes to work with configuration settings programmatically. The
ConfigurationManager class provides access to machine, application, and user configuration files. If you are
creating an ASP.NET application, you can use the WebConfigurationManager class, which provides the same
functionality while also allowing you to access settings that are unique to ASP.NET applications, such as those
found in <system.web>.
NOTE
The System.Security.Cryptography namespace contains classes that provide additional options for encrypting and
decrypting data. Use these classes if you require cryptographic services that are not available using protected configuration.
Some of these classes are wrappers for the unmanaged Microsoft CryptoAPI, while others are purely managed
implementations. For more information, see Cryptographic Services.
App.config Example
This example demonstrates how to toggle encrypting the connectionStrings section in an app.config file for a
Windows application. In this example, the procedure takes the name of the application as an argument, for
example, "MyApplication.exe". The app.config file will then be encrypted and copied to the folder that contains
the executable under the name of "MyApplication.exe.config".
NOTE
The connection string can only be decrypted on the computer on which it was encrypted.
The code uses the OpenExeConfiguration method to open the app.config file for editing, and the GetSection
method returns the connectionStrings section. The code then checks the IsProtected property, calling the
ProtectSection to encrypt the section if it is not encrypted. The UnprotectSection method is invoked to decrypt the
section. The Save method completes the operation and saves the changes.
NOTE
You must set a reference to System.Configuration.dll in your project for the code to run.
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)
If section.SectionInformation.IsProtected Then
' Remove encryption.
section.SectionInformation.UnprotectSection()
Else
' Encrypt the section.
section.SectionInformation.ProtectSection( _
"DataProtectionConfigurationProvider")
End If
Console.WriteLine("Protected={0}", _
section.SectionInformation.IsProtected)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Web.config Example
This example uses the OpenWebConfiguration method of the WebConfigurationManager . Note that in this case you
can supply the relative path to the Web.config file by using a tilde. The code requires a reference to the
System.Web.Configuration class.
// Toggle encryption.
if (section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
}
else
{
section.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
}
For more information about securing ASP.NET applications, see Securing ASP.NET web sites.
See also
Connection String Builders
Protecting Connection Information
Using the Configuration Classes
Configuring Apps
ASP.NET Web Site Administration
ADO.NET Managed Providers and DataSet Developer Center
Connection String Syntax
4/28/2019 • 8 minutes to read • Edit Online
Each .NET Framework data provider has a Connection object that inherits from DbConnection as well as a
provider-specific ConnectionString property. The specific connection string syntax for each provider is documented
in its ConnectionString property. The following table lists the four data providers that are included in the .NET
Framework.
System.Data.SqlClient Provides data access for Microsoft SQL Server. For more
information on connection string syntax, see
ConnectionString.
System.Data.OleDb Provides data access for data sources exposed using OLE DB.
For more information on connection string syntax, see
ConnectionString.
System.Data.Odbc Provides data access for data sources exposed using ODBC.
For more information on connection string syntax, see
ConnectionString.
System.Data.OracleClient Provides data access for Oracle version 8.1.7 or later. For more
information on connection string syntax, see
ConnectionString.
Windows Authentication
We recommend using Windows Authentication (sometimes referred to as integrated security) to connect to data
sources that support it. The syntax employed in the connection string varies by provider. The following table shows
the Windows Authentication syntax used with the .NET Framework data providers.
PROVIDER SYNTAX
PROVIDER SYNTAX
-- or --
Integrated Security=SSPI;
Odbc Trusted_Connection=yes;
NOTE
Integrated Security=true throws an exception when used with the OleDb provider.
IMPORTANT
The default setting for the Persist Security Info keyword is false . Setting it to true or yes allows security-
sensitive information, including the user ID and password, to be obtained from the connection after the connection has been
opened. Keep Persist Security Info set to false to ensure that an untrusted source does not have access to sensitive
connection string information.
When you connect to Azure SQL Database or to Azure SQL Data Warehouse and provide a login in the format
user@servername , make sure that the servername value in the login matches the value provided for Server= .
NOTE
Windows authentication takes precedence over SQL Server logins. If you specify both Integrated Security=true as well as a
user name and password, the user name and password will be ignored and Windows authentication will be used.
Data Source=MySqlServer\MSSQL1;"
You can also set the DataSource property of the SqlConnectionStringBuilder to the instance name when building a
connection string. The DataSource property of a SqlConnection object is read-only.
Type System Version Changes
The Type System Version keyword in a SqlConnection.ConnectionString specifies the client-side representation of
SQL Server types. See SqlConnection.ConnectionString for more information about the Type System Version
keyword.
Using TrustServerCertificate
The TrustServerCertificate keyword is valid only when connecting to a SQL Server instance with a valid
certificate. When TrustServerCertificate is set to true , the transport layer will use SSL to encrypt the channel
and bypass walking the certificate chain to validate trust.
"TrustServerCertificate=true;"
NOTE
If TrustServerCertificate is set to true and encryption is turned on, the encryption level specified on the server will be
used even if Encrypt is set to false in the connection string. The connection will fail otherwise.
Enabling Encryption
To enable encryption when a certificate has not been provisioned on the server, the Force Protocol Encryption
and the Trust Server Certificate options must be set in SQL Server Configuration Manager. In this case,
encryption will use a self-signed server certificate without validation if no verifiable certificate has been
provisioned on the server.
Application settings cannot reduce the level of security configured in SQL Server, but can optionally strengthen it.
An application can request encryption by setting the TrustServerCertificate and Encrypt keywords to true ,
guaranteeing that encryption takes place even when a server certificate has not been provisioned and Force
Protocol Encryption has not been configured for the client. However, if TrustServerCertificate is not enabled in
the client configuration, a provisioned server certificate is still required.
The following table describes all cases.
If the Jet database is secured using user-level security, you must provide the location of the workgroup information
file (.mdw ). The workgroup information file is used to validate the credentials presented in the connection string.
IMPORTANT
It is possible to supply connection information for an OleDbConnection in a Universal Data Link (UDL) file; however you
should avoid doing so. UDL files are not encrypted, and expose connection string information in clear text. Because a UDL file
is an external file-based resource to your application, it cannot be secured using the .NET Framework. UDL files are not
supported for SqlClient.
"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=|DataDirectory|\Northwind.mdb;
Jet OLEDB:System Database=|DataDirectory|\System.mdw;"
IMPORTANT
Specifying the location of the system database in the connection string is not required if the Access/Jet database is
unsecured. Security is off by default, with all users connecting as the built-in Admin user with a blank password. Even when
user-level security is correctly implemented, a Jet database remains vulnerable to attack. Therefore, storing sensitive
information in an Access/Jet database is not recommended because of the inherent weakness of its file-based security
scheme.
Connecting to Excel
The Microsoft Jet provider is used to connect to an Excel workbook. In the following connection string, the
Extended Properties keyword sets properties that are specific to Excel. "HDR=Yes;" indicates that the first row
contains column names, not data, and "IMEX=1;" tells the driver to always read "intermixed" data columns as text.
Note that the double quotation character required for the Extended Properties must also be enclosed in double
quotation marks.
Data Shape Provider Connection String Syntax
Use both the Provider and the Data Provider keywords when using the Microsoft Data Shape provider. The
following example uses the Shape provider to connect to a local instance of SQL Server.
See also
Connection Strings
Connecting to a Data Source
ADO.NET Managed Providers and DataSet Developer Center
Protecting Connection Information
4/8/2019 • 3 minutes to read • Edit Online
Protecting access to your data source is one of the most important goals when securing an application. A
connection string presents a potential vulnerability if it is not secured. Storing connection information in plain text
or persisting it in memory risks compromising your entire system. Connection strings embedded in your source
code can be read using the Ildasm.exe (IL Disassembler) to view Microsoft intermediate language (MSIL ) in a
compiled assembly.
Security vulnerabilities involving connection strings can arise based on the type of authentication used, how
connection strings are persisted in memory and on disk, and the techniques used to construct them at run time.
<identity impersonate="true"
userName="MyDomain\UserAccount"
password="*****" />
The fixed identity account should be a low -privilege account that has been granted only necessary permissions in
the database. In addition, you should encrypt the configuration file so that the user name and password are not
exposed in clear text.
See also
Securing ADO.NET Applications
Encrypting Configuration Information Using Protected Configuration
Security in .NET
ADO.NET Managed Providers and DataSet Developer Center
Connection Pooling
4/8/2019 • 2 minutes to read • Edit Online
Connecting to a data source can be time consuming. To minimize the cost of opening connections, ADO.NET uses
an optimization technique called connection pooling, which minimizes the cost of repeatedly opening and closing
connections. Connection pooling is handled differently for the .NET Framework data providers.
In This Section
SQL Server Connection Pooling (ADO.NET)
Provides an overview of connection pooling and describes how connection pooling works in SQL Server.
OLE DB, ODBC, and Oracle Connection Pooling
Describes connection pooling for the .NET Framework Data Provider for OLE DB, the .NET Framework Data
Provider for ODBC, and the .NET Framework Data Provider for Oracle.
See also
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
SQL Server Connection Pooling (ADO.NET)
6/4/2019 • 10 minutes to read • Edit Online
Connecting to a database server typically consists of several time-consuming steps. A physical channel such as a
socket or a named pipe must be established, the initial handshake with the server must occur, the connection string
information must be parsed, the connection must be authenticated by the server, checks must be run for enlisting
in the current transaction, and so on.
In practice, most applications use only one or a few different configurations for connections. This means that
during application execution, many identical connections will be repeatedly opened and closed. To minimize the
cost of opening connections, ADO.NET uses an optimization technique called connection pooling.
Connection pooling reduces the number of times that new connections must be opened. The pooler maintains
ownership of the physical connection. It manages connections by keeping alive a set of active connections for each
given connection configuration. Whenever a user calls Open on a connection, the pooler looks for an available
connection in the pool. If a pooled connection is available, it returns it to the caller instead of opening a new
connection. When the application calls Close on the connection, the pooler returns it to the pooled set of active
connections instead of closing it. Once the connection is returned to the pool, it is ready to be reused on the next
Open call.
Only connections with the same configuration can be pooled. ADO.NET keeps several pools at the same time, one
for each configuration. Connections are separated into pools by connection string, and by Windows identity when
integrated security is used. Connections are also pooled based on whether they are enlisted in a transaction. When
using ChangePassword, the SqlCredential instance affects the connection pool. Different instances of
SqlCredential will use different connection pools, even if the user ID and password are the same.
Pooling connections can significantly enhance the performance and scalability of your application. By default,
connection pooling is enabled in ADO.NET. Unless you explicitly disable it, the pooler optimizes the connections as
they are opened and closed in your application. You can also supply several connection string modifiers to control
connection pooling behavior. For more information, see "Controlling Connection Pooling with Connection String
Keywords" later in this topic.
NOTE
When connection pooling is enabled, and if a timeout error or other login error occurs, an exception will be thrown and
subsequent connection attempts will fail for the next five seconds, the "blocking period". If the application attempts to
connect within the blocking period, the first exception will be thrown again. Subsequent failures after a blocking period ends
will result in a new blocking periods that is twice as long as the previous blocking period, up to a maximum of one minute.
If MinPoolSize is either not specified in the connection string or is specified as zero, the connections in the pool
will be closed after a period of inactivity. However, if the specified MinPoolSize is greater than zero, the connection
pool is not destroyed until the AppDomain is unloaded and the process ends. Maintenance of inactive or empty
pools involves minimal system overhead.
NOTE
The pool is automatically cleared when a fatal error occurs, such as a failover.
Adding Connections
A connection pool is created for each unique connection string. When a pool is created, multiple connection
objects are created and added to the pool so that the minimum pool size requirement is satisfied. Connections are
added to the pool as needed, up to the maximum pool size specified (100 is the default). Connections are released
back into the pool when they are closed or disposed.
When a SqlConnection object is requested, it is obtained from the pool if a usable connection is available. To be
usable, a connection must be unused, have a matching transaction context or be unassociated with any transaction
context, and have a valid link to the server.
The connection pooler satisfies requests for connections by reallocating connections as they are released back into
the pool. If the maximum pool size has been reached and no usable connection is available, the request is queued.
The pooler then tries to reclaim any connections until the time-out is reached (the default is 15 seconds). If the
pooler cannot satisfy the request before the connection times out, an exception is thrown.
Cau t i on
We strongly recommend that you always close the connection when you are finished using it so that the
connection will be returned to the pool. You can do this using either the Close or Dispose methods of the
Connection object, or by opening all connections inside a using statement in C#, or a Using statement in Visual
Basic. Connections that are not explicitly closed might not be added or returned to the pool. For more information,
see using Statement or How to: Dispose of a System Resource for Visual Basic.
NOTE
Do not call Close or Dispose on a Connection , a DataReader , or any other managed object in the Finalize
method of your class. In a finalizer, only release unmanaged resources that your class owns directly. If your class does not
own any unmanaged resources, do not include a Finalize method in your class definition. For more information, see
Garbage Collection.
For more info about the events associated with opening and closing connections, see Audit Login Event Class and
Audit Logout Event Class in the SQL Server documentation.
Removing Connections
The connection pooler removes a connection from the pool after it has been idle for approximately 4-8 minutes, or
if the pooler detects that the connection with the server has been severed. Note that a severed connection can be
detected only after attempting to communicate with the server. If a connection is found that is no longer connected
to the server, it is marked as invalid. Invalid connections are removed from the connection pool only when they are
closed or reclaimed.
If a connection exists to a server that has disappeared, this connection can be drawn from the pool even if the
connection pooler has not detected the severed connection and marked it as invalid. This is the case because the
overhead of checking that the connection is still valid would eliminate the benefits of having a pooler by causing
another round trip to the server to occur. When this occurs, the first attempt to use the connection will detect that
the connection has been severed, and an exception is thrown.
Transaction Support
Connections are drawn from the pool and assigned based on transaction context. Unless Enlist=false is specified
in the connection string, the connection pool makes sure that the connection is enlisted in the Current context.
When a connection is closed and returned to the pool with an enlisted System.Transactions transaction, it is set
aside in such a way that the next request for that connection pool with the same System.Transactions transaction
will return the same connection if it is available. If such a request is issued, and there are no pooled connections
available, a connection is drawn from the non-transacted part of the pool and enlisted. If no connections are
available in either area of the pool, a new connection is created and enlisted.
When a connection is closed, it is released back into the pool and into the appropriate subdivision based on its
transaction context. Therefore, you can close the connection without generating an error, even though a distributed
transaction is still pending. This allows you to commit or abort the distributed transaction later.
Pool Fragmentation
Pool fragmentation is a common problem in many Web applications where the application can create a large
number of pools that are not freed until the process exits. This leaves a large number of connections open and
consuming memory, which results in poor performance.
Pool Fragmentation Due to Integrated Security
Connections are pooled according to the connection string plus the user identity. Therefore, if you use Basic
authentication or Windows Authentication on the Web site and an integrated security login, you get one pool per
user. Although this improves the performance of subsequent database requests for a single user, that user cannot
take advantage of connections made by other users. It also results in at least one connection per user to the
database server. This is a side effect of a particular Web application architecture that developers must weigh
against security and auditing requirements.
Pool Fragmentation Due to Many Databases
Many Internet service providers host several Web sites on a single server. They may use a single database to
confirm a Forms authentication login and then open a connection to a specific database for that user or group of
users. The connection to the authentication database is pooled and used by everyone. However, there is a separate
pool of connections to each database, which increase the number of connections to the server.
This is also a side-effect of the application design. There is a relatively simple way to avoid this side effect without
compromising security when you connect to SQL Server. Instead of connecting to a separate database for each
user or group, connect to the same database on the server and then execute the Transact-SQL USE statement to
change to the desired database. The following code fragment demonstrates creating an initial connection to the
master database and then switching to the desired database specified in the databaseName string variable.
See also
Connection Pooling
SQL Server and ADO.NET
Performance Counters
ADO.NET Managed Providers and DataSet Developer Center
OLE DB, ODBC, and Oracle Connection Pooling
4/8/2019 • 5 minutes to read • Edit Online
Pooling connections can significantly enhance the performance and scalability of your application. This section
discusses connection pooling for the .NET Framework data providers for OLE DB, ODBC and Oracle.
We recommend that you always close or dispose of a connection when you are finished using it in order to return
the connection to the pool. Connections that are not explicitly closed may not get returned to the pool. For
example, a connection that has gone out of scope but that has not been explicitly closed will only be returned to
the connection pool if the maximum pool size has been reached and the connection is still valid.
For more information about OLE DB session or resource pooling, as well as how to disable pooling by overriding
OLE DB provider service defaults, see the OLE DB Programmer's Guide.
See also
Connection Pooling
Performance Counters
ADO.NET Managed Providers and DataSet Developer Center
Commands and Parameters
4/8/2019 • 2 minutes to read • Edit Online
After establishing a connection to a data source, you can execute commands and return results from the data
source using a DbCommand object. You can create a command using one of the command constructors for the
.NET Framework data provider you are working with. Constructors can take optional arguments, such as an SQL
statement to execute at the data source, a DbConnection object, or a DbTransaction object. You can also configure
those objects as properties of the command. You can also create a command for a particular connection using
the CreateCommand method of a DbConnection object. The SQL statement being executed by the command can
be configured using the CommandText property.
Each .NET Framework data provider included with the .NET Framework has a Command object. The .NET
Framework Data Provider for OLE DB includes an OleDbCommand object, the .NET Framework Data Provider
for SQL Server includes a SqlCommand object, the .NET Framework Data Provider for ODBC includes an
OdbcCommand object, and the .NET Framework Data Provider for Oracle includes an OracleCommand object.
In This Section
Executing a Command
Describes the ADO.NET Command object and how to use it to execute queries and commands against a data
source.
Configuring Parameters and Parameter Data Types
Describes working with Command parameters, including direction, data types, and parameter syntax.
Generating Commands with CommandBuilders
Describes how to use command builders to automatically generate INSERT, UPDATE, and DELETE commands
for a DataAdapter that has a single-table SELECT command.
Obtaining a Single Value from a Database
Describes how to use the ExecuteScalar method of a Command object to return a single value from a database
query.
Using Commands to Modify Data
Describes how to use a data provider to execute stored procedures or data definition language (DDL ) statements.
See also
DataAdapters and DataReaders
DataSets, DataTables, and DataViews
Connecting to a Data Source
ADO.NET Managed Providers and DataSet Developer Center
Executing a Command
4/8/2019 • 2 minutes to read • Edit Online
Each .NET Framework data provider included with the .NET Framework has its own command object that inherits
from DbCommand. The .NET Framework Data Provider for OLE DB includes an OleDbCommand object, the
.NET Framework Data Provider for SQL Server includes a SqlCommand object, the .NET Framework Data
Provider for ODBC includes an OdbcCommand object, and the .NET Framework Data Provider for Oracle
includes an OracleCommand object. Each of these objects exposes methods for executing commands based on the
type of command and desired return value, as described in the following table.
Each strongly typed command object also supports a CommandType enumeration that specifies how a command
string is interpreted, as described in the following table.
COMMANDTYPE DESCRIPTION
StoredProcedure The name of the stored procedure. You can use the
Parameters property of a command to access input and
output parameters and return values, regardless of which
Execute method is called. When using ExecuteReader ,
return values and output parameters will not be accessible
until the DataReader is closed.
Example
The following code example demonstrates how to create a SqlCommand object to execute a stored procedure by
setting its properties. A SqlParameter object is used to specify the input parameter to the stored procedure. The
command is executed using the ExecuteReader method, and the output from the SqlDataReader is displayed in the
console window.
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;
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
Troubleshooting Commands
The .NET Framework Data Provider for SQL Server adds performance counters to enable you to detect
intermittent problems related to failed command executions. For more information see Performance Counters.
See also
Commands and Parameters
DataAdapters and DataReaders
ADO.NET Overview
Configuring parameters and parameter data types
5/18/2019 • 9 minutes to read • Edit Online
Command objects use parameters to pass values to SQL statements or stored procedures, providing type
checking and validation. Unlike command text, parameter input is treated as a literal value, not as executable code.
This helps guard against "SQL injection" attacks, in which an attacker inserts a command that compromises
security on the server into an SQL statement.
Parameterized commands can also improve query execution performance, because they help the database server
accurately match the incoming command with a proper cached query plan. For more information, see Execution
Plan Caching and Reuse and Parameters and Execution Plan Reuse. In addition to the security and performance
benefits, parameterized commands provide a convenient method for organizing values passed to a data source.
A DbParameter object can be created by using its constructor, or by adding it to the DbParameterCollection by
calling the Add method of the DbParameterCollection collection. The Add method will take as input either
constructor arguments or an existing parameter object, depending on the data provider.
.NET FRAMEWORK
TYPE DBTYPE SQLDBTYPE OLEDBTYPE ODBCTYPE ORACLETYPE
NOTE
Conversions from decimal to other types are narrowing conversions that round the decimal value to the nearest integer
value toward zero. If the result of the conversion is not representable in the destination type, an OverflowException is
thrown.
NOTE
When you send a null parameter value to the server, you must specify DBNull, not null ( Nothing in Visual Basic). The
null value in the system is an empty object that has no value. DBNull is used to represent null values. For more information
about database nulls, see Handling Null Values.
NOTE
Deriving parameter information incurs a performance penalty because it requires an additional round trip to the data source
to retrieve the information. If parameter information is known at design time, you can improve the performance of your
application by setting the parameters explicitly.
When using parameters with a SqlCommand to execute a SQL Server stored procedure, the names of the
parameters added to the Parameters collection must match the names of the parameter markers in the stored
procedure. The .NET Framework Data Provider for SQL Server does not support the question mark (?)
placeholder for passing parameters to an SQL statement or a stored procedure. It treats parameters in the stored
procedure as named parameters and searches for matching parameter markers. For example, the CustOrderHist
stored procedure is defined by using a parameter named @CustomerID . When your code executes the stored
procedure, it must also use a parameter named @CustomerID .
Example
This example demonstrates how to call a SQL Server stored procedure in the Northwind sample database. The
name of the stored procedure is dbo.SalesByCategory and it has an input parameter named @CategoryName with a
data type of nvarchar(15) . The code creates a new SqlConnection inside a using block so that the connection is
disposed when the procedure ends. The SqlCommand and SqlParameter objects are created, and their properties
set. A SqlDataReader executes the SqlCommand and returns the result set from the stored procedure, displaying the
output in the console window.
NOTE
Instead of creating SqlCommand and SqlParameter objects and then setting properties in separate statements, you can
instead elect to use one of the overloaded constructors to set multiple properties in a single statement.
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;
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
As a result, the order in which Parameter objects are added to the Parameters collection must directly correspond
to the position of the ? placeholder for the parameter.
OleDb Example
Dim command As OleDbCommand = New OleDbCommand( _
"SampleProc", connection)
command.CommandType = CommandType.StoredProcedure
parameter = command.Parameters.Add( _
"@InputParm", OleDbType.VarChar, 12)
parameter.Value = "Sample Value"
parameter = command.Parameters.Add( _
"@OutputParm", OleDbType.VarChar, 28)
parameter.Direction = ParameterDirection.Output
parameter = command.Parameters.Add(
"@InputParm", OleDbType.VarChar, 12);
parameter.Value = "Sample Value";
parameter = command.Parameters.Add(
"@OutputParm", OleDbType.VarChar, 28);
parameter.Direction = ParameterDirection.Output;
Odbc Example
Dim command As OdbcCommand = New OdbcCommand( _
"{ ? = CALL SampleProc(?, ?) }", connection)
command.CommandType = CommandType.StoredProcedure
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;
parameter = command.Parameters.Add( _
"@InputParm", OdbcType.VarChar, 12);
parameter.Value = "Sample Value";
parameter = command.Parameters.Add( _
"@OutputParm", OdbcType.VarChar, 28);
parameter.Direction = ParameterDirection.Output;
See also
Commands and Parameters
DataAdapter Parameters
Data Type Mappings in ADO.NET
ADO.NET Overview
Generating Commands with CommandBuilders
4/8/2019 • 6 minutes to read • Edit Online
When the SelectCommand property is dynamically specified at run time, such as through a query tool that takes a
textual command from the user, you may not be able to specify the appropriate InsertCommand , UpdateCommand , or
DeleteCommand at design time. If your DataTable maps to or is generated from a single database table, you can take
advantage of the DbCommandBuilder object to automatically generate the DeleteCommand , InsertCommand , and
UpdateCommand of the DbDataAdapter.
As a minimum requirement, you must set the SelectCommand property in order for automatic command
generation to work. The table schema retrieved by the SelectCommand property determines the syntax of the
automatically generated INSERT, UPDATE, and DELETE statements.
The DbCommandBuilder must execute the SelectCommand in order to return the metadata necessary to construct
the INSERT, UPDATE, and DELETE SQL commands. As a result, an extra trip to the data source is necessary, and
this can hinder performance. To achieve optimal performance, specify your commands explicitly rather than using
the DbCommandBuilder.
The SelectCommand must also return at least one primary key or unique column. If none are present, an
InvalidOperation exception is generated, and the commands are not generated.
When associated with a DataAdapter , the DbCommandBuilder automatically generates the InsertCommand ,
UpdateCommand , and DeleteCommand properties of the DataAdapter if they are null references. If a Command already
exists for a property, the existing Command is used.
Database views that are created by joining two or more tables together are not considered a single database table.
In this instance you cannot use the DbCommandBuilder to automatically generate commands; you must specify
your commands explicitly. For information about explicitly setting commands to resolve updates to a DataSet
back to the data source, see Updating Data Sources with DataAdapters.
You might want to map output parameters back to the updated row of a DataSet . One common task would be
retrieving the value of an automatically generated identity field or time stamp from the data source. The
DbCommandBuilder will not map output parameters to columns in an updated row by default. In this instance
you must specify your command explicitly. For an example of mapping an automatically generated identity field
back to a column of an inserted row, see Retrieving Identity or Autonumber Values.
COMMAND RULE
InsertCommand Inserts a row at the data source for all rows in the table with a
RowState of Added. Inserts values for all columns that are
updateable (but not columns such as identities, expressions,
or timestamps).
COMMAND RULE
UpdateCommand Updates rows at the data source for all rows in the table with
a RowState of Modified. Updates the values of all columns
except for columns that are not updateable, such as identities
or expressions. Updates all rows where the column values at
the data source match the primary key column values of the
row, and where the remaining columns at the data source
match the original values of the row. For more information,
see "Optimistic Concurrency Model for Updates and Deletes,"
later in this topic.
DeleteCommand Deletes rows at the data source for all rows in the table with a
RowState of Deleted. Deletes all rows where the column
values match the primary key column values of the row, and
where the remaining columns at the data source match the
original values of the row. For more information, see
"Optimistic Concurrency Model for Updates and Deletes,"
later in this topic.
The following code example writes to the console the update command that was automatically generated.
Console.WriteLine(builder.GetUpdateCommand().CommandText)
Console.WriteLine(builder.GetUpdateCommand().CommandText);
The following example recreates the Customers table in the custDS dataset. The RefreshSchema method is
called to refresh the automatically generated commands with this new column information.
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");
See also
Commands and Parameters
Executing a Command
DbConnection, DbCommand and DbException
ADO.NET Managed Providers and DataSet Developer Center
Obtaining a Single Value from a Database
4/8/2019 • 2 minutes to read • Edit Online
You may need to return database information that is simply a single value rather than in the form of a table or data
stream. For example, you may want to return the result of an aggregate function such as COUNT(*), SUM (Price),
or AVG (Quantity). The Command object provides the capability to return single values using the ExecuteScalar
method. The ExecuteScalar method returns, as a scalar value, the value of the first column of the first row of the
result set.
The following code example inserts a new value in the database using a SqlCommand. The ExecuteScalar method
is used to return the identity column value for the inserted record.
Return newProdID
End Function
See also
Commands and Parameters
Executing a Command
DbConnection, DbCommand and DbException
ADO.NET Managed Providers and DataSet Developer Center
Using Commands to Modify Data
4/8/2019 • 2 minutes to read • Edit Online
Using a .NET Framework data provider, you can execute stored procedures or data definition language statements
(for example, CREATE TABLE and ALTER COLUMN ) to perform schema manipulation on a database or catalog.
These commands do not return rows as a query would, so the Command object provides an ExecuteNonQuery
to process them.
In addition to using ExecuteNonQuery to modify schema, you can also use this method to process SQL
statements that modify data but that do not return rows, such as INSERT, UPDATE, and DELETE.
Although rows are not returned by the ExecuteNonQuery method, input and output parameters and return
values can be passed and returned via the Parameters collection of the Command object.
In This Section
Updating Data in a Data Source
Describes how to execute commands or stored procedures that modify data in a database.
Performing Catalog Operations
Describes how to execute commands that modify database schema.
See also
Retrieving and Modifying Data in ADO.NET
Commands and Parameters
ADO.NET Managed Providers and DataSet Developer Center
Updating Data in a Data Source
4/8/2019 • 2 minutes to read • Edit Online
SQL statements that modify data (such as INSERT, UPDATE, or DELETE ) do not return rows. Similarly, many
stored procedures perform an action but do not return rows. To execute commands that do not return rows, create
a Command object with the appropriate SQL command and a Connection, including any required Parameters.
Execute the command with the ExecuteNonQuery method of the Command object.
The ExecuteNonQuery method returns an integer that represents the number of rows affected by the statement
or stored procedure that was executed. If multiple statements are executed, the value returned is the sum of the
records affected by all of the statements executed.
Example
The following code example executes an INSERT statement to insert a record into a database using
ExecuteNonQuery.
The following code example executes the stored procedure created by the sample code in Performing Catalog
Operations. No rows are returned by the stored procedure, so the ExecuteNonQuery method is used, but the
stored procedure does receive an input parameter and returns an output parameter and a return value.
For the OleDbCommand object, the ReturnValue parameter must be added to the Parameters collection first.
' Assumes connection is a valid SqlConnection.
Dim command As SqlCommand = _
New SqlCommand("InsertCategory" , connection)
command.CommandType = CommandType.StoredProcedure
parameter = command.Parameters.Add( _
"@CategoryName", SqlDbType.NChar, 15)
parameter = command.Parameters.Add(
"@CategoryName", SqlDbType.NChar, 15);
See also
Using Commands to Modify Data
Updating Data Sources with DataAdapters
Commands and Parameters
ADO.NET Managed Providers and DataSet Developer Center
Performing Catalog Operations
4/8/2019 • 2 minutes to read • Edit Online
To execute a command to modify a database or catalog, such as the CREATE TABLE or CREATE PROCEDURE
statement, create a Command object using the appropriate SQL statements and a Connection object. Execute
the command with the ExecuteNonQuery method of the Command object.
The following code example creates a stored procedure in a Microsoft SQL Server database.
See also
Using Commands to Modify Data
Commands and Parameters
ADO.NET Managed Providers and DataSet Developer Center
DataAdapters and DataReaders
4/8/2019 • 2 minutes to read • Edit Online
You can use the ADO.NET DataReader to retrieve a read-only, forward-only stream of data from a database.
Results are returned as the query executes, and are stored in the network buffer on the client until you request
them using the Read method of the DataReader. Using the DataReader can increase application performance
both by retrieving data as soon as it is available, and (by default) storing only one row at a time in memory,
reducing system overhead.
A DataAdapter is used to retrieve data from a data source and populate tables within a DataSet. The
DataAdapter also resolves changes made to the DataSet back to the data source. The DataAdapter uses the
Connection object of the .NET Framework data provider to connect to a data source, and it uses Command
objects to retrieve data from and resolve changes to the data source.
Each .NET Framework data provider included with the .NET Framework has a DbDataReader and a
DbDataAdapter object: the .NET Framework Data Provider for OLE DB includes an OleDbDataReader and an
OleDbDataAdapter object, the .NET Framework Data Provider for SQL Server includes a SqlDataReader and a
SqlDataAdapter object, the .NET Framework Data Provider for ODBC includes an OdbcDataReader and an
OdbcDataAdapter object, and the .NET Framework Data Provider for Oracle includes an OracleDataReader and
an OracleDataAdapter object.
In This Section
Retrieving Data Using a DataReader
Describes the ADO.NET DataReader object and how to use it to return a stream of results from a data source.
Populating a DataSet from a DataAdapter
Describes how to fill a DataSet with tables, columns, and rows by using a DataAdapter .
DataAdapter Parameters
Describes how to use parameters with the command properties of a DataAdapter including how to map the
contents of a column in a DataSet to a command parameter.
Adding Existing Constraints to a DataSet
Describes how to add existing constraints to a DataSet .
DataAdapter DataTable and DataColumn Mappings
Describes how to set up DataTableMappings and ColumnMappings for a DataAdapter .
Paging Through a Query Result
Provides an example of viewing the results of a query as pages of data.
Updating Data Sources with DataAdapters
Describes how to use a DataAdapter to resolve changes in a DataSet back to the database.
Handling DataAdapter Events
Describes DataAdapter events and how to use them.
Performing Batch Operations Using DataAdapters
Describes enhancing application performance by reducing the number of round trips to SQL Server when
applying updates from the DataSet .
See also
Connecting to a Data Source
Commands and Parameters
Transactions and Concurrency
DataSets, DataTables, and DataViews
ADO.NET Managed Providers and DataSet Developer Center
Retrieve data using a DataReader
4/8/2019 • 8 minutes to read • Edit Online
To retrieve data using a DataReader, create an instance of the Command object, and then create a DataReader
by calling Command.ExecuteReader to retrieve rows from a data source. The DataReader provides an
unbuffered stream of data that allows procedural logic to efficiently process results from a data source
sequentially. The DataReader is a good choice when you're retrieving large amounts of data because the data is
not cached in memory.
The following example illustrates using a DataReader, where reader represents a valid DataReader and
command represents a valid Command object.
reader = command.ExecuteReader();
reader = command.ExecuteReader()
Use the DataReader.Read method to obtain a row from the query results. You can access each column of the
returned row by passing the name or ordinal number of the column to the DataReader. However, for best
performance, the DataReader provides a series of methods that allow you to access column values in their native
data types (GetDateTime, GetDouble, GetGuid, GetInt32, and so on). For a list of typed accessor methods for
data provider-specific DataReaders, see OleDbDataReader and SqlDataReader. Using the typed accessor
methods when you know the underlying data type reduces the amount of type conversion required when
retrieving the column value.
The following example iterates through a DataReader object and returns two columns from each row.
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()
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
NOTE
Do not call Close or Dispose on a Connection, a DataReader, or any other managed object in the Finalize method of
your class. In a finalizer, only release unmanaged resources that your class owns directly. If your class does not own any
unmanaged resources, do not include a Finalize method in your class definition. For more information, see Garbage
Collection.
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();
}
}
}
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
connection.Open()
Do While custReader.Read()
Console.WriteLine("Orders for " & custReader.GetString(1))
' custReader.GetString(1) = CompanyName
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();
while (custReader.Read())
{
Console.WriteLine("Orders for " + custReader.GetString(1));
// custReader.GetString(1) = CompanyName
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();
}
}
}
The following code creates an OracleCommand that returns the REF CURSORs from the previous Oracle
package by adding two parameters of type OracleType.Cursor to the OracleCommand.Parameters collection.
The following code returns the results of the previous command using the Read() and NextResult() methods of the
OracleDataReader. The REF CURSOR parameters are returned in order.
oraConn.Open()
Do While reader.Read()
Console.WriteLine("{0}" & vbTab & "{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1),
reader.GetString(2))
Loop
reader.NextResult()
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();
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();
The following example uses the previous command to populate a DataSet with the results of the Oracle package.
adapter.Fill(ds)
adapter.Fill(ds);
NOTE
To avoid an OverflowException, we recommend that you also handle any conversion from the Oracle NUMBER type to a
valid .NET Framework type before storing the value in a DataRow. You can use the FillError event to determine if an
OverflowException has occurred. For more information on the FillError event, see Handling DataAdapter Events.
See also
DataAdapters and DataReaders
Commands and Parameters
Retrieving Database Schema Information
ADO.NET Managed Providers and DataSet Developer Center
Populating a DataSet from a DataAdapter
5/18/2019 • 9 minutes to read • Edit Online
The ADO.NET DataSet is a memory-resident representation of data that provides a consistent relational
programming model independent of the data source. The DataSet represents a complete set of data that includes
tables, constraints, and relationships among the tables. Because the DataSet is independent of the data source, a
DataSet can include data local to the application, and data from multiple data sources. Interaction with existing
data sources is controlled through the DataAdapter .
The SelectCommand property of the DataAdapter is a Command object that retrieves data from the data source. The
InsertCommand , UpdateCommand , and DeleteCommand properties of the DataAdapter are Command objects that
manage updates to the data in the data source according to modifications made to the data in the DataSet . These
properties are covered in more detail in Updating Data Sources with DataAdapters.
The Fillmethod of the DataAdapter is used to populate a DataSet with the results of the SelectCommand of the
DataAdapter . Fill takes as its arguments a DataSet to be populated, and a DataTable object, or the name of the
DataTable to be filled with the rows returned from the SelectCommand .
NOTE
Using the DataAdapter to retrieve all of a table takes time, especially if there are many rows in the table. This is because
accessing the database, locating and processing the data, and then transferring the data to the client is time-consuming.
Pulling all of the table to the client also locks all of the rows on the server. To improve performance, you can use the WHERE
clause to greatly reduce the number of rows returned to the client. You can also reduce the amount of data returned to the
client by only explicitly listing required columns in the SELECT statement. Another good workaround is to retrieve the rows
in batches (such as several hundred rows at a time) and only retrieve the next batch when the client is finished with the
current batch.
The Fill method uses the DataReader object implicitly to return the column names and types that are used to
create the tables in the DataSet , and the data to populate the rows of the tables in the DataSet . Tables and
columns are only created if they do not already exist; otherwise Fill uses the existing DataSet schema. Column
types are created as .NET Framework types according to the tables in Data Type Mappings in ADO.NET. Primary
keys are not created unless they exist in the data source and DataAdapter . MissingSchemaAction is set to
MissingSchemaAction . AddWithKey . If Fill finds that a primary key exists for a table, it will overwrite data in the
DataSet with data from the data source for rows where the primary key column values match those of the row
returned from the data source. If no primary key is found, the data is appended to the tables in the DataSet . Fill
uses any mappings that may exist when you populate the DataSet (see DataAdapter DataTable and DataColumn
Mappings).
NOTE
If the SelectCommand returns the results of an OUTER JOIN, the DataAdapter does not set a PrimaryKey value for the
resulting DataTable . You must define the PrimaryKey yourself to make sure that duplicate rows are resolved correctly.
For more information, see Defining Primary Keys.
The following code example creates an instance of a SqlDataAdapter that uses a SqlConnection to the Microsoft
SQL Server Northwind database and populates a DataTable in a DataSet with the list of customers. The SQL
statement and SqlConnection arguments passed to the SqlDataAdapter constructor are used to create the
SelectCommand property of the SqlDataAdapter.
Example
' 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)
NOTE
The code shown in this example does not explicitly open and close the Connection . The Fill method implicitly opens the
Connection that the DataAdapter is using if it finds that the connection is not already open. If Fill opened the
connection, it also closes the connection when Fill is finished. This can simplify your code when you deal with a single
operation such as a Fill or an Update . However, if you are performing multiple operations that require an open
connection, you can improve the performance of your application by explicitly calling the Open method of the Connection
, performing the operations against the data source, and then calling the Close method of the Connection . You should
try to keep connections to the data source open as briefly as possible to free resources for use by other client applications.
custAdapter.Fill(customerOrders, "Customers");
ordAdapter.Fill(customerOrders, "Orders");
OLE DB Chapters
Hierarchical rowsets, or chapters (OLE DB type DBTYPE_HCHAPTER , ADO type adChapter ) can be used to fill the
contents of a DataSet . When the OleDbDataAdapter encounters a chaptered column during a Fill operation, a
DataTable is created for the chaptered column, and that table is filled with the columns and rows from the chapter.
The table created for the chaptered column is named by using both the parent table name and the chaptered
column name in the form "ParentTableNameChapteredColumnName". If a table already exists in the DataSet
that matches the name of the chaptered column, the current table is filled with the chapter data. If there is no
column in an existing table that matches a column found in the chapter, a new column is added.
Before the tables in the DataSet are filled with the data in the chaptered columns, a relation is created between the
parent and child tables of the hierarchical rowset by adding an integer column to both the parent and child table,
setting the parent column to auto-increment, and creating a DataRelation using the added columns from both
tables. The added relation is named by using the parent table and chapter column names in the form
"ParentTableNameChapterColumnName".
Note that the related column only exists in the DataSet . Subsequent fills from the data source can cause new rows
to be added to the tables instead of changes being merged into existing rows.
Note also that, if you use the DataAdapter.Fill overload that takes a DataTable , only that table will be filled. An
auto-incrementing integer column will still be added to the table, but no child table will be created or filled, and no
relation will be created.
The following example uses the MSDataShape Provider to generate a chapter column of orders for each customer
in a list of customers. A DataSet is then filled with the data.
adapter.Fill(customers, "Customers")
End Using
When the Fill operation is complete, the DataSet contains two tables: Customers and CustomersOrders , where
CustomersOrders represents the chaptered column. An additional column named Orders is added to the
Customers table, and an additional column named CustomersOrders is added to the CustomersOrders table. The
Orders column in the Customers table is set to auto-increment. A DataRelation , CustomersOrders , is created by
using the columns that were added to the tables with Customers as the parent table. The following tables show
some sample results.
TableName: Customers
CUSTOMERID COMPANYNAME ORDERS
TableName: CustomersOrders
CUSTOMERID ORDERID CUSTOMERSORDERS
ALFKI 10643 0
ALFKI 10692 0
ANATR 10308 1
ANATR 10625 1
See also
DataAdapters and DataReaders
Data Type Mappings in ADO.NET
Modifying Data with a DbDataAdapter
Multiple Active Result Sets (MARS )
ADO.NET Managed Providers and DataSet Developer Center
DataAdapter Parameters
5/14/2019 • 7 minutes to read • Edit Online
The DbDataAdapter has four properties that are used to retrieve data from and update data to the data source: the
SelectCommand property returns data from the data source; and the InsertCommand , UpdateCommand, and
DeleteCommand properties are used to manage changes at the data source. The SelectCommand property must be
set before you call the Fill method of the DataAdapter . The InsertCommand , UpdateCommand , or DeleteCommand
properties must be set before the Update method of the DataAdapter is called, depending on what changes were
made to the data in the DataTable. For example, if rows have been added, the InsertCommand must be set before
you call Update . When Update is processing an inserted, updated, or deleted row, the DataAdapter uses the
respective Command property to process the action. Current information about the modified row is passed to the
Command object through the Parameters collection.
When you update a row at the data source, you call the UPDATE statement, which uses a unique identifier to
identify the row in the table to be updated. The unique identifier is typically the value of a primary key field. The
UPDATE statement uses parameters that contain both the unique identifier and the columns and values to be
updated, as shown in the following Transact-SQL statement.
NOTE
The syntax for parameter placeholders depends on the data source. This example shows placeholders for a SQL Server data
source. Use question mark (?) placeholders for System.Data.OleDb and System.Data.Odbc parameters.
In this Visual Basic example, the CompanyName field is updated with the value of the @CompanyName parameter for
the row where CustomerID equals the value of the @CustomerID parameter. The parameters retrieve information
from the modified row using the SourceColumn property of the SqlParameter object. The following are the
parameters for the previous sample UPDATE statement. The code assumes that the variable adapter represents a
valid SqlDataAdapter object.
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
The Add method of the Parameters collection takes the name of the parameter, the data type, the size (if
applicable to the type), and the name of the SourceColumn from the DataTable . Notice that the SourceVersion of
the @CustomerID parameter is set to Original . This guarantees that the existing row in the data source is updated
if the value of the identifying column or columns has been changed in the modified DataRow. In that case, the
Original row value would match the current value at the data source, and the Current row value would contain
the updated value. The SourceVersion for the @CompanyName parameter is not set and uses the default, Current
row value.
NOTE
For both the Fill operations of the DataAdapter and the Get methods of the DataReader , the .NET Framework type
is inferred from the type returned from the .NET Framework data provider. The inferred .NET Framework types and accessor
methods for Microsoft SQL Server, OLE DB, and ODBC data types are described in Data Type Mappings in ADO.NET.
Parameter.SourceColumn, Parameter.SourceVersion
The SourceColumn and SourceVersion may be passed as arguments to the Parameter constructor, or set as
properties of an existing Parameter . The SourceColumn is the name of the DataColumn from the DataRow where
the value of the Parameter will be retrieved. The SourceVersion specifies the DataRow version that the
DataAdapter uses to retrieve the value.
The following table shows the DataRowVersion enumeration values available for use with SourceVersion .
Current The parameter uses the current value of the column. This is
the default.
The SqlClient code example in the next section defines a parameter for an UpdateCommand in which the
CustomerID column is used as a SourceColumn for two parameters: @CustomerID ( SET CustomerID = @CustomerID ),
and @OldCustomerID ( WHERE CustomerID = @OldCustomerID ). The @CustomerID parameter is used to update the
CustomerID column to the current value in the DataRow . As a result, the CustomerID SourceColumn with a
SourceVersion of Current is used. The @OldCustomerID parameter is used to identify the current row in the data
source. Because the matching column value is found in the Original version of the row, the same SourceColumn (
CustomerID ) with a SourceVersion of Original is used.
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
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
The parameterized query statements define which input and output parameters must be created. To create a
parameter, use the Parameters.Add method or the Parameter constructor to specify the column name, data type,
and size. For intrinsic data types, such as Integer , you do not have to include the size, or you can specify the
default size.
The following code example creates the parameters for a SQL statement and then fills a DataSet .
OleDb Example
' Assumes that connection is a valid OleDbConnection object.
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter
Odbc Parameters
' Assumes that connection is a valid OdbcConnection object.
Dim adapter As OdbcDataAdapter = New OdbcDataAdapter
NOTE
If a parameter name is not supplied for a parameter, the parameter is given an incremental default name of ParameterN ,
starting with "Parameter1". We recommend that you avoid the ParameterN naming convention when you supply a
parameter name, because the name that you supply might conflict with an existing default parameter name in the
ParameterCollection . If the supplied name already exists, an exception is thrown.
See also
DataAdapters and DataReaders
Commands and Parameters
Updating Data Sources with DataAdapters
Modifying Data with Stored Procedures
Data Type Mappings in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Adding Existing Constraints to a DataSet
4/8/2019 • 2 minutes to read • Edit Online
The Fill method of the DataAdapter fills a DataSet only with table columns and rows from a data source; though
constraints are commonly set by the data source, the Fill method does not add this schema information to the
DataSet by default. To populate a DataSet with existing primary key constraint information from a data source,
you can either call the FillSchema method of the DataAdapter, or set the MissingSchemaAction property of
the DataAdapter to AddWithKey before calling Fill. This will ensure that primary key constraints in the DataSet
reflect those at the data source. Foreign key constraint information is not included and must be created explicitly, as
shown in DataTable Constraints.
Adding schema information to a DataSet before filling it with data ensures that primary key constraints are
included with the DataTable objects in the DataSet. As a result, when additional calls to fill the DataSet are made,
the primary key column information is used to match new rows from the data source with current rows in each
DataTable, and current data in the tables is overwritten with data from the data source. Without the schema
information, the new rows from the data source are appended to the DataSet, resulting in duplicate rows.
NOTE
If a column in a data source is identified as auto-incrementing, the FillSchema method, or the Fill method with a
MissingSchemaAction of AddWithKey, creates a DataColumn with an AutoIncrement property set to true . However,
you will need to set the AutoIncrementStep and AutoIncrementSeed values yourself. For more information about auto-
incrementing columns, see Creating AutoIncrement Columns.
Using FillSchema or setting the MissingSchemaAction to AddWithKey requires extra processing at the data
source to determine primary key column information. This additional processing can hinder performance. If you
know the primary key information at design time, we recommend that you explicitly specify the primary key
column or columns in order to achieve optimal performance. For information about explicitly setting primary key
information for a table, see Defining Primary Keys.
The following code example shows how to add schema information to a DataSet using FillSchema.
The following code example shows how to add schema information to a DataSet using the
MissingSchemaAction.AddWithKey property of the Fill method.
custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
custAdapter.Fill(custDataSet, "Customers")
DataSet custDataSet = new DataSet();
custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
custAdapter.Fill(custDataSet, "Customers");
NOTE
If the FillSchema method of the OleDbDataAdapter object is called for a command that returns multiple result sets, only
the schema information from the first result set is returned. When returning schema information for multiple result sets using
the OleDbDataAdapter, it is recommended that you specify a MissingSchemaAction of AddWithKey and obtain the
schema information when calling the Fill method.
See also
DataAdapters and DataReaders
DataSets, DataTables, and DataViews
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
DataAdapter DataTable and DataColumn Mappings
4/8/2019 • 3 minutes to read • Edit Online
A DataAdapter contains a collection of zero or more DataTableMapping objects in its TableMappings property.
A DataTableMapping provides a master mapping between the data returned from a query against a data source,
and a DataTable. The DataTableMapping name can be passed in place of the DataTable name to the Fill
method of the DataAdapter. The following example creates a DataTableMapping named AuthorsMapping for
the Authors table.
workAdapter.TableMappings.Add("AuthorsMapping", "Authors")
workAdapter.TableMappings.Add("AuthorsMapping", "Authors");
A DataTableMapping enables you to use column names in a DataTable that are different from those in the
database. The DataAdapter uses the mapping to match the columns when the table is updated.
If you do not specify a TableName or a DataTableMapping name when calling the Fill or Update method of
the DataAdapter, the DataAdapter looks for a DataTableMapping named "Table". If that DataTableMapping
does not exist, the TableName of the DataTable is "Table". You can specify a default DataTableMapping by
creating a DataTableMapping with the name of "Table".
The following code example creates a DataTableMapping (from the System.Data.Common namespace) and
makes it the default mapping for the specified DataAdapter by naming it "Table". The example then maps the
columns from the first table in the query result (the Customers table of the Northwind database) to a set of more
user-friendly names in the Northwind Customers table in the DataSet. For columns that are not mapped, the
name of the column from the data source is used.
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);
In more advanced situations, you may decide that you want the same DataAdapter to support loading different
tables with different mappings. To do this, simply add additional DataTableMapping objects.
When the Fill method is passed an instance of a DataSet and a DataTableMapping name, if a mapping with
that name exists it is used; otherwise, a DataTable with that name is used.
The following examples create a DataTableMapping with a name of Customers and a DataTable name of
BizTalkSchema. The example then maps the rows returned by the SELECT statement to the BizTalkSchema
DataTable.
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
If a source column name is not supplied for a column mapping or a source table name is not supplied for a table mapping,
default names will be automatically generated. If no source column is supplied for a column mapping, the column mapping is
given an incremental default name of SourceColumn N, starting with SourceColumn1. If no source table name is supplied
for a table mapping, the table mapping is given an incremental default name of SourceTable N, starting with
SourceTable1.
NOTE
We recommend that you avoid the naming convention of SourceColumn N for a column mapping, or SourceTable N for a
table mapping, because the name you supply may conflict with an existing default column mapping name in the
ColumnMappingCollection or table mapping name in the DataTableMappingCollection. If the supplied name already
exists, an exception will be thrown.
adapter.Fill(customersDataSet, "Customers")
Two tables are created in the DataSet: Customers and Customers1. You can use table mappings to ensure that
the second table is named Orders instead of Customers1. To do this, map the source table of Customers1 to the
DataSet table Orders, as shown in the following example.
adapter.TableMappings.Add("Customers1", "Orders")
adapter.Fill(customersDataSet, "Customers")
See also
DataAdapters and DataReaders
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Paging Through a Query Result
4/8/2019 • 3 minutes to read • Edit Online
Paging through a query result is the process of returning the results of a query in smaller subsets of data, or pages.
This is a common practice for displaying results to a user in small, easy-to-manage chunks.
The DataAdapter provides a facility for returning only a page of data, through overloads of the Fill method.
However, this might not be the best choice for paging through large query results because, although the
DataAdapter fills the target DataTable or DataSet with only the requested records, the resources to return the
entire query are still used. To return a page of data from a data source without using the resources to return the
entire query, specify additional criteria for your query that reduce the rows returned to only those required.
To use the Fill method to return a page of data, specify a startRecord parameter, for the first record in the page of
data, and a maxRecords parameter, for the number of records in the page of data.
The following code example shows how to use the Fill method to return the first page of a query result where the
page size is five records.
int currentIndex = 0;
int pageSize = 5;
In the previous example, the DataSet is only filled with five records, but the entire Orders table is returned. To fill
the DataSet with those same five records, but only return five records, use the TOP and WHERE clauses in your
SQL statement, as in the following code example.
Note that, when paging through the query results in this way, you must preserve the unique identifier that orders
the rows, in order to pass the unique ID to the command to return the next page of records, as shown in the
following code example.
string lastRecord =
dataSet.Tables["Orders"].Rows[pageSize - 1]["OrderID"].ToString();
To return the next page of records using the overload of the Fill method that takes the startRecord and
maxRecords parameters, increment the current record index by the page size and fill the table. Remember that the
database server returns the entire query results even though only one page of records is added to the DataSet. In
the following code example, the table rows are cleared before they are filled with the next page of data. You might
want to preserve a certain number of returned rows in a local cache to reduce trips to the database server.
dataSet.Tables("Orders").Rows.Clear()
currentIndex += pageSize;
dataSet.Tables["Orders"].Rows.Clear();
To return the next page of records without having the database server return the entire query, specify restrictive
criteria to the SELECT statement. Because the preceding example preserved the last record returned, you can use it
in the WHERE clause to specify a starting point for the query, as shown in the following code example.
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");
See also
DataAdapters and DataReaders
ADO.NET Managed Providers and DataSet Developer Center
Updating Data Sources with DataAdapters
4/28/2019 • 14 minutes to read • Edit Online
The Update method of the DataAdapter is called to resolve changes from a DataSet back to the data source. The
Update method, like the Fill method, takes as arguments an instance of a DataSet , and an optional DataTable
object or DataTable name. The DataSet instance is the DataSet that contains the changes that have been made,
and the DataTable identifies the table from which to retrieve the changes. If no DataTable is specified, the first
DataTable in the DataSet is used.
When you call the Update method, the DataAdapter analyzes the changes that have been made and executes the
appropriate command (INSERT, UPDATE, or DELETE ). When the DataAdapter encounters a change to a
DataRow, it uses the InsertCommand, UpdateCommand, or DeleteCommand to process the change. This allows
you to maximize the performance of your ADO.NET application by specifying command syntax at design time
and, where possible, through the use of stored procedures. You must explicitly set the commands before calling
Update . If Update is called and the appropriate command does not exist for a particular update (for example, no
DeleteCommand for deleted rows), an exception is thrown.
NOTE
If you are using SQL Server stored procedures to edit or delete data using a DataAdapter , make sure that you do not use
SET NOCOUNT ON in the stored procedure definition. This causes the rows affected count returned to be zero, which the
DataAdapter interprets as a concurrency conflict. In this event, a DBConcurrencyException will be thrown.
Command parameters can be used to specify input and output values for an SQL statement or stored procedure
for each modified row in a DataSet . For more information, see DataAdapter Parameters.
NOTE
It is important to understand the difference between deleting a row in a DataTable and removing the row. When you call
the Remove or RemoveAt method, the row is removed immediately. Any corresponding rows in the back end data source
will not be affected if you then pass the DataTable or DataSet to a DataAdapter and call Update . When you use the
Delete method, the row remains in the DataTable and is marked for deletion. If you then pass the DataTable or
DataSet to a DataAdapter and call Update , the corresponding row in the back end data source is deleted.
If your DataTable maps to or is generated from a single database table, you can take advantage of the
DbCommandBuilder object to automatically generate the DeleteCommand , InsertCommand , and UpdateCommand
objects for the DataAdapter . For more information, see Generating Commands with CommandBuilders.
Both Both the output parameters and the first row of a returned
result set may be mapped to the changed row in the
DataSet .
FirstReturnedRecord Only the data in the first row of a returned result set may be
mapped to the changed row in the DataSet .
The Update method resolves your changes back to the data source; however other clients may have modified
data at the data source since the last time you filled the DataSet . To refresh your DataSet with current data, use
the DataAdapter and Fill method. New rows will be added to the table, and updated information will be
incorporated into existing rows. The Fill method determines whether a new row will be added or an existing
row will be updated by examining the primary key values of the rows in the DataSet and the rows returned by
the SelectCommand . If the Fill method encounters a primary key value for a row in the DataSet that matches a
primary key value from a row in the results returned by the SelectCommand , it updates the existing row with the
information from the row returned by the SelectCommand and sets the RowState of the existing row to Unchanged .
If a row returned by the SelectCommand has a primary key value that does not match any of the primary key
values of the rows in the DataSet , the Fill method adds a new row with a RowState of Unchanged .
NOTE
If the SelectCommand returns the results of an OUTER JOIN, the DataAdapter will not set a PrimaryKey value for the
resulting DataTable . You must define the PrimaryKey yourself to ensure that duplicate rows are resolved correctly. For
more information, see Defining Primary Keys.
To handle exceptions that may occur when calling the Update method, you can use the RowUpdated event to
respond to row update errors as they occur (see Handling DataAdapter Events), or you can set
DataAdapter.ContinueUpdateOnError to true before calling Update , and respond to the error information stored
in the RowError property of a particular row when the update is complete (see Row Error Information).
Note Calling AcceptChanges on the DataSet , DataTable , or DataRow will cause all Original values for a
DataRow to be overwritten with the Current values for the DataRow . If the field values that identify the row as
unique have been modified, after calling AcceptChanges the Original values will no longer match the values in
the data source. AcceptChanges is called automatically for each row during a call to the Update method of a
DataAdapter . You can preserve the original values during a call to the Update method by first setting the
AcceptChangesDuringUpdate property of the DataAdapter to false, or by creating an event handler for the
RowUpdated event and setting the Status to SkipCurrentRow. For more information, see Merging DataSet
Contents and Handling DataAdapter Events.
Example
The following examples demonstrate how to perform updates to modified rows by explicitly setting the
UpdateCommand of a DataAdapter and calling its Update method. Notice that the parameter specified in the
WHERE clause of the UPDATE statement is set to use the Original value of the SourceColumn . This is important,
because the Current value may have been modified and may not match the value in the data source. The
Original value is the value that was used to populate the DataTable from the data source.
dataAdpater.UpdateCommand.Parameters.Add(
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
dataAdpater.Update(categoryTable);
adapter.UpdateCommand.Parameters.Add( _
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")
adapter.Update(categoryTable)
AutoIncrement Columns
If the tables from your data source have auto-incrementing columns, you can fill the columns in your DataSet
either by returning the auto-increment value as an output parameter of a stored procedure and mapping that to a
column in a table, by returning the auto-increment value in the first row of a result set returned by a stored
procedure or SQL statement, or by using the RowUpdated event of the DataAdapter to execute an additional
SELECT statement. For more information and an example, see Retrieving Identity or Autonumber Values.
Example
For example, the following code ensures that the deleted rows of the table are processed first, then the updated
rows, and then the inserted rows.
USE [master]
GO
GO
USE [MySchool]
GO
SET ANSI_NULLS ON
GO
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)
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# and Visual Basic projects with this code sample can be found on Developer Code Samples.
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Linq;
using CSDataAdapterOperations.Properties;
namespace CSDataAdapterOperations.Properties {
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
[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];
// 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);
connection.Open();
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;
// 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
// 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();
adapter.Fill(table);
}
}
}
connection.Open();
adapter.Update(table);
See also
DataAdapters and DataReaders
Row States and Row Versions
AcceptChanges and RejectChanges
Merging DataSet Contents
Retrieving Identity or Autonumber Values
ADO.NET Managed Providers and DataSet Developer Center
Handling DataAdapter Events
4/8/2019 • 6 minutes to read • Edit Online
The ADO.NET DataAdapter exposes three events that you can use to respond to changes made to data at the
data source. The following table shows the DataAdapter events.
EVENT DESCRIPTION
STATUS DESCRIPTION
SkipCurrentRow Ignore the current row and continue the update operation.
Setting the Status property to ErrorsOccurred causes an exception to be thrown. You can control which
exception is thrown by setting the Errors property to the desired exception. Using one of the other values for
Status prevents an exception from being thrown.
You can also use the ContinueUpdateOnError property to handle errors for updated rows. If
DataAdapter.ContinueUpdateOnError is true , when an update to a row results in an exception being thrown, the
text of the exception is placed into the RowError information of the particular row, and processing continues
without throwing an exception. This enables you to respond to errors when the Update is complete, in contrast to
the RowUpdated event, which enables you to respond to errors when the error is encountered.
The following code sample shows how to both add and remove event handlers. The RowUpdating event handler
writes a log of all deleted records with a time stamp. The RowUpdated event handler adds error information to the
RowError property of the row in the DataSet , suppresses the exception, and continues processing (mirroring the
behavior of ContinueUpdateOnError = true ).
' Set DataAdapter command properties, fill DataSet, and modify DataSet.
custAdapter.Update(custDS, "Customers")
// Add handlers.
custAdapter.RowUpdating += new SqlRowUpdatingEventHandler(OnRowUpdating);
custAdapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);
custAdapter.Update(custDS, "Customers");
// Remove handlers.
custAdapter.RowUpdating -= new SqlRowUpdatingEventHandler(OnRowUpdating);
custAdapter.RowUpdated -= new SqlRowUpdatedEventHandler(OnRowUpdated);
FillError
The DataAdapter issues the FillError event when an error occurs during a Fill operation. This type of error
commonly occurs when the data in the row being added could not be converted to a .NET Framework type
without some loss of precision.
If an error occurs during a Fill operation, the current row is not added to the DataTable . The FillError event
enables you to resolve the error and add the row, or to ignore the excluded row and continue the Fill operation.
The FillErrorEventArgs passed to the FillError event can contain several properties that enable you to
respond to and resolve errors. The following table shows the properties of the FillErrorEventArgs object.
PROPERTY DESCRIPTION
DataTable The DataTable object being filled when the error occurred.
PROPERTY DESCRIPTION
Values An array of objects that contains the values of the row being
added when the error occurred. The ordinal references of the
Values array correspond to the ordinal references of the
columns of the row being added. For example, Values[0] is
the value that was being added as the first column of the row.
The following code example adds an event handler for the FillError event of the DataAdapter . In the FillError
event code, the example determines if there is the potential for precision loss, providing the opportunity to
respond to the exception.
Batch support in ADO.NET allows a DataAdapter to group INSERT, UPDATE, and DELETE operations from a
DataSet or DataTable to the server, instead of sending one operation at a time. The reduction in the number of
round trips to the server typically results in significant performance gains. Batch updates are supported for the
.NET data providers for SQL Server (System.Data.SqlClient) and Oracle (System.Data.OracleClient).
When updating a database with changes from a DataSet in previous versions of ADO.NET, the Update method of
a DataAdapter performed updates to the database one row at a time. As it iterated through the rows in the
specified DataTable, it examined each DataRow to see if it had been modified. If the row had been modified, it
called the appropriate UpdateCommand , InsertCommand , or DeleteCommand , depending on the value of the RowState
property for that row. Every row update involved a network round-trip to the database.
Starting with ADO.NET 2.0, the DbDataAdapter exposes an UpdateBatchSize property. Setting the
UpdateBatchSize to a positive integer value causes updates to the database to be sent as batches of the specified
size. For example, setting the UpdateBatchSize to 10 will group 10 separate statements and submit them as single
batch. Setting the UpdateBatchSize to 0 will cause the DataAdapter to use the largest batch size that the server can
handle. Setting it to 1 disables batch updates, as rows are sent one at a time.
Executing an extremely large batch could decrease performance. Therefore, you should test for the optimum batch
size setting before implementing your application.
// Create a SqlDataAdapter.
SqlDataAdapter adapter = new SqlDataAdapter();
See also
DataAdapters and DataReaders
Updating Data Sources with DataAdapters
Handling DataAdapter Events
ADO.NET Managed Providers and DataSet Developer Center
Transactions and Concurrency
4/8/2019 • 2 minutes to read • Edit Online
A transaction consists of a single command or a group of commands that execute as a package. Transactions
allow you to combine multiple operations into a single unit of work. If a failure occurs at one point in the
transaction, all of the updates can be rolled back to their pre-transaction state.
A transaction must conform to the ACID properties—atomicity, consistency, isolation, and durability—in order to
guarantee data consistency. Most relational database systems, such as Microsoft SQL Server, support
transactions by providing locking, logging, and transaction management facilities whenever a client application
performs an update, insert, or delete operation.
NOTE
Transactions that involve multiple resources can lower concurrency if locks are held too long. Therefore, keep transactions
as short as possible.
If a transaction involves multiple tables in the same database or server, then explicit transactions in stored
procedures often perform better. You can create transactions in SQL Server stored procedures by using the
Transact-SQL BEGIN TRANSACTION , COMMIT TRANSACTION , and ROLLBACK TRANSACTION statements. For more
information, see SQL Server Books Online.
Transactions involving different resource managers, such as a transaction between SQL Server and Oracle,
require a distributed transaction.
In This Section
Local Transactions
Demonstrates how to perform transactions against a database.
Distributed Transactions
Describes how to perform distributed transactions in ADO.NET.
System.Transactions Integration with SQL Server
Describes System.Transactions integration with SQL Server for working with distributed transactions.
Optimistic Concurrency
Describes optimistic and pessimistic concurrency, and how you can test for concurrency violations.
See also
Transaction Fundamentals
Connecting to a Data Source
Commands and Parameters
DataAdapters and DataReaders
DbProviderFactories
ADO.NET Managed Providers and DataSet Developer Center
Local Transactions
5/18/2019 • 3 minutes to read • Edit Online
Transactions in ADO.NET are used when you want to bind multiple tasks together so that they execute as a single
unit of work. For example, imagine that an application performs two tasks. First, it updates a table with order
information. Second, it updates a table that contains inventory information, debiting the items ordered. If either
task fails, then both updates are rolled back.
NOTE
Transactions are most efficient when they are performed on the server. If you are working with a SQL Server database that
makes extensive use of explicit transactions, consider writing them as stored procedures using the Transact-SQL BEGIN
TRANSACTION statement.
NOTE
The EnlistDistributedTransaction method should not be used for a local transaction.
The scope of the transaction is limited to the connection. The following example performs an explicit transaction
that consists of two separate commands in the try block. The commands execute INSERT statements against the
Production.ScrapReason table in the AdventureWorks SQL Server sample database, which are committed if no
exceptions are thrown. The code in the catch block rolls back the transaction if an exception is thrown. If the
transaction is aborted or the connection is closed before the transaction has completed, it is automatically rolled
back.
Example
Follow these steps to perform a transaction.
1. Call the BeginTransaction method of the SqlConnection object to mark the start of the transaction. The
BeginTransaction method returns a reference to the transaction. This reference is assigned to the
SqlCommand objects that are enlisted in the transaction.
2. Assign the Transaction object to the Transaction property of the SqlCommand to be executed. If a
command is executed on a connection with an active transaction, and the Transaction object has not been
assigned to the Transaction property of the Command object, an exception is thrown.
3. Execute the required commands.
4. Call the Commit method of the SqlTransaction object to complete the transaction, or call the Rollback
method to end the transaction. If the connection is closed or disposed before either the Commit or Rollback
methods have been executed, the transaction is rolled back.
The following code example demonstrates transactional logic using ADO.NET with Microsoft SQL Server.
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();
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()
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()
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()
See also
Transactions and Concurrency
Distributed Transactions
System.Transactions Integration with SQL Server
ADO.NET Managed Providers and DataSet Developer Center
Distributed Transactions
4/8/2019 • 4 minutes to read • Edit Online
A transaction is a set of related tasks that either succeeds (commit) or fails (abort) as a unit, among other things. A
distributed transaction is a transaction that affects several resources. For a distributed transaction to commit, all
participants must guarantee that any change to data will be permanent. Changes must persist despite system
crashes or other unforeseen events. If even a single participant fails to make this guarantee, the entire transaction
fails, and any changes to data within the scope of the transaction are rolled back.
NOTE
An exception will be thrown if you attempt to commit or roll back a transaction if a DataReader is started while the
transaction is active.
NOTE
The maximum number of distributed transactions that an Oracle database can participate in at one time is set to 10 by
default. After the 10th transaction when connected to an Oracle database, an exception is thrown. Oracle does not support
DDL inside of a distributed transaction.
NOTE
Once a connection is explicitly enlisted on a transaction, it cannot be un-enlisted or enlisted in another transaction until the
first transaction finishes.
Cau t i on
EnlistTransaction throws an exception if the connection has already begun a transaction using the connection's
BeginTransaction method. However, if the transaction is a local transaction started at the data source (for example,
executing the BEGIN TRANSACTION statement explicitly using a SqlCommand), EnlistTransaction will roll back
the local transaction and enlist in the existing distributed transaction as requested. You will not receive notice that
the local transaction was rolled back, and must manage any local transactions not started using BeginTransaction.
If you are using the .NET Framework Data Provider for SQL Server ( SqlClient ) with SQL Server, an attempt to
enlist will throw an exception. All other cases will go undetected.
See also
Transactions and Concurrency
System.Transactions Integration with SQL Server
ADO.NET Managed Providers and DataSet Developer Center
System.Transactions Integration with SQL Server
5/18/2019 • 9 minutes to read • Edit Online
The .NET Framework version 2.0 introduced a transaction framework that can be accessed through the
System.Transactions namespace. This framework exposes transactions in a way that is fully integrated in the .NET
Framework, including ADO.NET.
In addition to the programmability enhancements, System.Transactions and ADO.NET can work together to
coordinate optimizations when you work with transactions. A promotable transaction is a lightweight (local)
transaction that can be automatically promoted to a fully distributed transaction on an as-needed basis.
Starting with ADO.NET 2.0, System.Data.SqlClient supports promotable transactions when you work with SQL
Server. A promotable transaction does not invoke the added overhead of a distributed transaction unless the
added overhead is required. Promotable transactions are automatic and require no intervention from the
developer.
Promotable transactions are only available when you use the .NET Framework Data Provider for SQL Server (
SqlClient ) with SQL Server.
NOTE
In a partially trusted scenario, the DistributedTransactionPermission is required when a transaction is promoted to a
distributed transaction.
KEYWORD DESCRIPTION
Implicit Unbind The default. The connection detaches from the transaction
when it ends, switching back to autocommit mode.
Explicit Unbind The connection remains attached to the transaction until the
transaction is closed. The connection will fail if the associated
transaction is not active or does not match Current.
Using TransactionScope
The TransactionScope class makes a code block transactional by implicitly enlisting connections in a distributed
transaction. You must call the Complete method at the end of the TransactionScope block before leaving it.
Leaving the block invokes the Dispose method. If an exception has been thrown that causes the code to leave
scope, the transaction is considered aborted.
We recommend that you use a using block to make sure that Dispose is called on the TransactionScope object
when the using block is exited. Failure to commit or roll back pending transactions can significantly damage
performance because the default time-out for the TransactionScope is one minute. If you do not use a using
statement, you must perform all work in a Try block and explicitly call the Dispose method in the Finally block.
If an exception occurs in the TransactionScope, the transaction is marked as inconsistent and is abandoned. It will
be rolled back when the TransactionScope is disposed. If no exception occurs, participating transactions commit.
NOTE
The TransactionScope class creates a transaction with a IsolationLevel of Serializable by default. Depending on your
application, you might want to consider lowering the isolation level to avoid high contention in your application.
NOTE
We recommend that you perform only updates, inserts, and deletes within distributed transactions because they consume
significant database resources. Select statements may lock database resources unnecessarily, and in some scenarios, you
may have to use transactions for selects. Any non-database work should be done outside the scope of the transaction,
unless it involves other transacted resource managers. Although an exception in the scope of the transaction prevents the
transaction from committing, the TransactionScope class has no provision for rolling back any changes your code has made
outside the scope of the transaction itself. If you have to take some action when the transaction is rolled back, you must
write your own implementation of the IEnlistmentNotification interface and explicitly enlist in the transaction.
Example
Working with System.Transactions requires that you have a reference to System.Transactions.dll.
The following function demonstrates how to create a promotable transaction against two different SQL Server
instances, represented by two different SqlConnection objects, which are wrapped in a TransactionScope block.
The code creates the TransactionScope block with a using statement and opens the first connection, which
automatically enlists it in the TransactionScope. The transaction is initially enlisted as a lightweight transaction, not
a full distributed transaction. The second connection is enlisted in the TransactionScope only if the command in
the first connection does not throw an exception. When the second connection is opened, the transaction is
automatically promoted to a full distributed transaction. The Complete method is invoked, which commits the
transaction only if no exceptions have been thrown. If an exception has been thrown at any point in the
TransactionScope block, Complete will not be called, and the distributed transaction will roll back when the
TransactionScope is disposed at the end of its using block.
// 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();
// 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 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)
Try
' The transaction is promoted to a full distributed
' transaction when connection2 is opened.
connection2.Open()
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
Return returnValue
End Function
See also
Transactions and Concurrency
ADO.NET Managed Providers and DataSet Developer Center
Optimistic Concurrency
4/8/2019 • 8 minutes to read • Edit Online
In a multiuser environment, there are two models for updating data in a database: optimistic concurrency and
pessimistic concurrency. The DataSet object is designed to encourage the use of optimistic concurrency for long-
running activities, such as remoting data and interacting with data.
Pessimistic concurrency involves locking rows at the data source to prevent other users from modifying data in a
way that affects the current user. In a pessimistic model, when a user performs an action that causes a lock to be
applied, other users cannot perform actions that would conflict with the lock until the lock owner releases it. This
model is primarily used in environments where there is heavy contention for data, so that the cost of protecting
data with locks is less than the cost of rolling back transactions if concurrency conflicts occur.
Therefore, in a pessimistic concurrency model, a user who updates a row establishes a lock. Until the user has
finished the update and released the lock, no one else can change that row. For this reason, pessimistic
concurrency is best implemented when lock times will be short, as in programmatic processing of records.
Pessimistic concurrency is not a scalable option when users are interacting with data and causing records to be
locked for relatively large periods of time.
NOTE
If you need to update multiple rows in the same operation, then creating a transaction is a more scalable option than using
pessimistic locking.
By contrast, users who use optimistic concurrency do not lock a row when reading it. When a user wants to update
a row, the application must determine whether another user has changed the row since it was read. Optimistic
concurrency is generally used in environments with a low contention for data. Optimistic concurrency improves
performance because no locking of records is required, and locking of records requires additional server resources.
Also, in order to maintain record locks, a persistent connection to the database server is required. Because this is
not the case in an optimistic concurrency model, connections to the server are free to serve a larger number of
clients in less time.
In an optimistic concurrency model, a violation is considered to have occurred if, after a user receives a value from
the database, another user modifies the value before the first user has attempted to modify it. How the server
resolves a concurrency violation is best shown by first describing the following example.
The following tables follow an example of optimistic concurrency.
At 1:00 p.m., User1 reads a row from the database with the following values:
CustID LastName FirstName
101 Smith Bob
The update succeeds because the values in the database at the time of update match the original values that User2
has.
At 1:05 p.m., User1 changes "Bob"'s first name to "James" and tries to update the row.
At this point, User1 encounters an optimistic concurrency violation because the value in the database ("Robert") no
longer matches the original value that User1 was expecting ("Bob"). The concurrency violation simply lets you
know that the update failed. The decision now needs to be made whether to overwrite the changes supplied by
User2 with the changes supplied by User1, or to cancel the changes by User1.
To test for an optimistic concurrency violation when updating a row in Table1, you would issue the following
UPDATE statement:
UPDATE Table1 Set Col1 = @NewCol1Value,
Set Col2 = @NewCol2Value,
Set Col3 = @NewCol3Value
WHERE Col1 = @OldCol1Value AND
Col2 = @OldCol2Value AND
Col3 = @OldCol3Value
As long as the original values match the values in the database, the update is performed. If a value has been
modified, the update will not modify the row because the WHERE clause will not find a match.
Note that it is recommended to always return a unique primary key value in your query. Otherwise, the preceding
UPDATE statement may update more than one row, which might not be your intent.
If a column at your data source allows nulls, you may need to extend your WHERE clause to check for a matching
null reference in your local table and at the data source. For example, the following UPDATE statement verifies that
a null reference in the local row still matches a null reference at the data source, or that the value in the local row
still matches the value at the data source.
You may also choose to apply less restrictive criteria when using an optimistic concurrency model. For example,
using only the primary key columns in the WHERE clause causes the data to be overwritten regardless of whether
the other columns have been updated since the last query. You can also apply a WHERE clause only to specific
columns, resulting in data being overwritten unless particular fields have been updated since they were last
queried.
The DataAdapter.RowUpdated Event
The RowUpdated event of the DataAdapter object can be used in conjunction with the techniques described
earlier, to provide notification to your application of optimistic concurrency violations. RowUpdated occurs after
each attempt to update a Modified row from a DataSet. This enables you to add special handling code, including
processing when an exception occurs, adding custom error information, adding retry logic, and so on. The
RowUpdatedEventArgs object returns a RecordsAffected property containing the number of rows affected by a
particular update command for a modified row in a table. By setting the update command to test for optimistic
concurrency, the RecordsAffected property will, as a result, return a value of 0 when an optimistic concurrency
violation has occurred, because no records were updated. If this is the case, an exception is thrown. The
RowUpdated event enables you to handle this occurrence and avoid the exception by setting an appropriate
RowUpdatedEventArgs.Status value, such as UpdateStatus.SkipCurrentRow. For more information about the
RowUpdated event, see Handling DataAdapter Events.
Optionally, you can set DataAdapter.ContinueUpdateOnError to true, before calling Update, and respond to
the error information stored in the RowError property of a particular row when the Update is completed. For
more information, see Row Error Information.
adapter.Update(dataSet, "Customers");
See also
Retrieving and Modifying Data in ADO.NET
Updating Data Sources with DataAdapters
Row Error Information
Transactions and Concurrency
ADO.NET Managed Providers and DataSet Developer Center
Retrieving Identity or Autonumber Values
5/15/2019 • 24 minutes to read • Edit Online
A primary key in a relational database is a column or combination of columns that always contain unique values.
Knowing the primary key value allows you to locate the row that contains it. Relational database engines, such as
SQL Server, Oracle, and Microsoft Access/Jet support the creation of automatically incrementing columns that
can be designated as primary keys. These values are generated by the server as rows are added to a table. In SQL
Server, you set the identity property of a column, in Oracle you create a Sequence, and in Microsoft Access you
create an AutoNumber column.
A DataColumn can also be used to generate automatically incrementing values by setting the AutoIncrement
property to true. However, you might end up with duplicate values in separate instances of a DataTable, if multiple
client applications are independently generating automatically incrementing values. Having the server generate
automatically incrementing values eliminates potential conflicts by allowing each user to retrieve the generated
value for each inserted row.
During a call to the Update method of a DataAdapter , the database can send data back to your ADO.NET
application as output parameters or as the first returned record of the result set of a SELECT statement executed
in the same batch as the INSERT statement. ADO.NET can retrieve these values and update the corresponding
columns in the DataRow being updated.
Some database engines, such as the Microsoft Access Jet database engine, do not support output parameters and
cannot process multiple statements in a single batch. When working with the Jet database engine, you can
retrieve the new AutoNumber value generated for an inserted row by executing a separate SELECT command in
an event handler for the RowUpdated event of the DataAdapter .
NOTE
An alternative to using an auto incrementing value is to use the NewGuid method of a Guid object to generate a GUID, or
globally unique identifier, on the client computer that can be copied to the server as each new row is inserted. The
NewGuid method generates a 16-byte binary value that is created using an algorithm that provides a high probability that
no value will be duplicated. In a SQL Server database, a GUID is stored in a uniqueidentifier column which SQL Server
can automatically generate using the Transact-SQL NEWID() function. Using a GUID as a primary key can adversely affect
performance. SQL Server provides support for the NEWSEQUENTIALID() function, which generates a sequential GUID that is
not guaranteed to be globally unique but that can be indexed more efficiently.
FUNCTION DESCRIPTION
SCOPE_IDENTITY Returns the last identity value within the current execution
scope. SCOPE_IDENTITY is recommended for most scenarios.
@@IDENTITY Contains the last identity value generated in any table in the
current session. @@IDENTITY can be affected by triggers and
may not return the identity value that you expect.
FUNCTION DESCRIPTION
IDENT_CURRENT Returns the last identity value generated for a specific table in
any session and any scope.
The following stored procedure demonstrates how to insert a row into the Categories table and use an output
parameter to return the new identity value generated by the Transact-SQL SCOPE_IDENTITY () function.
The stored procedure can then be specified as the source of the InsertCommand of a SqlDataAdapter object. The
CommandType property of the InsertCommand must be set to StoredProcedure. The identity output is retrieved
by creating a SqlParameter that has a ParameterDirection of Output. When the InsertCommand is processed, the
auto-incremented identity value is returned and placed in the CategoryID column of the current row if you set
the UpdatedRowSource property of the insert command to UpdateRowSource.OutputParameters or to
UpdateRowSource.Both .
If your insert command executes a batch that includes both an INSERT statement and a SELECT statement that
returns the new identity value, then you can retrieve the new value by setting the UpdatedRowSource property of
the insert command to 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);
adapter.Update(categories);
When either of these methods is used to preserve original values in a DataRow during a DataAdapter update,
ADO.NET performs a series of actions to set the current values of the DataRow to new values returned by output
parameters or by the first returned row of a result set, while still preserving the original value in each DataColumn .
First, the AcceptChanges method of the DataRow is called to preserve the current values as original values, and
then the new values are assigned. Following these actions, DataRows that had their RowState property set to
Added will have their RowState property set to Modified, which may be unexpected.
How the command results are applied to each DataRow being updated is determined by the UpdatedRowSource
property of each DbCommand. This property is set to a value from the UpdateRowSource enumeration.
The following table describes how the UpdateRowSource enumeration values affect the RowState property of
updated rows.
Example
This example demonstrates extracting changed rows from a DataTable and using a SqlDataAdapter to update the
data source and retrieve a new identity column value. The InsertCommand executes two Transact-SQL
statements; the first one is the INSERT statement, and the second one is a SELECT statement that uses the
SCOPE_IDENTITY function to retrieve the identity value.
The UpdatedRowSource property of the insert command is set to UpdateRowSource.FirstReturnedRow and the
MissingSchemaAction property of the DataAdapter is set to MissingSchemaAction.AddWithKey . The DataTable is
filled and the code adds a new row to the DataTable . The changed rows are then extracted into a new DataTable ,
which is passed to the DataAdapter , which then updates the server.
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);
adapter.Update(dataChanges);
connection.Close();
The OnRowUpdated event handler checks the StatementType of the SqlRowUpdatedEventArgs to determine if the
row is an insert. If it is, then the Status property is set to SkipCurrentRow. The row is updated, but the original
values in the row are preserved. In the main body of the procedure, the Merge method is called to merge the new
identity value into the original DataTable , and finally AcceptChanges is called.
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;
}
}
// Create a DataTable
DataTable categories = new DataTable();
The RowUpdated event handler uses the same open OleDbConnection as the Update statement of the
OleDbDataAdapter . It checks the StatementType of the OleDbRowUpdatedEventArgs for inserted rows. For each
inserted row a new OleDbCommand is created to execute the SELECT @@IDENTITY statement on the
connection, returning the new Autonumber value, which is placed in the CategoryID column of the DataRow . The
Status property is then set to UpdateStatus.SkipCurrentRow to suppress the hidden call to AcceptChanges . In the
main body of the procedure, the Merge method is called to merge the two DataTable objects, and finally
AcceptChanges is called.
USE [master]
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
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
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
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].[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,
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
TIP
The code listing refers to an Access database file called MySchool.mdb. You can download MySchool.mdb (as part of the full
C# or Visual Basic sample project) from 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;";
// 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";
conn.Open();
cmd.ExecuteNonQuery();
// 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.Parameters.Add(
new SqlParameter("@FirstName", SqlDbType.NVarChar, 50, "FirstName"));
mySchool.InsertCommand.Parameters.Add(
new SqlParameter("@LastName", SqlDbType.NVarChar, 50, "LastName"));
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);
mySchool.Fill(persons);
mySchool.RowUpdated += OnRowUpdated;
mySchool.Update(dataChanges);
persons.Merge(dataChanges);
persons.AcceptChanges();
// 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;
}
}
return persons;
}
Console.WriteLine();
}
}
}
See also
Retrieving and Modifying Data in ADO.NET
DataAdapters and DataReaders
Row States and Row Versions
AcceptChanges and RejectChanges
Merging DataSet Contents
Updating Data Sources with DataAdapters
ADO.NET Managed Providers and DataSet Developer Center
Retrieving Binary Data
4/8/2019 • 5 minutes to read • Edit Online
By default, the DataReader loads incoming data as a row as soon as an entire row of data is available. Binary large
objects (BLOBs) need different treatment, however, because they can contain gigabytes of data that cannot be
contained in a single row. The Command.ExecuteReader method has an overload that will take a
CommandBehavior argument to modify the default behavior of the DataReader. You can pass SequentialAccess
to the ExecuteReader method to modify the default behavior of the DataReader so that instead of loading rows
of data, it will load data sequentially as it is received. This is ideal for loading BLOBs or other large data structures.
Note that this behavior may depend on your data source. For example, returning a BLOB from Microsoft Access
will load the entire BLOB being loaded into memory, rather than sequentially as it is received.
When setting the DataReader to use SequentialAccess, it is important to note the sequence in which you access
the fields returned. The default behavior of the DataReader, which loads an entire row as soon as it is available,
allows you to access the fields returned in any order until the next row is read. When using SequentialAccess
however, you must access the fields returned by the DataReader in order. For example, if your query returns three
columns, the third of which is a BLOB, you must return the values of the first and second fields before accessing
the BLOB data in the third field. If you access the third field before the first or second fields, the first and second
field values are no longer available. This is because SequentialAccess has modified the DataReader to return
data in sequence and the data is not available after the DataReader has read past it.
When accessing the data in the BLOB field, use the GetBytes or GetChars typed accessors of the DataReader,
which fill an array with data. You can also use GetString for character data; however. to conserve system resources
you might not want to load an entire BLOB value into a single string variable. You can instead specify a specific
buffer size of data to be returned, and a starting location for the first byte or character to be read from the returned
data. GetBytes and GetChars will return a long value, which represents the number of bytes or characters
returned. If you pass a null array to GetBytes or GetChars, the long value returned will be the total number of
bytes or characters in the BLOB. You can optionally specify an index in the array as a starting position for the data
being read.
Example
The following example returns the publisher ID and logo from the pubs sample database in Microsoft SQL Server.
The publisher ID ( pub_id ) is a character field, and the logo is an image, which is a BLOB. Because the logo field is
a bitmap, the example returns binary data using GetBytes. Notice that the publisher ID is accessed for the current
row of data before the logo, because the fields must be accessed sequentially.
' Assumes that connection is a valid SqlConnection object.
Dim command As SqlCommand = New SqlCommand( _
"SELECT pub_id, logo FROM pub_info", connection)
' 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)
' 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
while (reader.Read())
{
// Get the publisher id, which must occur before getting the logo.
pubID = reader.GetString(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();
Stored procedures can accept data as input parameters and can return data as output parameters, result sets, or
return values. The sample below illustrates how ADO.NET sends and receives input parameters, output
parameters, and return values. The example inserts a new record into a table where the primary key column is an
identity column in a SQL Server database.
NOTE
If you are using SQL Server stored procedures to edit or delete data using a SqlDataAdapter, make sure that you do not use
SET NOCOUNT ON in the stored procedure definition. This causes the rows affected count returned to be zero, which the
DataAdapter interprets as a concurrency conflict. In this event, a DBConcurrencyException will be thrown.
Example
The sample uses the following stored procedure to insert a new category into the Northwind Categories table.
The stored procedure takes the value in the CategoryName column as an input parameter and uses the
SCOPE_IDENTITY () function to retrieve the new value of the identity field, CategoryID, and return it in an output
parameter. The RETURN statement uses the @@ROWCOUNT function to return the number of rows inserted.
The following code example uses the InsertCategory stored procedure shown above as the source for the
InsertCommand of the SqlDataAdapter. The @Identity output parameter will be reflected in the DataSet after the
record has been inserted into the database when the Update method of the SqlDataAdapter is called. The code
also retrieves the return value.
NOTE
When using the OleDbDataAdapter, you must specify parameters with a ParameterDirection of ReturnValue before the
other parameters.
using System;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = GetConnectionString();
ReturnIdentity(connectionString);
// Console.ReadLine();
}
}
Option Explicit On
Option Strict On
Imports System
Imports System.Data
Imports System.Data.SqlClient
Module Class1
Sub Main()
Sub Main()
Dim connectionString As String = _
GetConnectionString()
ReturnIdentity(connectionString)
' Console.ReadLine()
End Sub
See also
Retrieving and Modifying Data in ADO.NET
DataAdapters and DataReaders
Executing a Command
ADO.NET Managed Providers and DataSet Developer Center
Retrieving Database Schema Information
4/8/2019 • 2 minutes to read • Edit Online
Obtaining schema information from a database is accomplished with the process of schema discovery. Schema
discovery allows applications to request that managed providers find and return information about the database
schema, also known as metadata, of a given database. Different database schema elements such as tables,
columns, and stored-procedures are exposed through schema collections. Each schema collection contains a
variety of schema information specific to the provider being used.
Each of the .NET Framework managed providers implement the GetSchema method in the Connection class,
and the schema information that is returned from the GetSchema method comes in the form of a DataTable. The
GetSchema method is an overloaded method that provides optional parameters for specifying the schema
collection to return, and restricting the amount of information returned.
The .NET Framework Data Providers for OLE DB, ODBC, Oracle, and SqlClient provide a GetSchemaTable
method that returns a DataTable describing the column metadata of the DataReader.
The .NET Framework Data Provider for OLE DB also exposes schema information by using the
GetOleDbSchemaTable method of the OleDbConnection object. As arguments, GetOleDbSchemaTable takes
an OleDbSchemaGuid that identifies the schema information to return, and an array of restrictions on those
returned columns. GetOleDbSchemaTable returns a DataTable populated with the requested schema
information.
In This Section
GetSchema and Schema Collections
Describes the GetSchema method and how it can be used to retrieve and restrict schema information from a
database.
Schema Restrictions
Describes schema restrictions that can be used with GetSchema.
Common Schema Collections
Describes all of the common schema collections supported by all of the .NET Framework managed providers.
SQL Server Schema Collections
Describes the schema collection supported by the .NET Framework provider for SQL Server.
Oracle Schema Collections
Describes the schema collection supported by the .NET Framework provider for Oracle.
ODBC Schema Collections
Describes the schema collections for ODBC drivers.
OLE DB Schema Collections
Describes the schema collections for OLE DB providers.
Reference
GetSchema
Describes the GetSchema method of the DbConnection class.
GetSchema
Describes the GetSchema method of the OdbcConnection class.
GetSchema
Describes the GetSchema method of the OleDbConnection class.
GetSchema
Describes the GetSchema method of the OracleConnection class.
GetSchema
Describes the GetSchema method of the SqlConnection class.
GetSchemaTable
Describes the GetSchemaTable method of the DbDataReader class.
GetSchemaTable
Describes the GetSchemaTable method of the OdbcDataReader class.
GetSchemaTable
Describes the GetSchemaTable method of the OleDbDataReader class.
GetSchemaTable
Describes the GetSchemaTable method of the OracleDataReader class.
GetSchemaTable
Describes the GetSchemaTable method of the SqlDataReader class.
See also
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
GetSchema and Schema Collections
4/8/2019 • 2 minutes to read • Edit Online
The Connection classes in each of the .NET Framework managed providers implement a GetSchema method
which is used to retrieve schema information about the database that is currently connected, and the schema
information returned from the GetSchema method comes in the form of a DataTable. The GetSchema method is
an overloaded method that provides optional parameters for specifying the schema collection to return, and
restricting the amount of information returned.
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")
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");
See also
Retrieving Database Schema Information
ADO.NET Managed Providers and DataSet Developer Center
Schema Restrictions
4/8/2019 • 4 minutes to read • Edit Online
The second optional parameter of the GetSchema method is the restrictions that are used to limit the amount of
schema information returned, and it is passed to the GetSchema method as an array of strings. The position in the
array determines the values that you can pass, and this is equivalent to the restriction number.
For example, the following table describes the restrictions supported by the "Tables" schema collection using the
.NET Framework Data Provider for SQL Server. Additional restrictions for SQL Server schema collections are
listed at the end of this topic.
NOTE
The restrictions collections for SqlClient and OracleClient have an additional ParameterName column. The restriction
default column is still there for backwards compatibility, but is currently ignored. Parameterized queries rather than string
replacement should be used to minimize the risk of an SQL injection attack when specifying restriction values.
NOTE
The number of elements in the array must be less than or equal to the number of restrictions supported for the specified
schema collection else an ArgumentException will be thrown. There can be fewer than the maximum number of restrictions.
The missing restrictions are assumed to be null (unrestricted).
You can query a .NET Framework managed provider to determine the list of supported restrictions by calling the
GetSchema method with the name of the restrictions schema collection, which is "Restrictions". This will return a
DataTable with a list of the collection names, the restriction names, the default restriction values, and the restriction
numbers.
Example
The following examples demonstrate how to use the GetSchema method of the .NET Framework Data Provider for
the SQL Server SqlConnection class to retrieve schema information about all of the tables contained in the
AdventureWorks sample database, and to restrict the information returned to only those tables in the "Sales"
schema:
Imports System.Data.SqlClient
Module Module1
Sub Main()
Dim connectionString As String = _
"Data Source=(local);Database=AdventureWorks;" & _
"Integrated Security=true;";
class Program
{
static void Main()
{
string connectionString =
"Data Source=(local);Database=AdventureWorks;" +
"Integrated Security=true;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
connection.Open();
Databases
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
Tables
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
Columns
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
StructuredTypeMembers
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
Views
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
ViewColumns
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
ProcedureParameters
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
Procedures
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
IndexColumns
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
UserDefinedTypes
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
ForeignKeys
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
AllColumns
RESTRICTION NAME PARAMETER NAME RESTRICTION DEFAULT RESTRICTION NUMBER
See also
ADO.NET Managed Providers and DataSet Developer Center
Common Schema Collections
4/8/2019 • 10 minutes to read • Edit Online
The common schema collections are the schema collections that are implemented by each of the .NET Framework
managed providers. You can query a .NET Framework managed provider to determine the list of supported
schema collections by calling the GetSchema method with no arguments, or with the schema collection name
"MetaDataCollections". This will return a DataTable with a list of the supported schema collections, the number of
restrictions that they each support, and the number of identifier parts that they use. These collections describe all
of the required columns. Providers are free to add additional columns if they wish. For example, SqlClient and
OracleClient add ParameterName to the restrictions collection.
If a provider is unable to determine the value of a required column, it will return null.
For more information about using the GetSchema methods, see GetSchema and Schema Collections.
MetaDataCollections
This schema collection exposes information about all of the schema collections supported by the .NET Framework
managed provider that is currently used to connect to the database.
DataSourceInformation
This schema collection exposes information about data source that the .NET Framework managed provider is
currently connect to.
In some cases
DataSourceProductVersion and
DataSourceProductVersionNormalized
will be the same value. In the case of
OLE DB and ODBC, these will always be
the same as they are mapped to the
same function call in the underlying
native API.
COLUMNNAME DATATYPE DESCRIPTION
In some cases,
DataSourceProductVersion and
DataSourceProductVersionNormalized
will be the same value. In the case of
OLE DB and ODBC these will always be
the same as they are mapped to the
same function call in the underlying
native API.
DataTypes
This schema collection exposes information about the data types that are supported by the database that the .NET
Framework managed provider is currently connected to.
Restrictions
This schema collection exposed information about the restrictions that are supported by the .NET Framework
managed provider that is currently used to connect to the database.
ReservedWords
This schema collection exposes information about the words that are reserved by the database that the .NET
Framework managed provider that is currently connected to.
See also
Retrieving Database Schema Information
GetSchema and Schema Collections
ADO.NET Managed Providers and DataSet Developer Center
SQL Server Schema Collections
4/8/2019 • 11 minutes to read • Edit Online
The Microsoft .NET Framework Data Provider for SQL Server supports additional schema collections in addition
to the common schema collections. The schema collections vary slightly by the version of SQL Server you are
using. To determine the list of supported schema collections, call the GetSchema method with no arguments, or
with the schema collection name "MetaDataCollections". This will return a DataTable with a list of the supported
schema collections, the number of restrictions that they each support, and the number of identifier parts that they
use.
Databases
COLUMNNAME DATATYPE DESCRIPTION
Foreign Keys
COLUMNNAME DATATYPE DESCRIPTION
Indexes
COLUMNNAME DATATYPE DESCRIPTION
- HEAP
- CLUSTERED
- NONCLUSTERED
- XML
- SPATIAL
IndexColumns
COLUMNNAME DATATYPE DESCRIPTION
Procedures
COLUMNNAME DATATYPE DESCRIPTION
Procedure Parameters
COLUMNNAME DATATYPE DESCRIPTION
Tables
COLUMNNAME DATATYPE DESCRIPTION
Columns
COLUMNNAME DATATYPE DESCRIPTION
CHARACTER_OCTET_LENGTH Int32 – SQL8, Int16 – Sql7 Maximum length, in bytes, for binary
data, character data, or text and image
data. Otherwise, NULL is returned.
Users
COLUMNNAME DATATYPE DESCRIPTION
Views
COLUMNNAME DATATYPE DESCRIPTION
ViewColumns
COLUMNNAME DATATYPE DESCRIPTION
UserDefinedTypes
COLUMNNAME DATATYPE DESCRIPTION
See also
Retrieving Database Schema Information
ADO.NET Managed Providers and DataSet Developer Center
Oracle Schema Collections
3/5/2019 • 14 minutes to read • Edit Online
The Microsoft .NET Framework Data Provider for Oracle supports the following specific schema collections in
addition to the common schema collections:
Columns
Indexes
IndexColumns
Procedures
Sequences
Synonyms
Tables
Users
Views
Functions
Packages
PackageBodies
Arguments
UniqueKeys
PrimaryKeys
ForeignKeys
ForeignKeyColumns
ProcedureParameters
Columns
COLUMNNAME DATATYPE DESCRIPTION
Indexes
COLUMNNAME DATATYPE DESCRIPTION
IndexColumns
COLUMNNAME DATATYPE DESCRIPTION
Procedures
COLUMNNAME DATATYPE DESCRIPTION
Sequences
COLUMNNAME DATATYPE DESCRIPTION
Synonyms
COLUMNNAME DATATYPE DESCRIPTION
Tables
COLUMNNAME DATATYPE DESCRIPTION
Users
COLUMNNAME DATATYPE DESCRIPTION
Views
COLUMNNAME DATATYPE DESCRIPTION
Functions
COLUMNNAME DATATYPE DESCRIPTION
Packages
COLUMNNAME DATATYPE DESCRIPTION
PackageBodies
COLUMNNAME DATATYPE DESCRIPTION
Arguments
COLUMNNAME DATATYPE DESCRIPTION
PrimaryKeys
COLUMNNAME DATATYPE DESCRIPTION
ForeignKeys
COLUMNNAME DATATYPE DESCRIPTION
ForeignKeyColumns
COLUMNNAME DATATYPE DESCRIPTION
ProcedureParameters
COLUMNNAME DATATYPE DESCRIPTION
See also
ADO.NET Managed Providers and DataSet Developer Center
ODBC Schema Collections
3/5/2019 • 2 minutes to read • Edit Online
This section discusses schema collection support for the ODBC drivers for Microsoft SQL Server, Oracle, and
Microsoft Jet.
TABLE_CAT String
TABLE_SCHEM String
TABLE_NAME String
TABLE_TYPE String
REMARKS String
Indexes
COLUMNNAME DATATYPE
TABLE_CAT String
TABLE_SCHEM String
TABLE_NAME String
NON_UNIQUE Int16
INDEX_QUALIFIER String
COLUMNNAME DATATYPE
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
Columns
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
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
Procedures
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
PROCEDURE_CAT String
PROCEDURE_SCHEM String
PROCEDURE_NAME String
COLUMN_NAME String
COLUMN_TYPE Int16
DATA_TYPE Int16
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
PROCEDURE_CAT String
PROCEDURE_SCHEM String
PROCEDURE_NAME String
COLUMN_NAME String
COLUMN_TYPE Int16
DATA_TYPE Int16
TYPE_NAME String
COLUMNNAME DATATYPE
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
TABLE_QUALIFIER String
TABLE_OWNER String
TABLE_NAME String
TABLE_TYPE String
REMARKS String
Columns
COLUMNNAME DATATYPE
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
Procedures
COLUMNNAME DATATYPE
PROCEDURE_QUALIFIER String
PROCEDURE_OWNER String
PROCEDURE_NAME String
NUM_INPUT_PARAMS Int16
COLUMNNAME DATATYPE
NUM_OUTPUT_PARAMS Int16
NUM_RESULT_SETS Int16
REMARKS String
PROCEDURE_TYPE Int16
ProcedureColumns
COLUMNNAME DATATYPE
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
TABLE_QUALIFIER String
TABLE_OWNER String
TABLE_NAME String
TABLE_TYPE String
REMARKS String
Columns
COLUMNNAME DATATYPE
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
Procedures
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
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
See also
ADO.NET Managed Providers and DataSet Developer Center
OLE DB Schema Collections
4/28/2019 • 3 minutes to read • Edit Online
This section discusses schema collection support for the OLE DB providers for Microsoft SQL Server, Oracle, and
Microsoft Jet.
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
Columns
COLUMNNAME DATATYPE
TABLE_CATALOG String
TABLE_SCHEMA String
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
DESCRIPTION String
COLUMN_LCID Int32
COLUMN_COMPFLAGS Int32
COLUMN_SORTID Int32
COLUMN_TDSCOLLATION Byte[]
IS_COMPUTED Boolean
Procedures
COLUMNNAME DATATYPE
PROCEDURE_CATALOG String
PROCEDURE_SCHEMA String
PROCEDURE_NAME String
PROCEDURE_TYPE Int16
PROCEDURE_DEFINITION String
DESCRIPTION String
DATE_CREATED DateTime
DATE_MODIFIED DateTime
ProcedureParameters
COLUMNNAME DATATYPE
PROCEDURE_CATALOG String
PROCEDURE_SCHEMA String
PROCEDURE_NAME String
PARAMETER_NAME String
ORDINAL_POSITION Int32
PARAMETER_TYPE Int32
PARAMETER_HASDEFAULT Boolean
PARAMETER_DEFAULT String
COLUMNNAME DATATYPE
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
Catalog
COLUMNNAME DATATYPE
CATALOG_NAME String
DESCRIPTION String
Indexes
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
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
TABLE_CATALOG String
COLUMNNAME DATATYPE
TABLE_SCHEMA String
TABLE_NAME String
TABLE_TYPE String
TABLE_GUID Guid
DESCRIPTION String
TABLE_PROPID Int64
DATE_CREATED DateTime
DATE_MODIFIED DateTime
Columns
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
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
Procedures
COLUMNNAME DATATYPE
PROCEDURE_CATALOG String
PROCEDURE_SCHEMA String
PROCEDURE_NAME String
PROCEDURE_TYPE Int16
PROCEDURE_DEFINITION String
DESCRIPTION String
DATE_CREATED DateTime
DATE_MODIFIED DateTime
ProcedureColumns
COLUMNNAME DATATYPE
PROCEDURE_CATALOG String
COLUMNNAME DATATYPE
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
Views
COLUMNNAME DATATYPE
TABLE_CATALOG String
TABLE_SCHEMA String
TABLE_NAME String
VIEW_DEFINITION String
CHECK_OPTION Boolean
IS_UPDATABLE Boolean
DESCRIPTION String
COLUMNNAME DATATYPE
DATE_CREATED DateTime
DATE_MODIFIED DateTime
Indexes
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
CARDINALITY Decimal
PAGES Int32
FILTER_CONDITION String
INTEGRATED Boolean
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
Columns
COLUMNNAME DATATYPE
TABLE_CATALOG String
TABLE_SCHEMA String
COLUMNNAME DATATYPE
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
COLUMNNAME DATATYPE
DESCRIPTION String
Procedures
COLUMNNAME DATATYPE
PROCEDURE_CATALOG String
PROCEDURE_SCHEMA String
PROCEDURE_NAME String
PROCEDURE_TYPE Int16
PROCEDURE_DEFINITION String
DESCRIPTION String
DATE_CREATED DateTime
DATE_MODIFIED DateTime
Views
COLUMNNAME DATATYPE
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
Indexes
COLUMNNAME DATATYPE
TABLE_CATALOG String
TABLE_SCHEMA String
COLUMNNAME DATATYPE
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
See also
ADO.NET Managed Providers and DataSet Developer Center
DbProviderFactories
4/8/2019 • 2 minutes to read • Edit Online
The System.Data.Common namespace provides classes for creating DbProviderFactory instances to work with
specific data sources. When you create a DbProviderFactory instance and pass it information about the data
provider, the DbProviderFactory can determine the correct, strongly typed connection object to return based on
the information it has been provided.
Beginning in the .NET Framework version 4, data providers such as System.Data.Odbc, System.Data.OleDb,
System.Data.SqlClient, and System.Data.OracleClient are no longer listed in machine.config file, but custom
providers will continue to be listed there.
In This Section
Factory Model Overview
Provides an overview of the factory design pattern and programming interface.
Obtaining a DbProviderFactory
Demonstrates how to list the installed data providers and create a DbConnection from a DbProviderFactory .
DbConnection, DbCommand and DbException
Demonstrates how to create a DbCommand and DbDataReader, and how to handle data errors using
DbException.
Modifying Data with a DbDataAdapter
Demonstrates how to use a DbCommandBuilder with a DbDataAdapter to retrieve and modify data.
See also
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Factory Model Overview
4/8/2019 • 2 minutes to read • Edit Online
ADO.NET 2.0 introduced new base classes in the System.Data.Common namespace. The base classes are abstract,
which means that they can't be directly instantiated. They include DbConnection, DbCommand, and
DbDataAdapter and are shared by the .NET Framework data providers, such as System.Data.SqlClient and
System.Data.OleDb. The addition of base classes simplifies adding functionality to the .NET Framework data
providers without having to create new interfaces.
ADO.NET 2.0 also introduced abstract base classes, which enable a developer to write generic data access code
that does not depend on a specific data provider.
See also
Obtaining a DbProviderFactory
DbConnection, DbCommand and DbException
Modifying Data with a DbDataAdapter
ADO.NET Managed Providers and DataSet Developer Center
Obtaining a DbProviderFactory
4/8/2019 • 5 minutes to read • Edit Online
The process of obtaining a DbProviderFactory involves passing information about a data provider to the
DbProviderFactories class. Based on this information, the GetFactory method creates a strongly typed provider
factory. For example, to create a SqlClientFactory, you can pass GetFactory a string with the provider name
specified as "System.Data.SqlClient". The other overload of GetFactory takes a DataRow. Once you create the
provider factory, you can then use its methods to create additional objects. Some of the methods of a
SqlClientFactory include CreateConnection, CreateCommand, and CreateDataAdapter.
NOTE
The .NET Framework OracleClientFactory, OdbcFactory, and OleDbFactory classes also provide similar functionality.
Registering DbProviderFactories
Each .NET Framework data provider that supports a factory-based class registers configuration information in the
DbProviderFactories section of the machine.config file on the local computer. The following configuration file
fragment shows the syntax and format for 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>
The invariant attribute identifies the underlying data provider. This three-part naming syntax is also used when
creating a new factory and for identifying the provider in an application configuration file so that the provider
name, along with its associated connection string, can be retrieved at run time.
This DataTable can be used to enable a user to select a DataRow at run time. The selected DataRow can then be
passed to the GetFactory method to create a strongly typed DbProviderFactory. A selected DataRow can be
passed to the GetFactory method to create the desired DbProviderFactory object.
Return table
End Function
Using Application Configuration Files to Store Factory Information
The design pattern used for working with factories entails storing provider and connection string information in
an application configuration file, such as app.config for a Windows application, and web.config for an ASP.NET
application.
The following configuration file fragment demonstrates how to save two named connection strings,
"NorthwindSQL" for a connection to the Northwind database in SQL Server, and "NorthwindAccess" for a
connection to the Northwind database in Access/Jet. The invariant name is used for the providerName
attribute.
<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>
NOTE
A reference to System.Configuration.dll is required in order for the code to run.
// 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;
Return returnValue
End Function
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;
}
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
Once you have created a DbProviderFactory and a DbConnection, you can then work with commands and data
readers to retrieve data from the data source.
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
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.
}
See also
DbProviderFactories
Obtaining a DbProviderFactory
Modifying Data with a DbDataAdapter
ADO.NET Managed Providers and DataSet Developer Center
Modifying Data with a DbDataAdapter
4/8/2019 • 5 minutes to read • Edit Online
The CreateDataAdapter method of a DbProviderFactory object gives you a DbDataAdapter object that is strongly
typed to the underlying data provider specified at the time you create the factory. You can then use a
DbCommandBuilder to create commands to insert, update, and delete data from a DataSet to a data source.
using (connection)
{
// Define the query.
string queryString =
"SELECT CategoryName FROM Categories";
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
using (connection)
{
// Define the query.
string queryString =
"SELECT CustomerID, CompanyName FROM Customers";
adapter.Update(table);
adapter.Update(table);
// Delete a row.
DataRow[] deleteRow = table.Select("CustomerID = 'XYZZZ'");
foreach (DataRow row in deleteRow)
{
row.Delete();
}
adapter.Update(table);
Using connection
' Define the query.
Dim queryString As String = _
"SELECT CustomerID, CompanyName FROM Customers"
adapter.Update(table)
adapter.Update(table)
adapter.Update(table)
table.AcceptChanges()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Handling Parameters
The .NET Framework data providers handle naming and specifying parameters and parameter placeholders
differently. This syntax is tailored to a specific data source, as described in the following table.
The factory model is not helpful for creating parameterized DbCommand and DbDataAdapter objects. You will need
to branch in your code to create parameters that are tailored to your data provider.
IMPORTANT
Avoiding provider-specific parameters altogether by using string concatenation to construct direct SQL statements is not
recommended for security reasons. Using string concatenation instead of parameters leaves your application vulnerable to
SQL injection attacks.
See also
DbProviderFactories
Obtaining a DbProviderFactory
DbConnection, DbCommand and DbException
ADO.NET Managed Providers and DataSet Developer Center
Data Tracing in ADO.NET
6/4/2019 • 3 minutes to read • Edit Online
ADO.NET features built-in data tracing functionality that is supported by the .NET data providers for SQL Server,
Oracle, OLE DB and ODBC, as well as the ADO.NET DataSet, and the SQL Server network protocols.
Tracing data access API calls can help diagnose the following problems:
Schema mismatch between client program and the database.
Database unavailability or network library problems.
Incorrect SQL whether hard coded or generated by an application.
Incorrect programming logic.
Issues resulting from the interaction between multiple ADO.NET components or between ADO.NET and
your own components.
To support different trace technologies, tracing is extensible, so a developer can trace a problem at any level of the
application stack. Although tracing is not an ADO.NET-only feature, Microsoft providers take advantage of
generalized tracing and instrumentation APIs.
For more information about setting and configuring managed tracing in ADO.NET, see Tracing Data Access.
You can get the client connection ID programmatically by using the SqlConnection.ClientConnectionID property.
The ClientConnectionID is available for a SqlConnection object that successfully establishes a connection. If a
connection attempt fails, ClientConnectionID may be available via SqlException.ToString .
ADO.NET also sends a thread-specific activity ID. The activity ID is captured in the extended events sessions if the
sessions are started with the TRACK_CAUSALITY option enabled. For performance issues with an active
connection, you can get the activity ID from the client's data access trace ( ActivityID field) and then locate the
activity ID in the extended events output. The activity ID in extended events is a 16-byte GUID (not the same as the
GUID for the client connection ID ) appended with a four-byte sequence number. The sequence number represents
the order of a request within a thread and indicates the relative ordering of batch and RPC statements for the
thread. The ActivityID is currently optionally sent for SQL batch statements and RPC requests when data access
tracing is enabled on and the 18th bit in the data access tracing configuration word is turned ON.
The following is a sample that uses Transact-SQL to start an extended events session that will be stored in a ring
buffer and will record the activity ID sent from a client on RPC and batch operations.
See also
Network Tracing in the .NET Framework
Tracing and Instrumenting Applications
ADO.NET Managed Providers and DataSet Developer Center
Performance Counters in ADO.NET
4/8/2019 • 8 minutes to read • Edit Online
ADO.NET 2.0 introduced expanded support for performance counters that includes support for both
System.Data.SqlClient and System.Data.OracleClient. The System.Data.SqlClient performance counters available
in previous versions of ADO.NET have been deprecated and replaced with the new performance counters
discussed in this topic. You can use ADO.NET performance counters to monitor the status of your application and
the connection resources that it uses. Performance counters can be monitored by using Windows Performance
Monitor or can be accessed programmatically using the PerformanceCounter class in the System.Diagnostics
namespace.
HardConnectsPerSecond The number of connections per second that are being made
to a database server.
HardDisconnectsPerSecond The number of disconnects per second that are being made
to a database server.
NumberOfActiveConnectionPoolGroups The number of unique connection pool groups that are active.
This counter is controlled by the number of unique
connection strings that are found in the AppDomain.
NumberOfInactiveConnectionPools The number of inactive connection pools that have not had
any recent activity and are waiting to be disposed.
<system.diagnostics>
<switches>
<add name="ConnectionPoolPerformanceCounterDetail"
value="4"/>
</switches>
</system.diagnostics>
Example
Option Explicit On
Option Strict On
Imports System.Data.SqlClient
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Class Program
connection1.Close()
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
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program
{
PerformanceCounter[] PerfCounters = new PerformanceCounter[10];
SqlConnection connection = new SqlConnection();
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();
}
See also
Connecting to a Data Source
OLE DB, ODBC, and Oracle Connection Pooling
Performance Counters for ASP.NET
Runtime Profiling
Introduction to Monitoring Performance Thresholds
ADO.NET Overview
Asynchronous Programming
6/27/2019 • 15 minutes to read • Edit Online
This topic discusses support for asynchronous programming in the .NET Framework Data Provider for SQL
Server (SqlClient) including enhancements made to support asynchronous programming functionality that was
introduced in .NET Framework 4.5.
TIP
Beginning in the .NET Framework 4.5, these legacy methods no longer require Asynchronous Processing=true in the
connection string.
Calling an async method does not allocate any additional threads. It may use the existing I/O completion thread
briefly at the end.
The following methods were added in .NET Framework 4.5 to support asynchronous programming:
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
Other asynchronous members were added to support SqlClient Streaming Support.
TIP
The new asynchronous methods don't require Asynchronous Processing=true in the connection string.
When converted to use the new asynchronous functionality, the program would look like:
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
class A {
Adding the New Asynchronous Feature in an Existing Application (Mixing Old and New Patterns)
It is also possible to add new asynchronous capability (SqlConnection::OpenAsync) without changing the existing
asynchronous logic. For example, if an application currently uses:
You can begin to use the new asynchronous pattern without substantially changing the existing algorithm.
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
class A {
static void ProductList(IAsyncResult result) { }
Using the Base Provider Model and the New Asynchronous Feature
You may need to create a tool that is able to connect to different databases and execute queries. You can use the
base provider model and the new asynchronous feature.
The Microsoft Distributed Transaction Controller (MSDTC ) must be enabled on the server to use distributed
transactions. For information on how to enable MSDTC, see How to Enable MSDTC on a Web Server.
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();
class Program {
static void Main() {
string connectionString =
"Persist Security Info=False;Integrated Security=SSPI;database=Northwind;server=(local)";
Task task = ExecuteSqlTransaction(connectionString);
task.Wait();
}
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();
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;
await connection2.OpenAsync();
connection2.EnlistTransaction(transaction);
try {
SqlCommand command1 = connection1.CreateCommand();
command1.CommandText = "Insert into RegionTable1 (RegionID, RegionDescription) VALUES (100,
'Description')";
await command1.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);
}
}
}
}
}
}
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;
}
}
}
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";
// 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";
// 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.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);
// }
// }
// }
// }
// }
//}
NOTE
The following example uses the sample AdventureWorks database included with SQL Server. The connection string
provided in the sample code assumes that the database is installed and available on the local computer. Modify the
connection string as necessary for your environment.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
class Class1 {
static void Main() {
Task task = MultipleCommands();
task.Wait();
}
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";
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());
}
}
}
}
}
}
NOTE
The following example uses the sample AdventureWorks database included with SQL Server. The connection string
provided in the sample code assumes that the database is installed and available on the local computer. Modify the
connection string as necessary for your environment.
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();
}
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";
string updateSQL =
"UPDATE Purchasing.ProductVendor " +
"SET OnOrderQty = @OrderQty " +
"WHERE ProductID = @ProductID AND VendorID = @VendorID";
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;
See also
Retrieving and Modifying Data in ADO.NET
SqlClient Streaming Support
6/4/2019 • 11 minutes to read • Edit Online
Streaming support between SQL Server and an application (new in .NET Framework 4.5) supports unstructured
data on the server (documents, images, and media files). A SQL Server database can store binary large objects
(BLOBs), but retrieving BLOBS can use a lot of memory.
Streaming support to and from SQL Server simplifies writing applications that stream data, without having to
fully load the data into memory, resulting in fewer memory overflow exceptions.
Streaming support will also enable middle-tier applications to scale better, especially in scenarios where business
objects connect to SQL Azure in order to send, retrieve, and manipulate large BLOBs.
WARNING
Asynchronous calls are not supported if an application also uses the Context Connection connection string keyword.
The members added to support streaming are used to retrieve data from queries and to pass parameters to queries and
stored procedures. The streaming feature addresses basic OLTP and data migration scenarios and is applicable to on premise
and off premise data migrations.environments.
using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Threading.Tasks;
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";
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");
// 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
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)");
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);
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
};
// 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;
}
}
}
}
}
}
}
}
}
}
}
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"
private const string connectionString = @"Server=(localdb)\V11.0;Database=Demo2";
StreamBLOBToServer().Wait();
StreamTextToServer().Wait();
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 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";
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
// 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);
// 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;
See also
Retrieving and Modifying Data in ADO.NET
LINQ to DataSet
6/21/2019 • 2 minutes to read • Edit Online
LINQ to DataSet makes it easier and faster to query over data cached in a DataSet object. Specifically, LINQ to
DataSet simplifies querying by enabling developers to write queries from the programming language itself,
instead of by using a separate query language. This is especially useful for Visual Studio developers, who can now
take advantage of the compile-time syntax checking, static typing, and IntelliSense support provided by the Visual
Studio in their queries.
LINQ to DataSet can also be used to query over data that has been consolidated from one or more data sources.
This enables many scenarios that require flexibility in how data is represented and handled, such as querying
locally aggregated data and middle-tier caching in Web applications. In particular, generic reporting, analysis, and
business intelligence applications require this method of manipulation.
The LINQ to DataSet functionality is exposed primarily through the extension methods in the DataRowExtensions
and DataTableExtensions classes. LINQ to DataSet builds on and uses the existing ADO.NET architecture, and is
not meant to replace ADO.NET in application code. Existing ADO.NET code will continue to function in a LINQ to
DataSet application. The relationship of LINQ to DataSet to ADO.NET and the data store is illustrated in the
following diagram.
In This Section
Getting Started
Programming Guide
Reference
DataTableExtensions
DataRowExtensions
DataRowComparer
See also
Language-Integrated Query (LINQ ) - C#
Language-Integrated Query (LINQ ) - Visual Basic
LINQ and ADO.NET
ADO.NET
Getting Started (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
This section provides introductory information about programming with LINQ to DataSet.
In This Section
LINQ to DataSet Overview
Provides a conceptual overview of LINQ to DataSet.
Loading Data Into a DataSet
Provides an example of populating a DataSet. This example uses DataAdapter to retrieve data from a database.
Downloading Sample Databases
Provides information about downloading the AdventureWorks sample database, which is used in the samples
throughout the LINQ to DataSet section.
How to: Create a LINQ to DataSet Project In Visual Studio
Provides information about creating a LINQ to DataSet project in Visual Studio.
Reference
DataRowComparer
DataRowExtensions
DataTableExtensions
See also
LINQ and ADO.NET
Language-Integrated Query (LINQ ) - C#
Language-Integrated Query (LINQ ) - Visual Basic
LINQ to DataSet Overview
5/18/2019 • 3 minutes to read • Edit Online
The DataSet is one of the more widely used components of ADO.NET. It is a key element of the disconnected
programming model that ADO.NET is based on, and it enables you to explicitly cache data from different data
sources. For the presentation tier, the DataSet is tightly integrated with GUI controls for data-binding. For the
middle-tier, it provides a cache that preserves the relational shape of data, and includes fast simple query and
hierarchy navigation services. A common technique used to lower the number of requests on a database is to use
the DataSet for caching in the middle-tier. For example, consider a data-driven ASP.NET Web application. Often, a
significant portion of the application data does not change frequently and is common across sessions or users.
This data can be kept in memory on the Web server, which reduces the number of requests against the database
and speeds up the user’s interactions. Another useful aspect of the DataSet is that it allows an application to bring
subsets of data from one or more data source into the application space. The application can then manipulate the
data in-memory, while retaining its relational shape.
Despite its prominence, the DataSet has limited query capabilities. The Select method can be used for filtering and
sorting, and the GetChildRows and GetParentRow methods can be used for hierarchy navigation. For anything
more complex, however, the developer must write a custom query. This can result in applications that perform
poorly and are difficult to maintain.
LINQ to DataSet makes it easier and faster to query over data cached in a DataSet object. These queries are
expressed in the programming language itself, rather than as string literals embedded in the application code. This
means that developers do not have to learn a separate query language. Additionally, LINQ to DataSet enables
Visual Studio developers to work more productively, because the Visual Studio IDE provides compile-time syntax
checking, static typing, and IntelliSense support for LINQ. LINQ to DataSet can also be used to query over data
that has been consolidated from one or more data sources. This enables many scenarios that require flexibility in
how data is represented and handled. In particular, generic reporting, analysis, and business intelligence
applications require this method of manipulation.
See also
Querying DataSets
Language-Integrated Query (LINQ ) - C#
Language-Integrated Query (LINQ ) - Visual Basic
LINQ to SQL
Loading Data Into a DataSet
4/8/2019 • 3 minutes to read • Edit Online
A DataSet object must first be populated before you can query over it with LINQ to DataSet. There are
several different ways to populate the DataSet. For example, you can use LINQ to SQL to query the database
and load the results into the DataSet. For more information, see LINQ to SQL.
Another common way to load data into a DataSet is to use the DataAdapter class to retrieve data from the
database. This is illustrated in the following example.
Example
This example uses a DataAdapter to query the AdventureWorks database for sales information from the year
2002, and loads the results into a DataSet. After the DataSet has been populated, you can write queries
against it by using LINQ to DataSet. The FillDataSet method in this example is used in the example queries
in LINQ to DataSet Examples. For more information, see Querying DataSets.
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;";
' 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)
da.Fill(ds)
See also
LINQ to DataSet Overview
Querying DataSets
LINQ to DataSet Examples
Downloading Sample Databases (LINQ to DataSet)
4/9/2019 • 2 minutes to read • Edit Online
The samples and walkthroughs in the LINQ to DataSet documentation use the AdventureWorks sample database.
You can download this product free of charge from the Microsoft download site. The samples and walkthroughs in
the LINQ to DataSet documentation use SQL Server as the data store. SQL Server Express Edition, which is
available without charge, can also be used as the data store instead of SQL Server.
If you have installed these files to a different drive or directory, you must revise the paths appropriately
before you execute the sp_attach_db stored procedure.
See also
Getting Started
How to: Create a LINQ to DataSet project In Visual
Studio
10/16/2018 • 2 minutes to read • Edit Online
The different types of LINQ projects require certain assembly references and imported namespaces (Visual
Basic) or using directives (C#). The minimum requirement for LINQ is a reference to System.Core.dll and a
using directive for System.Linq.
These requirements are supplied by default if you create a new C# console app project in Visual Studio 2017. If
you're upgrading a project from an earlier version of Visual Studio, you might have to supply these LINQ -related
references manually.
LINQ to DataSet requires two additional references to System.Data.dll and System.Data.DataSetExtensions.dll.
NOTE
If you're building from a command prompt, you must manually reference the LINQ-related DLLs in
%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.5.
using System.Data;
using System.Linq;
See also
Get started with LINQ to DataSet
Programming Guide (LINQ to DataSet)
1/23/2019 • 2 minutes to read • Edit Online
This section provides conceptual information and examples for programming with LINQ to DataSet.
In This Section
Queries in LINQ to DataSet
Provides information about how to write LINQ to DataSet queries.
Querying DataSets
Describes how to query DataSet objects.
Comparing DataRows
Describes how to use the DataRowComparer object to compare data rows.
Creating a DataTable From a Query
Provides information about creating a DataTable from a LINQ to DataSet query by using the CopyToDataTable
method.
How to: Implement CopyToDataTable<T> Where the Generic Type T Is Not a DataRow
Describes how to implement a custom CopyToDataTable<T> method where the generic parameter T is not of type
DataRow.
Generic Field and SetField Methods
Provides information about the generic Field and SetField methods.
Data Binding and LINQ to DataSet
Describes databinding using the DataView object.
Debugging LINQ to DataSet Queries
Provides information about debugging and troubleshooting LINQ to DataSet queries.
Security
Describes security issues in LINQ to DataSet.
LINQ to DataSet Examples
Provides query examples that use the LINQ operators.
Reference
DataRowComparer
DataRowExtensions
DataTableExtensions
DataView
See also
LINQ and ADO.NET
Language Integrated Query (LINQ )
Queries in LINQ to DataSet
5/14/2019 • 7 minutes to read • Edit Online
A query is an expression that retrieves data from a data source. Queries are usually expressed in a specialized
query language, such as SQL for relational databases and XQuery for XML. Therefore, developers have had to
learn a new query language for each type of data source or data format that they query. Language-Integrated
Query (LINQ ) offers a simpler, consistent model for working with data across various kinds of data sources and
formats. In a LINQ query, you always work with programming objects.
A LINQ query operation consists of three actions: obtain the data source or sources, create the query, and execute
the query.
Data sources that implement the IEnumerable<T> generic interface can be queried through LINQ. Calling
AsEnumerable on a DataTable returns an object which implements the generic IEnumerable<T> interface, which
serves as the data source for LINQ to DataSet queries.
In the query, you specify exactly the information that you want to retrieve from the data source. A query can also
specify how that information should be sorted, grouped, and shaped before it is returned. In LINQ, a query is
stored in a variable. If the query is designed to return a sequence of values, the query variable itself must be a
enumerable type. This query variable takes no action and returns no data; it only stores the query information.
After you create a query you must execute that query to retrieve any data.
In a query that returns a sequence of values, the query variable itself never holds the query results and only stores
the query commands. Execution of the query is deferred until the query variable is iterated over in a foreach or
For Each loop. This is called deferred execution; that is, query execution occurs some time after the query is
constructed. This means that you can execute a query as often as you want to. This is useful when, for example, you
have a database that is being updated by other applications. In your application, you can create a query to retrieve
the latest information and repeatedly execute the query, returning the updated information every time.
In contrast to deferred queries, which return a sequence of values, queries that return a singleton value are
executed immediately. Some examples of singleton queries are Count, Max, Average, and First. These execute
immediately because the query results are required to calculate the singleton result. For example, in order to find
the average of the query results the query must be executed so that the averaging function has input data to work
with. You can also use the ToList or ToArray methods on a query to force immediate execution of a query that does
not produce a singleton value. These techniques to force immediate execution can be useful when you want to
cache the results of a query.
Queries
LINQ to DataSet queries can be formulated in two different syntaxes: query expression syntax and method-based
query syntax.
Query Expression Syntax
Query expressions are a declarative query syntax. This syntax enables a developer to write queries in C# or Visual
Basic in a format similar to SQL. By using query expression syntax, you can perform even complex filtering,
ordering, and grouping operations on data sources with minimal code. For more information, see LINQ Query
Expressions and Basic Query Operations (Visual Basic).
Query expression syntax is new in C# 3.0 and Visual Basic 2008. However, the .NET Framework common
language runtime (CLR ) cannot read the query expression syntax itself. Therefore, at compile time, query
expressions are translated to something that the CLR does understand: method calls. These methods are referred
to as the standard query operators. As a developer, you have the option of calling them directly by using method
syntax, instead of using query syntax. For more information, see Query Syntax and Method Syntax in LINQ. For
more information about the standard query operators, see Standard Query Operators Overview.
The following example uses Select to return all the rows from Product table and display the product names.
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"));
}
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);
}
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
Composing Queries
As mentioned earlier in this topic, the query variable itself only stores the query commands when the query is
designed to return a sequence of values. If the query does not contain a method that will cause immediate
execution, the actual execution of the query is deferred until you iterate over the query variable in a foreach or
For Each loop. Deferred execution enables multiple queries to be combined or a query to be extended. When a
query is extended, it is modified to include the new operations, and the eventual execution will reflect the changes.
In the following example, the first query returns all the products. The second query extends the first by using
Where to return all the products of size "L":
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> productsQuery =
from product in products.AsEnumerable()
select product;
IEnumerable<DataRow> largeProducts =
productsQuery.Where(p => p.Field<string>("Size") == "L");
Dim largeProducts = _
productsQuery.Where(Function(p) p.Field(Of String)("Size") = "L")
After a query has been executed, no additional queries can be composed, and all subsequent queries will use the
in-memory LINQ operators. Query execution will occur when you iterate over the query variable in a foreach or
For Each statement, or by a call to one of the LINQ conversion operators that cause immediate execution. These
operators include the following: ToList, ToArray, ToLookup, and ToDictionary.
In the following example, the first query returns all the products ordered by list price. The ToArray method is used
to force immediate query execution:
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> query =
from product in products.AsEnumerable()
orderby product.Field<Decimal>("ListPrice") descending
select product;
Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of Decimal)("ListPrice") Descending _
Select product
See also
Programming Guide
Querying DataSets
Getting Started with LINQ in C#
Getting Started with LINQ in Visual Basic
Querying DataSets (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
After a DataSet object has been populated with data, you can begin querying it. Formulating queries with LINQ
to DataSet is similar to using Language-Integrated Query (LINQ ) against other LINQ -enabled data sources.
Remember, however, that when you use LINQ queries over a DataSet object you are querying an enumeration of
DataRow objects, instead of an enumeration of a custom type. This means that you can use any of the members
of the DataRow class in your LINQ queries. This lets you to create rich, complex queries.
As with other implementations of LINQ, you can create LINQ to DataSet queries in two different forms: query
expression syntax and method-based query syntax. You can use query expression syntax or method-based query
syntax to perform queries against single tables in a DataSet, against multiple tables in a DataSet, or against tables
in a typed DataSet.
In This Section
Single-Table Queries
Describes how to perform single-table queries.
Cross-Table Queries
Describes how to perform cross-table queries.
Querying Typed DataSets
Describes how to query typed DataSet objects.
See also
LINQ to DataSet Examples
Loading Data Into a DataSet
Single-Table Queries (LINQ to DataSet)
4/8/2019 • 3 minutes to read • Edit Online
Language-Integrated Query (LINQ ) queries work on data sources that implement the IEnumerable<T> interface
or the IQueryable<T> interface. The DataTable class does not implement either interface, so you must call the
AsEnumerable method if you want to use the DataTable as a source in the From clause of a LINQ query.
The following example gets all the online orders from the SalesOrderHeader table and outputs the order ID, order
date, and order number to the console.
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")
};
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") _
}
See also
Cross-Table Queries
Querying Typed DataSets
Generic Field and SetField Methods
Cross-Table Queries (LINQ to DataSet)
6/4/2019 • 2 minutes to read • Edit Online
In addition to querying a single table, you can also perform cross-table queries in LINQ to DataSet. This is done
by using a join. A join is the association of objects in one data source with objects that share a common attribute in
another data source, such as a product or contact ID. In object-oriented programming, relationships between
objects are relatively easy to navigate because each object has a member that references another object. In
external database tables, however, navigating relationships is not as straightforward. Database tables do not
contain built-in relationships. In these cases, the join operation can be used to match elements from each source.
For example, given two tables that contain product information and sales information, you could use a join
operation to match sales information and products for the same sales order.
The Language-Integrated Query (LINQ ) framework provides two join operators, Join and GroupJoin. These
operators perform equi-joins: that is, joins that match two data sources only when their keys are equal. (By
contrast, Transact-SQL supports join operators other than equals , such as the less than operator.)
In relational database terms, Join implements an inner join. An inner join is a type of join in which only those
objects that have a match in the opposite data set are returned.
The GroupJoin operators have no direct equivalent in relational database terms; they implement a superset of
inner joins and left outer joins. A left outer join is a join that returns each element of the first (left) collection, even
if it has no correlated elements in the second collection.
For more information about joins, see Join Operations.
Example
The following example performs a traditional join of the SalesOrderHeader and SalesOrderDetail tables from the
AdventureWorks sample database to obtain online orders from the month of August.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
};
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") _
}
See also
Querying DataSets
Single-Table Queries
Querying Typed DataSets
Join Operations
LINQ to DataSet Examples
Query typed DataSets
8/29/2018 • 2 minutes to read • Edit Online
If the schema of the DataSet is known at application design time, we recommend that you use a typed DataSet
when using LINQ to DataSet. A typed DataSet is a class that derives from a DataSet. As such, it inherits all the
methods, events, and properties of a DataSet. Additionally, a typed DataSet provides strongly typed methods,
events, and properties. This means that you can access tables and columns by name, instead of using collection-
based methods. This makes queries simpler and more readable. For more information, see Typed DataSets.
LINQ to DataSet also supports querying over a typed DataSet. With a typed DataSet, you do not have to use the
generic Field method or SetField method to access column data. Property names are available at compile time
because the type information is included in the DataSet. LINQ to DataSet provides access to column values as the
correct type, so that type mismatch errors are caught when the code is compiled instead of at run time.
Before you can begin querying a typed DataSet, you must generate the class by using the DataSet Designer in
Visual Studio. For more information, see Create and configure DataSets.
Example
The following example shows a query over a typed DataSet:
Dim query = _
From o In orders _
Where o.OnlineOrderFlag = True _
Select New {SalesOrderID := o.SalesOrderID, _
OrderDate := o.OrderDate, _
SalesOrderNumber := o.SalesOrderNumber}
See also
Querying DataSets
Cross-Table Queries
Single-Table Queries
Comparing DataRows (LINQ to DataSet)
4/28/2019 • 2 minutes to read • Edit Online
Language-Integrated Query (LINQ ) defines various set operators to compare source elements to see if they are
equal. LINQ provides the following set operators:
Distinct
Union
Intersect
Except
These operators compare source elements by calling the GetHashCode and Equals methods on each collection of
elements. In the case of a DataRow, these operators perform a reference comparison, which is generally not the
ideal behavior for set operations over tabular data. For set operations, you usually want to determine whether the
element values are equal and not the element references. Therefore, the DataRowComparer class has been added
to LINQ to DataSet. This class can be used to compare row values.
The DataRowComparer class contains a value comparison implementation for DataRow, so this class can be used
for set operations such as Distinct. This class cannot be directly instantiated; instead, the Default property must be
used to return an instance of the DataRowComparer<TRow>. The Equals method is then called and the two
DataRow objects to be compared are passed in as input parameters. The Equals method returns true if the
ordered set of column values in both DataRow objects are equal; otherwise, false .
Example
This example uses Intersect to return contacts that appear in both tables.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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
Example
The following example compares two rows and gets their hash codes.
' 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)
See also
DataRowComparer
Loading Data Into a DataSet
LINQ to DataSet Examples
Creating a DataTable From a Query (LINQ to
DataSet)
4/9/2019 • 10 minutes to read • Edit Online
Data binding is a common use of DataTable object. The CopyToDataTable method takes the results of a query and
copies the data into a DataTable, which can then be used for data binding. When the data operations have been
performed, the new DataTable is merged back into the source DataTable.
The CopyToDataTable method uses the following process to create a DataTable from a query:
1. The CopyToDataTable method clones a DataTable from the source table (a DataTable object that
implements the IQueryable<T> interface). The IEnumerable source has generally originated from a LINQ
to DataSet expression or method query.
2. The schema of the cloned DataTable is built from the columns of the first enumerated DataRow object in
the source table and the name of the cloned table is the name of the source table with the word "query"
appended to it.
3. For each row in the source table, the content of the row is copied into a new DataRow object, which is then
inserted into the cloned table. The RowState and RowError properties are preserved across the copy
operation. An ArgumentException is thrown if the DataRow objects in the source are from different tables.
4. The cloned DataTable is returned after all DataRow objects in the input queryable table have been copied. If
the source sequence does not contain any DataRow objects, the method returns an empty DataTable.
Note that calling the CopyToDataTable method will cause the query bound to the source table to execute.
When the CopyToDataTable method encounters either a null reference or nullable value type in a row in the
source table, it replaces the value with Value. This way, null values are handled correctly in the returned DataTable.
Note: The CopyToDataTable method accepts as input a query that can return rows from multiple DataTable or
DataSet objects. The CopyToDataTable method will copy the data but not the properties from the source
DataTable or DataSet objects to the returned DataTable. You will need to explicitly set the properties on the
returned DataTable, such as Locale and TableName.
The following example queries the SalesOrderHeader table for orders after August 8, 2001 and uses the
CopyToDataTable method to create a DataTable from that query. The DataTable is then bound to a BindingSource,
which acts as proxy for a DataGridView.
// Bind the System.Windows.Forms.DataGridView object
// to the System.Windows.Forms.BindingSource object.
dataGridView.DataSource = bindingSource;
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
End Class
Example
This example performs a join over the SalesOrderHeader and SalesOrderDetail tables to get online orders from
the month of August and creates a table from the query.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
};
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") _
}
Example
The following example queries a collection for items of price greater than $9.99 and creates a table from the query
results.
// 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"}};
Example
The following example queries a collection for items of price greater than 9.99 and projects the results. The
returned sequence of anonymous types is loaded into an existing table.
// 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));
query.CopyToDataTable(table, LoadOption.PreserveChanges);
' 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.CopyToDataTable(table, LoadOption.PreserveChanges)
Example
The following example queries a collection for items of price greater than $9.99 and projects the results. The
returned sequence of anonymous types is loaded into an existing table. The table schema is automatically
expanded because the Book and Movies types are derived from the Item type.
// 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.CopyToDataTable(table, LoadOption.PreserveChanges);
Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"
query.CopyToDataTable(table, LoadOption.PreserveChanges)
Example
The following example queries a collection for items of price greater than $9.99 and returns a sequence of Double,
which is loaded into a new table.
// 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"}};
See also
Programming Guide
Generic Field and SetField Methods
LINQ to DataSet Examples
How to: Implement CopyToDataTable<T> Where the
Generic Type T Is Not a DataRow
4/9/2019 • 8 minutes to read • Edit Online
The DataTable object is often used for data binding. The CopyToDataTable method takes the results of a query and
copies the data into a DataTable, which can then be used for data binding. The CopyToDataTable methods,
however, only operate on an IEnumerable<T> source where the generic parameter T is of type DataRow.
Although this is useful, it does not allow tables to be created from a sequence of scalar types, from queries that
project anonymous types, or from queries that perform table joins.
This topic describes how to implement two custom CopyToDataTable<T> extension methods that accept a generic
parameter T of a type other than DataRow. The logic to create a DataTable from an IEnumerable<T> source is
contained in the ObjectShredder<T> class, which is then wrapped in two overloaded CopyToDataTable<T> extension
methods. The Shred method of the ObjectShredder<T> class returns the filled DataTable and accepts three input
parameters: an IEnumerable<T> source, a DataTable, and a LoadOption enumeration. The initial schema of the
returned DataTable is based on the schema of the type T . If an existing table is provided as input, the schema
must be consistent with the schema of the type T . Each public property and field of the type T is converted to a
DataColumn in the returned table. If the source sequence contains a type derived from T , the returned table
schema is expanded for any additional public properties or fields.
For examples of using the custom CopyToDataTable<T> methods, see Creating a DataTable From a Query.
To implement the custom CopyToDataTable<T> methods in your application
1. Implement the ObjectShredder<T> class to create a DataTable from an IEnumerable<T> source:
// 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)
{
// Load the table from the scalar sequence if T is a primitive type.
if (typeof(T).IsPrimitive)
{
{
return ShredPrimitive(source, table, options);
}
// 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();
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();
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();
}
' 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
' 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
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
' Initialize the ordinal map and extend the table schema based on type T.
' 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()
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()
Next
Next
End Class
The preceding example assumes that the properties of the DataColumn are not nullable types. To handle
properties with nullable types, use the following code:
<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. Add the ObjectShredder<T> class and CopyToDataTable<T> extension methods to your application.
Module Module1
Sub Main()
' Your application code using CopyToDataTable<T>.
End Sub
End Module
class Program
{
static void Main(string[] args)
{
// Your application code using CopyToDataTable<T>.
}
}
public static class CustomLINQtoDataSetMethods
{
…
}
public class ObjectShredder<T>
{
…
}
See also
Creating a DataTable From a Query
Programming Guide
Generic Field and SetField Methods (LINQ to
DataSet)
6/25/2019 • 3 minutes to read • Edit Online
LINQ to DataSet provides extension methods to the DataRow class for accessing column values: the Field method
and the SetField method. These methods provide easier access to column values for developers, especially
regarding null values. The DataSet uses DBNull.Value to represent null values, whereas LINQ uses the Nullable
and Nullable<T> types. Using the pre-existing column accessor in DataRow requires you to cast the return object
to the appropriate type. If a particular field in a DataRow can be null, you must explicitly check for a null value
because returning DBNull.Value and implicitly casting it to another type throws an InvalidCastException. In the
following example, if the DataRow.IsNull method was not used to check for a null value, an exception would be
thrown if the indexer returned DBNull.Value and tried to cast it to a String.
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"]
};
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 _
}
The Field method provides access to the column values of a DataRow and the SetField sets column values in a
DataRow. Both the Field method and SetField method handle nullable types, so you do not have to explicitly check
for null values as in the previous example. Both methods are generic methods, also, so you do not have to cast the
return type.
The following example uses the Field method.
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")
};
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") _
}
Note that the data type specified in the generic parameter T of the Field method and the SetField method must
match the type of the underlying value. Otherwise, an InvalidCastException exception will be thrown. The specified
column name must also match the name of a column in the DataSet, or an ArgumentException will be thrown. In
both cases, the exception is thrown at run time during the enumeration of the data when the query is executed.
The SetField method itself does not perform any type conversions. This does not mean, however, that a type
conversion will not occur. The SetField method exposes the ADO.NET behavior of the DataRow class. A type
conversion could be performed by the DataRow object and the converted value would then be saved to the
DataRow object.
See also
DataRowExtensions
Data Binding and LINQ to DataSet
4/8/2019 • 2 minutes to read • Edit Online
Data binding is the process that establishes a connection between the application UI and business logic. If the
binding has the correct settings and the data provides the proper notifications, when the data changes its value,
the elements that are bound to the data reflect changes automatically. The DataSet is an in- memory
representation of data that provides a consistent relational programming model, regardless of the source of the
data it contains. The ADO.NET 2.0 DataView enables you to sort and filter the data stored in a DataTable. This
functionality is often used in data-binding applications. By using a DataView, you can expose the data in a table
with different sort orders, and you can filter the data by row state or based on a filter expression. For more
information about the DataView object, see DataViews.
LINQ to DataSet allows developers to create complex, powerful queries over a DataSet by using Language-
Integrated Query (LINQ ). However, a LINQ to DataSet query returns an enumeration of DataRow objects, which
is not easily used in a binding scenario. To make binding easier, you can create a DataView from a LINQ to
DataSet query. This DataView uses the filtering and sorting specified in the query, but is better suited for data
binding. LINQ to DataSet extends the functionality of the DataView by providing LINQ expression-based filtering
and sorting, which allows for much more complex and powerful filtering and sorting operations than string-based
filtering and sorting.
Note that the DataView represents the query itself and is not a view on top of the query. The DataView is bound
to a UI control, such as a DataGrid or a DataGridView, providing a simple data binding model. A DataView can
also be created from a DataTable, providing a default view of that table.
In This Section
Creating a DataView Object
Provides information about creating a DataView.
Filtering with DataView
Describes how to filter with the DataView.
Sorting with DataView
Describes how to sort with the DataView.
Querying the DataRowView Collection in a DataView
Provides information about querying the DataRowView collection exposed by DataView.
DataView Performance
Provides information about DataView and performance.
How to: Bind a DataView Object to a Windows Forms DataGridView Control
Describes how to bind a DataView object to a DataGridView.
See also
Programming Guide
Creating a DataView Object (LINQ to DataSet)
4/28/2019 • 4 minutes to read • Edit Online
There are two ways to create a DataView in the LINQ to DataSet context. You can create a DataView from a LINQ
to DataSet query over a DataTable, or you can create it from a typed or un-typed DataTable. In both cases, you
create the DataView by using one of the AsDataView extension methods; DataView is not directly constructible in
the LINQ to DataSet context.
After the DataView has been created, you can bind it to a UI control in a Windows forms application or an
ASP.NET application, or change the filtering and sorting settings.
DataView constructs an index, which significantly increases the performance of operations that can use the index,
such as filtering and sorting. The index for a DataView is built both when the DataView is created and when any of
the sorting or filtering information is modified. Creating a DataView and then setting the sorting or filtering
information later causes the index to be built at least twice: once when the DataView is created, and again when
any of the sort or filter properties are modified.
For more information about filtering and sorting with DataView, see Filtering with DataView and Sorting with
DataView.
NOTE
In most cases, the expressions used for filtering and sorting should not have side effects and must be deterministic. Also, the
expressions should not contain any logic that depend on a set number of executions, as the sorting and filtering operations
may be executed any number of times.
Creating a DataView from a query that returns anonymous types or queries that perform join operations is not
supported.
Only the following query operators are supported in a query used to create DataView:
Cast
OrderBy
OrderByDescending
Select
ThenBy
ThenByDescending
Where
Note that when a DataView is created from a LINQ to DataSet query the Select method must be the final method
called in the query. This is shown in the following example, which creates a DataView of online orders sorted by
total due:
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;
bindingSource1.DataSource = view;
Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True _
Order By order.Field(Of Decimal)("TotalDue") _
Select order
You can also use the string-based RowFilter and Sort properties to filter and sort a DataView after it has been
created from a query. Note that this will clear the sorting and filtering information inherited from the query. The
following example creates a DataView from a LINQ to DataSet query that filters by last names that start with 'S'.
The string-based Sort property is set to sort on last names in ascending order and then first names in descending
order:
bindingSource1.DataSource = view;
Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName").StartsWith("S") _
Select contact
dataGridView1.AutoResizeColumns();
Filtering and sorting can be set on the DataView after it has been created from a DataTable. The following example
creates a DataView from the Contact table and sets the Sort property to sort on last names in ascending order and
then first names in descending order:
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()
However, there is a performance loss that comes with setting the RowFilter or Sort property after the DataView
has been created from a query, because DataView constructs an index to support filtering and sorting operations.
Setting the RowFilter or Sort property rebuilds the index for the data, adding overhead to your application and
decreasing performance. When possible, it is better to specify the filtering and sorting information when you first
create the DataView and avoid modifying it afterwards.
See also
Data Binding and LINQ to DataSet
Filtering with DataView
Sorting with DataView
Filtering with DataView (LINQ to DataSet)
4/28/2019 • 9 minutes to read • Edit Online
The ability to filter data using specific criteria and then present the data to a client through a UI control is an
important aspect of data binding. DataView provides several ways to filter data and return subsets of data rows
meeting specific filter criteria. In addition to the string-based filtering capabilities DataView also provides the
ability to use LINQ expressions for the filtering criteria. LINQ expressions allow for much more complex and
powerful filtering operations than the string-based filtering.
There are two ways to filter data using a DataView:
Create a DataView from a LINQ to DataSet query with a Where clause.
Use the existing, string-based filtering capabilities of DataView.
NOTE
In most cases, the expressions used for filtering should not have side effects and must be deterministic. Also, the expressions
should not contain any logic that depends on a set number of executions, because the filtering operations might be
executed any number of times.
Example
The following example queries the SalesOrderDetail table for orders with a quantity greater than 2 and less than
6; creates a DataView from that query; and binds the DataView to a BindingSource:
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
Example
The following example creates a DataView from a query for orders placed after June 6, 2001:
bindingSource1.DataSource = view;
Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") > New DateTime(2002, 6, 1) _
Select order
Example
Filtering can also be combined with sorting. The following example creates a DataView from a query for contacts
whose last name start with "S" and sorted by last name, then first name:
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
Example
The following example uses the SoundEx algorithm to find contacts whose last name is similar to "Zhu". The
SoundEx algorithm is implemented in the SoundEx method.
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
Dim query = _
From contact In contacts.AsEnumerable() _
Where SoundEx(contact.Field(Of String)("LastName")) = soundExCode _
Select contact
SoundEx is a phonetic algorithm used for indexing names by sound, as they are pronounced in English, originally
developed by the U.S. Census Bureau. The SoundEx method returns a four character code for a name consisting
of an English letter followed by three numbers. The letter is the first letter of the name and the numbers encode
the remaining consonants in the name. Similar sounding names share the same SoundEx code. The SoundEx
implementation used in the SoundEx method of the previous example is shown here:
// Value to return.
string value = "";
// 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;
}
// 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));
Case "L"
currCode = 4
Case "R"
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
view.RowFilter = "LastName='Zhu'";
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
After a DataView has been created from a DataTable or LINQ to DataSet query, you can use the RowFilter
property to specify subsets of rows based on their column values. The string-based and expression-based filters
are mutually exclusive. Setting the RowFilter property will clear the filter expression inferred from the LINQ to
DataSet query, and the filter expression cannot be reset.
DataTable contacts = dataSet.Tables["Contact"];
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
view.RowFilter = "LastName='Zhu'";
Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName") = "Hernandez" _
Select contact
dataGridView1.AutoResizeColumns()
view.RowFilter = "LastName='Zhu'"
If you want to return the results of a particular query on the data, as opposed to providing a dynamic view of a
subset of the data, you can use the Find or FindRows methods of the DataView, rather than setting the RowFilter
property. The RowFilter property is best used in a data-bound application where a bound control displays filtered
results. Setting the RowFilter property rebuilds the index for the data, adding overhead to your application and
decreasing performance. The Find and FindRows methods use the current index without requiring the index to be
rebuilt. If you are going to call Find or FindRows only once, then you should use the existing DataView. If you are
going to call Find or FindRows multiple times, you should create a new DataView to rebuild the index on the
column you want to search on, and then call the Find or FindRows methods. For more information about the Find
and FindRows methods see Finding Rows and DataView Performance.
bindingSource1.DataSource = view;
view.RowFilter = null;
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
Example
The following example creates a DataView from a table sets the RowFilter property, and then clears the filter by
setting the RowFilter property to an empty string:
view.RowFilter = "LastName='Zhu'";
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
See also
Data Binding and LINQ to DataSet
Sorting with DataView
Sorting with DataView (LINQ to DataSet)
4/28/2019 • 4 minutes to read • Edit Online
The ability to sort data based on specific criteria and then present the data to a client through a UI control is an
important aspect of data binding. DataView provides several ways to sort data and return data rows ordered by
specific ordering criteria. In addition to its string-based sorting capabilities, DataView also enables you to use
Language-Integrated Query (LINQ ) expressions for the sorting criteria. LINQ expressions allow for much more
complex and powerful sorting operations than string-based sorting. This topic describes both approaches to
sorting using DataView.
NOTE
In most cases, the expressions used for sorting should not have side effects and must be deterministic. Also, the expressions
should not contain any logic that depends on a set number of executions, because the sorting operations might be
executed any number of times.
Example
The following example queries the SalesOrderHeader table and orders the returned rows by the order date;
creates a DataView from that query; and binds the DataView to a BindingSource.
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
Example
The following example queries the SalesOrderHeader table and orders the returned row by total amount due;
creates a DataView from that query; and binds the DataView to a BindingSource.
EnumerableRowCollection<DataRow> query =
from order in orders.AsEnumerable()
orderby order.Field<decimal>("TotalDue")
select order;
bindingSource1.DataSource = view;
Dim query = _
From order In orders.AsEnumerable() _
Order By order.Field(Of Decimal)("TotalDue") _
Select order
Example
The following example queries the SalesOrderDetail table and orders the returned rows by order quantity and
then by sales order ID; creates a DataView from that query; and binds the DataView to a BindingSource.
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
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()
Example
The following example queries the Contact table for last names that start with the letter "S". A DataView is created
from that query and bound to a BindingSource object.
bindingSource1.DataSource = view;
Dim query = _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("LastName").StartsWith("S") _
Select contact
bindingSource1.DataSource = view;
view.Sort = "";
Dim query = _
From order In orders.AsEnumerable() _
Order By order.Field(Of Decimal)("TotalDue") _
Select order
Example
The following example creates a DataView from the Contact table and sets the Sort property to sort by last name
in descending order. The sorting information is then cleared by setting the Sort property to null :
DataTable contacts = dataSet.Tables["Contact"];
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()
See also
Data Binding and LINQ to DataSet
Filtering with DataView
Sorting Data
Querying the DataRowView Collection in a DataView
4/8/2019 • 2 minutes to read • Edit Online
The DataView exposes an enumerable collection of DataRowView objects. DataRowView represents a customized
view of a DataRow and displays a specific version of that DataRow in a control. Only one version of a DataRow can
be displayed through a control, such as a DataGridView. You can access the DataRow that is exposed by the
DataRowView through the Row property of the DataRowView. When you view values by using a DataRowView,
the RowStateFilter property determines which row version of the underlying DataRow is exposed. For information
about accessing different row versions using a DataRow, see Row States and Row Versions. Because the collection
of DataRowView objects exposed by the DataView is enumerable, you can use LINQ to DataSet to query over it.
The following example queries the Product table for red-colored products and creates a table from that query. A
DataView is created from the table and the RowStateFilter property is set to filter on deleted and modified rows.
The DataView is then used as a source in a LINQ query, and the DataRowView objects that have been modified
and deleted are bound to a DataGridView control.
dataGridView2.DataSource = modifiedDeletedQuery.ToList();
Dim products As DataTable = dataSet.Tables("Product")
dataGridView2.DataSource = modifiedDeletedQuery.ToList()
The following example creates a table of products from a view that is bound to a DataGridView control. The
DataView is queried for red-colored products and the ordered results are bound to a DataGridView control.
// 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")};
' 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")}
See also
Data Binding and LINQ to DataSet
DataView Performance
4/8/2019 • 3 minutes to read • Edit Online
This topic discusses the performance benefits of using the Find and FindRows methods of the DataView class, and
of caching a DataView in a Web application.
Dim query = _
From contact In contacts.AsEnumerable() _
Order By contact.Field(Of String)("LastName") _
Select contact
The following example uses the FindRows method to find all the red colored products.
DataTable products = dataSet.Tables["Product"];
view.Sort = "Color";
Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of Decimal)("ListPrice"), product.Field(Of String)("Color") _
Select product
ASP.NET
ASP.NET has a caching mechanism that allows you to store objects that require extensive server resources to
create in memory. Caching these types of resources can significantly improve the performance of your application.
Caching is implemented by the Cache class, with cache instances that are private to each application. Because
creating a new DataView object can be resource intensive, you might want to use this caching functionality in Web
applications so that the DataView does not have to be rebuilt every time the Web page is refreshed.
In the following example, the DataView is cached so that the data does not have to be re-sorted when the page is
refreshed.
If (Cache("ordersView") = Nothing) Then
FillDataSet(dataSet)
Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True _
Order By order.Field(Of Decimal)("TotalDue") _
Select order
Cache.Insert("ordersView", view)
End If
GridView1.DataSource = ordersView
GridView1.DataBind()
if (Cache["ordersView"] == null)
{
// Fill the DataSet.
DataSet dataSet = FillDataSet();
EnumerableRowCollection<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<bool>("OnlineOrderFlag") == true
orderby order.Field<decimal>("TotalDue")
select order;
GridView1.DataSource = ordersView;
GridView1.DataBind();
See also
Data Binding and LINQ to DataSet
How to: Bind a DataView Object to a Windows
Forms DataGridView Control
4/9/2019 • 2 minutes to read • Edit Online
The DataGridView control provides a powerful and flexible way to display data in a tabular format. The
DataGridView control supports the standard Windows Forms data binding model, so it will bind to DataView and
a variety of other data sources. In most situations, however, you will bind to a BindingSource component that will
manage the details of interacting with the data source.
For more information about the DataGridView control, see DataGridView Control Overview.
To connect a DataGridView control to a DataView
1. Implement a method to handle the details of retrieving data from a database. The following code example
implements a GetData method that initializes a SqlDataAdapter component and uses it to fill a DataSet. Be
sure to set the connectionString variable to a value that is appropriate for your database. You will need
access to a server with the AdventureWorks SQL Server sample database installed.
}
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"
Catch ex As SqlException
MessageBox.Show(ex.Message)
End Try
End Sub
2. In the Load event handler of your form, bind the DataGridView control to the BindingSource component
and call the GetData method to retrieve the data from the database. The DataView is created from a LINQ
to DataSet query over the Contact DataTable and is then bound to the BindingSource component.
contactDataGridView.DataSource = contactBindingSource;
contactView = contactQuery.AsDataView();
contactDataGridView.DataSource = contactBindingSource
contactView = contactQuery.AsDataView()
See also
Data Binding and LINQ to DataSet
Debugging LINQ to DataSet Queries
4/8/2019 • 2 minutes to read • Edit Online
Visual Studio supports the debugging of LINQ to DataSet code. However, there are some differences between
debugging LINQ to DataSet code and non-LINQ to DataSet managed code. Most debugging features work with
LINQ to DataSet statements, including stepping, setting breakpoints, and viewing results that are shown in
debugger windows. However, deferred query execution in has some side effects that you should consider while
debugging LINQ to DataSet code and there are some limitations to using Edit and Continue. This topic discusses
aspects of debugging that are unique to LINQ to DataSet compared to non-LINQ to DataSet managed code.
Viewing Results
You can view the result of a LINQ to DataSet statement by using DataTips, the Watch window, and the
QuickWatch dialog box. By using a source window, you can pause the pointer on a query in the source window and
a DataTip will appear. You can copy a LINQ to DataSet variable and paste it into the Watch window or the
QuickWatch dialog box. In LINQ to DataSet, a query is not evaluated when it is created or declared, but only when
the query is executed. This is called deferred execution. Therefore, the query variable does not have a value until it
is evaluated. For more information, see Queries in LINQ to DataSet.
The debugger must evaluate a query to display the query results. This implicit evaluation occurs when you view a
LINQ to DataSet query result in the debugger, and it has some effects you should consider. Each evaluation of the
query takes time. Expanding the results node takes time. For some queries, repeated evaluation might cause a
noticeable performance penalty. Evaluating a query can also cause side effects, which are changes to the value of
data or the state of your program. Not all queries have side effects. To determine whether a query can be safely
evaluated without side effects, you must understand the code that implements the query. For more information,
see Side Effects and Expressions.
See also
Debugging Managed Code
Programming Guide
Security (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
External Input
Applications often take external input (from a user or another external agent) and perform actions based on that
input. In the case of LINQ to DataSet, the application might construct a query in a certain way, based on external
input or use external input in the query. LINQ to DataSet queries accept parameters everywhere that literals are
accepted. Application developers should use parameterized queries, rather than injecting literals from an external
agent directly into the query.
Any input directly or indirectly derived from the user or an external agent might have content that leverages the
syntax of the target language in order to perform unauthorized actions. This is known as a SQL injection attack,
named after an attack pattern where the target language is Transact-SQL. User input injected directly into the
query is used to drop a database table, cause a denial of service, or otherwise change the nature of the operation
being performed. Although query composition is possible in LINQ to DataSet, it is performed through the object
model API. LINQ to DataSet queries are not composed by using string manipulation or concatenation, as they are
in Transact-SQL, and are not susceptible to SQL injection attacks in the traditional sense.
See also
Programming Guide
LINQ to DataSet Examples
4/28/2019 • 2 minutes to read • Edit Online
This section provides LINQ to DataSet programming examples that use the standard query operators. The
DataSet used in these examples is populated by using the FillDataSet method, which is specified in Loading
Data Into a DataSet. For more information, see Standard Query Operators Overview (C#) or Standard Query
Operators Overview (Visual Basic).
In This Section
Query Expression Examples
Contains the following examples:
Projection
Restriction
Partitioning
Ordering
Element Operators
Aggregate Operators
Join Operators
Method-Based Query Examples
Contains the following examples:
Projection
Partitioning
Ordering
Set Operators
Conversion Operators
Element Operators
Aggregate Operators
Join
DataSet-Specific Operator Examples
Contains examples that demonstrate how to use the CopyToDataTable method and the DataRowComparer
class.
See also
Programming Guide
Loading Data Into a DataSet
Query Expression Examples (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
This section provides LINQ to DataSet programming examples in query expression syntax that use the standard
query operators. The DataSet used in these examples is populated by using the FillDataSet method, which is
specified in Loading Data Into a DataSet. For more information, see Standard Query Operators Overview (C#) or
Standard Query Operators Overview (Visual Basic).
In This Section
Projection
The examples in this topic demonstrate how to use the Select and SelectMany methods to query a DataSet.
Restriction
The examples in this topic demonstrate how to use the Where method to query a DataSet.
Partitioning
The examples in this topic demonstrate how to use the Skip and Take methods to query a DataSet and partition
the results.
Ordering
The examples in this topic demonstrate how to use the OrderBy, OrderByDescending, Reverse, and
ThenByDescending methods to query a DataSet and order the results.
Element Operators
The examples in this topic demonstrate how to use the First and ElementAt methods to get DataRow elements
from a DataSet.
Aggregate Operators
The examples in this topic demonstrate how to use the Average, Count, Max, Min, and Sum methods to query a
DataSet and aggregate data.
Join Operators
The examples in this topic demonstrate how to use the GroupJoin and Join methods to query a DataSet.
See also
Method-Based Query Examples
DataSet-Specific Operator Examples
LINQ to DataSet Examples
Query Expression Syntax Examples: Projection (LINQ
to DataSet)
4/8/2019 • 5 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Select and SelectMany methods to query a DataSet using
the query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Select
Example
This example uses the Select method to return all the rows from the Product table and display the product names.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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"));
}
Example
This example uses Select to return a sequence of only product names.
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)
Console.WriteLine("Product Names:")
For Each productName In query
Console.WriteLine(productName)
Next
SelectMany
Example
This example uses From …, … (the equivalent of the SelectMany method) to select all orders where TotalDue is
less than 500.00.
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")
};
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") _
}
Example
This example uses From …, … (the equivalent of the SelectMany method) to select all orders where the order was
made on October 1, 2002 or later.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
};
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") _
}
Example
This example uses a From …, … (the equivalent of the SelectMany method) to select all orders where the order
total is greater than 10000.00 and uses From assignment to avoid requesting the total twice.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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);
}
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 _
}
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Query Expression Syntax Examples: Restriction (LINQ
to DataSet)
4/8/2019 • 3 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Where method to query a DataSet using the query
expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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;
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Where
Example
This example returns all online orders.
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")
};
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") _
}
Example
This example returns the orders where the order quantity is greater than 2 and less than 6.
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")
};
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") _
}
Example
This example returns all red colored products.
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")
};
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") _
}
Example
This example uses the Where method to find orders that were made after December 1, 2002 and then uses the
GetChildRows method to get the details for each order.
IEnumerable<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") >= new DateTime(2002, 12, 1)
select order;
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Query Expression Syntax Examples: Partitioning
(LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Skip and Take methods to query a DataSet using the query
expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Skip
Example
This example uses the Skip method to get all but the first two addresses in Seattle.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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);
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)
Take
Example
This example uses the Take method to get the first three addresses in Seattle.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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);
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)
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Query Expression Syntax Examples: Ordering (LINQ
to DataSet)
4/8/2019 • 4 minutes to read • Edit Online
The examples in this topic demonstrate how to use the OrderBy, OrderByDescending, Reverse, and
ThenByDescending methods to query a DataSet and order the results using the query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
OrderBy
Example
This example uses OrderBy to return a list of contacts ordered by last name.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> query =
from contact in contacts.AsEnumerable()
orderby contact.Field<string>("LastName")
select contact;
Dim query = _
From contact In contacts.AsEnumerable() _
Select contact _
Order By contact.Field(Of String)("LastName")
Example
This example uses OrderBy to sort a list of contacts by length of last name.
IEnumerable<DataRow> query =
from contact in contacts.AsEnumerable()
orderby contact.Field<string>("LastName").Length
select contact;
Dim query = _
From contact In contacts.AsEnumerable() _
Select contact _
Order By contact.Field(Of String)("LastName").Length
OrderByDescending
Example
This example uses orderby… descending ( Order By … Descending ), which is equivalent to the OrderByDescending
method, to sort the price list from highest to lowest.
IEnumerable<Decimal> query =
from product in products.AsEnumerable()
orderby product.Field<Decimal>("ListPrice") descending
select product.Field<Decimal>("ListPrice");
Dim query = _
From product In products.AsEnumerable() _
Select product _
Order By product.Field(Of Decimal)("ListPrice") Descending
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"));
}
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")
ThenByDescending
Example
This example uses OrderBy… Descending , which is equivalent to the ThenByDescending method, to sort a list of
products, first by name and then by list price, from highest to lowest.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> query =
from product in products.AsEnumerable()
orderby product.Field<string>("Name"),
product.Field<Decimal>("ListPrice") descending
select product;
Dim query = _
From product In products.AsEnumerable() _
Order By product.Field(Of String)("Name"), _
product.Field(Of Decimal)("ListPrice") Descending _
Select product
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Query Expression Syntax Examples: Element
Operators (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
The examples in this topic demonstrate how to use the First and ElementAt methods to get DataRow elements
from a DataSet using the query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
ElementAt
Example
This example uses the ElementAt method to retrieve the fifth address where PostalCode == "M4B 1V7".
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
var fifthAddress = (
from address in addresses.AsEnumerable()
where address.Field<string>("PostalCode") == "M4B 1V7"
select address.Field<string>("AddressLine1"))
.ElementAt(5);
Dim fifthAddress = ( _
From address In addresses.AsEnumerable() _
Where address.Field(Of String)("PostalCode") = "M4B 1V7" _
Select address.Field(Of String)("AddressLine1")).ElementAt(5)
First
Example
This example uses the First method to return the first contact whose first name is 'Brooke'.
DataRow query = (
from contact in contacts.AsEnumerable()
where (string)contact["FirstName"] == "Brooke"
select contact)
.First();
Dim query = ( _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Brooke" _
Select contact).First()
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Query Expression Syntax Examples: Aggregate
Operators (LINQ to DataSet)
4/8/2019 • 9 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Average, Count, Max, Min, and Sum methods to query a
DataSet and aggregate data using query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Average
Example
This example uses the Average method to find the average list price of the products of each style.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")) _
}
Example
This example uses Average to get the average total due for each contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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"))
};
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")) _
}
Example
This example uses Average to get the orders with the average TotalDue for each contact.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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)
};
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) _
}
Count
Example
This example uses Count to return a list of contact IDs and how many orders each has.
Dim query = _
From contact In contacts.AsEnumerable() _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.OrderCount = contact.GetChildRows("SalesOrderContact").Count() _
}
Example
This example groups products by color and uses Count to return the number of products in each color group.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
var query =
from product in products.AsEnumerable()
group product by product.Field<string>("Color") into g
select new { Color = g.Key, ProductCount = g.Count() };
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()}
Max
Example
This example uses the Max method to get the largest total due for each contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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"))
};
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
Example
This example uses the Max method to get the orders with the largest TotalDue for each contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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)
};
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) _
}
Min
Example
This example uses the Min method to get the smallest total due for each contact ID.
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"))
};
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")) _
}
Example
This example uses the Min method to get the orders with the smallest total due for each contact.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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)
};
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) _
}
Sum
Example
This example uses the Sum method to get the total due for each contact ID.
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);
}
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")) _
}
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Query Expression Syntax Examples: Join Operators
(LINQ to DataSet)
4/8/2019 • 4 minutes to read • Edit Online
Joining is an important operation in queries that target data sources that have no navigable relationships to each
other, such as relational database tables. A join of two data sources is the association of objects in one data source
with objects that share a common attribute in the other data source. For more information, see Standard Query
Operators Overview (C#) or Standard Query Operators Overview (Visual Basic).
The examples in this topic demonstrate how to use the GroupJoin and Join methods to query a DataSet using the
query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
GroupJoin
Example
This example performs a GroupJoin over the SalesOrderHeader and SalesOrderDetail tables to find the number of
orders per customer. A group join is the equivalent of a left outer join, which returns each element of the first (left)
data source, even if no correlated elements are in the other data source.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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()
};
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() _
}
Example
This example performs a GroupJoin over the Contact and SalesOrderHeader tables. A group join is the equivalent
of a left outer join, which returns each element of the first (left) data source, even if no correlated elements are in
the other data source.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
};
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") _
}
Join
Example
This example performs a join over the SalesOrderHeader and SalesOrderDetail tables to get online orders from
the month of August.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
};
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") _
}
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Examples (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
This section provides LINQ to DataSet programming examples in method-based query syntax that use the
standard query operators. The DataSet used in these examples is populated by using the FillDataSet method,
which is specified in Loading Data Into a DataSet. For more information, see Standard Query Operators Overview
(C#) or Standard Query Operators Overview (Visual Basic).
In This Section
Projection
The examples in this topic demonstrate how to use the Select and SelectMany methods to query a DataSet.
Partitioning
The examples in this topic demonstrate how to use the Skip and Take methods to query a DataSet and partition
the results.
Ordering
The examples in this topic demonstrate how to use the OrderBy, OrderByDescending, Reverse, and
ThenByDescending methods to query a DataSet and order the results.
Set Operators
The examples in this topic demonstrate how to use the Distinct, Except, Intersect, and Union operators to perform
value-based comparison operations on sets of data rows.
Conversion Operators
The examples in this topic demonstrate how to use the ToArray, ToDictionary, and ToList methods to immediately
execute a query expression.
Element Operators
The examples in this topic demonstrate how to use the First and ElementAt methods to get DataRow elements
from a DataSet.
Aggregate Operators
The examples in this topic demonstrate how to use the Average, Count, Max, Min, and Sum methods to query a
DataSet and aggregate data.
Join
The examples in this topic demonstrate how to use the GroupJoin and Join methods to query a DataSet.
See also
Query Expression Examples
DataSet-Specific Operator Examples
LINQ to DataSet Examples
Method-Based Query Syntax Examples: Projection
(LINQ to DataSet)
4/8/2019 • 3 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Select and SelectMany methods to query a DataSet using
the method-based query syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Select
Example
This example uses the Select method to project the Name , ProductNumber , and ListPrice properties to a sequence
of anonymous types. The ListPrice property is also renamed to Price in the resulting type.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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);
}
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
Example
This example uses the SelectMany method to select all orders where TotalDue is less than 500.00.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
}));
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") _
}))
Example
This example uses the SelectMany method to select all orders where the order was made on October 1, 2002 or
later.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
}));
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") _
}))
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Syntax Examples: Partitioning
(LINQ
4/8/2019 • 5 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Skip, SkipWhile, Take, and TakeWhile methods to query a
DataSet using the query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Skip
Example
This example uses the Skip method to get all but the first five contacts of the Contact table.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
Example
This example uses the Skip method to get all but the first two addresses in Seattle.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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);
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)
SkipWhile
Example
This example uses OrderBy and SkipWhile methods to return products from the Product table with a list price
greater than 300.00.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> skipWhilePriceLessThan300 =
products.AsEnumerable()
.OrderBy(listprice => listprice.Field<decimal>("ListPrice"))
.SkipWhile(product => product.Field<decimal>("ListPrice") < 300.00M);
Take
Example
This example uses the Take method to get only the first five contacts from the Contact table.
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)
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
Example
This example uses the Take method to get the first three addresses in Seattle.
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);
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)
TakeWhile
Example
This example uses OrderBy and TakeWhile to return products from the Product table with a list price less than
300.00.
IEnumerable<DataRow> takeWhileListPriceLessThan300 =
products.AsEnumerable()
.OrderBy(listprice => listprice.Field<decimal>("ListPrice"))
.TakeWhile(product => product.Field<decimal>("ListPrice") < 300.00M);
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Syntax Examples: Ordering
(LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
The examples in this topic demonstrate how to use the OrderBy, Reverse, and ThenBy methods to query a DataSet
and order the results using the method query syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
OrderBy
Example
This example uses the OrderBy method with a custom comparer to do a case-insensitive sort of last names.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> query =
contacts.AsEnumerable().OrderBy(contact => contact.Field<string>("LastName"),
new CaseInsensitiveComparer());
Reverse
Example
This example uses the Reverse method to create a list of orders where OrderDate is earlier than Feb 20, 2002.
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 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")
ThenBy
Example
This example uses OrderBy and ThenBy methods with a custom comparer to first sort by list price, and then
perform a case-insensitive descending sort of the product names.
IEnumerable<DataRow> query =
products.AsEnumerable().OrderBy(product => product.Field<Decimal>("ListPrice"))
.ThenBy(product => product.Field<string>("Name"),
new CaseInsensitiveComparer());
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Syntax Examples: Set
Operators (LINQ to DataSet)
4/8/2019 • 5 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Distinct, Except, Intersect, and Union operators to perform
value-based comparison operations on sets of data rows.Loading Data Into a DataSet See Comparing DataRows
for more information on DataRowComparer.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Distinct
Example
This example uses the Distinct method to remove duplicate elements in a sequence.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
// 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);
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)
' 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)
Console.WriteLine("Unique contacts:")
For Each uniqueContact In uniqueContacts
Console.WriteLine(uniqueContact.Field(Of Integer)("ContactID"))
Next
Except
Example
This example uses the Except method to return contacts that appear in the first table but not in the second.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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 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
Union
Example
This example uses the Union method to return unique contacts from either of the two tables.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Syntax Examples: Conversion
Operators (LINQ to DataSet)
4/8/2019 • 2 minutes to read • Edit Online
The examples in this topic demonstrate how to use the ToArray, ToDictionary, and ToList methods to immediately
execute a query expression.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
ToArray
Example
This example uses the ToArray method to immediately evaluate a sequence into an array.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> query =
from product in productsArray
orderby product.Field<Decimal>("ListPrice") descending
select product;
Dim query = _
From product In productsArray _
Select product _
Order By product.Field(Of Decimal)("ListPrice") Descending
ToDictionary
Example
This example uses the ToDictionary method to immediately evaluate a sequence and a related key expression into
a dictionary.
ToList
Example
This example uses the ToList method to immediately evaluate a sequence into a List<T>, where T is of type
DataRow.
IEnumerable<DataRow> query =
from product in productList
orderby product.Field<string>("Name")
select product;
Dim query = _
From product In productList _
Select product _
Order By product.Field(Of String)("Name")
The examples in this topic demonstrate how to use the First and ElementAt methods to get DataRow elements
from a DataSet using the query expression syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
ElementAt
Example
This example uses the ElementAt method to retrieve the fifth address where PostalCode == "M4B 1V7".
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
var fifthAddress = (
from address in addresses.AsEnumerable()
where address.Field<string>("PostalCode") == "M4B 1V7"
select address.Field<string>("AddressLine1"))
.ElementAt(5);
Dim fifthAddress = ( _
From address In addresses.AsEnumerable() _
Where address.Field(Of String)("PostalCode") = "M4B 1V7" _
Select address.Field(Of String)("AddressLine1")).ElementAt(5)
First
Example
This example uses the First method to return the first contact whose first name is 'Brooke'.
DataRow query = (
from contact in contacts.AsEnumerable()
where (string)contact["FirstName"] == "Brooke"
select contact)
.First();
Dim query = ( _
From contact In contacts.AsEnumerable() _
Where contact.Field(Of String)("FirstName") = "Brooke" _
Select contact).First()
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Syntax Examples: Aggregate
Operators (LINQ to DataSet)
4/8/2019 • 12 minutes to read • Edit Online
The examples in this topic demonstrate how to use the Aggregate, Average, Count, LongCount, Max, Min, and
Sum operators to query a DataSet and aggregate data using method query syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Aggregate
Example
This example uses the Aggregate method to get the first 5 contacts from the Contact table and build a comma-
delimited list of the last names.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
string nameList =
contacts.Take(5).Select(contact => contact.Field<string>("LastName")).Aggregate((workingList, next) =>
workingList + "," + next);
Console.WriteLine(nameList);
Console.WriteLine(nameList)
Average
Example
This example uses the Average method to find the average list price of the products.
Decimal averageListPrice =
products.Average(product => product.Field<Decimal>("ListPrice"));
Example
This example uses the Average method to find the average list price of the products of each style.
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")) _
}
Example
This example uses the Average method to find the average total due.
Example
This example uses the Average method to get the average total due for each contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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"))
};
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")) _
}
Example
This example uses the Average method to get the orders with the average TotalDue for each contact.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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)
};
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) _
}
Count
Example
This example uses the Count method to return the number of products in the Product table.
Example
This example uses the Count method to return a list of contact IDs and how many orders each has.
Dim query = _
From contact In contacts.AsEnumerable() _
Select New With _
{ _
.ContactID = contact.Field(Of Integer)("ContactID"), _
.OrderCount = contact.GetChildRows("SalesOrderContact").Count() _
}
Example
This example groups products by color and uses the Count method to return the number of products in each color
group.
var query =
from product in products.AsEnumerable()
group product by product.Field<string>("Color") into g
select new { Color = g.Key, ProductCount = g.Count() };
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()}
Max
Example
This example uses the Max method to get the largest total due.
Example
This example uses the Max method to get the largest total due for each contact ID.
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"))
};
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
Example
This example uses the Max method to get the orders with the largest TotalDue for each contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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)
};
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) _
}
Min
Example
This example uses the Min method to get the smallest total due.
Example
This example uses the Min method to get the smallest total due for each contact ID.
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"))
};
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")) _
}
Example
This example uses the Min method to get the orders with the smallest total due for each contact.
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)
};
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) _
}
Sum
Example
This example uses the Sum method to get the total number of order quantities in the SalesOrderDetail table.
Example
This example uses the Sum method to get the total due for each contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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);
}
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")) _
}
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Method-Based Query Syntax Examples: Join (LINQ
to DataSet)
4/8/2019 • 3 minutes to read • Edit Online
Joining is an important operation in queries that target data sources that have no navigable relationships to each
other, such as relational database tables. A join of two data sources is the association of objects in one data source
with objects that share a common attribute in the other data source. For more information, see Standard Query
Operators Overview (C#) or Standard Query Operators Overview (Visual Basic).
The examples in this topic demonstrate how to use the Join method to query a DataSet using the method query
syntax.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
Join
Example
This example performs a join over the Contact and SalesOrderHeader tables.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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")
});
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") _
})
Example
This example performs a join over the Contact and SalesOrderHeader tables, grouping the results by contact ID.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
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)
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Standard Query Operators Overview (C#)
Standard Query Operators Overview (Visual Basic)
Join Samples
Dataset Samples
DataSet-Specific Operator Examples (LINQ to
DataSet)
4/8/2019 • 2 minutes to read • Edit Online
The examples in this topic demonstrate how to use the CopyToDataTable method and the DataRowComparer
class.
The FillDataSet method used in these examples is specified in Loading Data Into a DataSet.
The examples in this topic use the Contact, Address, Product, SalesOrderHeader, and SalesOrderDetail tables in
the AdventureWorks sample database.
The examples in this topic use the following using / Imports statements:
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
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
For more information, see How to: Create a LINQ to DataSet Project In Visual Studio.
CopyToDataTable
Example
This example loads a DataTable with query results by using the CopyToDataTable method.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
IEnumerable<DataRow> query =
from contact in contacts1.AsEnumerable()
where contact.Field<string>("Title") == "Ms."
&& contact.Field<string>("FirstName") == "Carla"
select contact;
Dim query = _
From contact In contactTable.AsEnumerable() _
Where contact.Field(Of String)("Title") = "Ms." _
And contact.Field(Of String)("FirstName") = "Carla" _
Select contact
DataRowComparer
Example
This example compares two different data rows by using DataRowComparer.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
See also
Loading Data Into a DataSet
LINQ to DataSet Examples
Entity Data Model
4/8/2019 • 2 minutes to read • Edit Online
The Entity Data Model (EDM ) is a set of concepts that describe the structure of data, regardless of its stored
form. The EDM borrows from the Entity-Relationship Model described by Peter Chen in 1976, but it also builds
on the Entity-Relationship Model and extends its traditional uses.
The EDM addresses the challenges that arise from having data stored in many forms. For example, consider a
business that stores data in relational databases, text files, XML files, spreadsheets, and reports. This presents
significant challenges in data modeling, application design, and data access. When designing a data-oriented
application, the challenge is to write efficient and maintainable code without sacrificing efficient data access,
storage, and scalability. When data has a relational structure, data access, storage, and scalability are very
efficient, but writing efficient and maintainable code becomes more difficult. When data has an object structure,
the trade-offs are reversed: Writing efficient and maintainable code comes at the cost of efficient data access,
storage, and scalability. Even if the right balance between these trade-offs can be found, new challenges arise
when data is moved from one form to another. The Entity Data Model addresses these challenges by describing
the structure of data in terms of entities and relationships that are independent of any storage schema. This
makes the stored form of data irrelevant to application design and development. And, because entities and
relationships describe the structure of data as it is used in an application (not its stored form), they can evolve as
an application evolves.
A conceptual model is a specific representation of the structure of data as entities and relationships, and is
generally defined in a domain-specific language (DSL ) that implements the concepts of the EDM. Conceptual
schema definition language (CSDL ) is an example of such a domain-specific language. Entities and relationships
described in a conceptual model can be thought of as abstractions of objects and associations in an application.
This allows developers to focus on the conceptual model without concern for the storage schema, and allows
them to write code with efficiency and maintainability in mind. Meanwhile storage schema designers can focus
on the efficiency of data access, storage, and scalability.
In This Section
Topics in this section describe the concepts of the Entity Data Model. Any DSL that implements the EDM should
include the concepts described here. Note that the ADO.NET Entity Framework uses CSDL to define conceptual
models. For more information, see CSDL Specification.
Entity Data Model Key Concepts
Entity Data Model: Namespaces
Entity Data Model: Primitive Data Types
Entity Data Model: Inheritance
association end
association end multiplicity
association set
association set end
association type
complex type
entity container
entity key
entity set
entity type
facet
foreign key property
model-declared function
model-defined function
navigation property
property
referential integrity constraint
See also
ADO.NET Entity Data Model Tools
.edmx File Overview
CSDL Specification
Entity Data Model Key Concepts
4/8/2019 • 3 minutes to read • Edit Online
The Entity Data Model (EDM ) uses three key concepts to describe the structure of data: entity type, association
type, and property. These are the most important concepts in describing the structure of data in any
implementation of the EDM.
Entity Type
The entity type is the fundamental building block for describing the structure of data with the Entity Data Model.
In a conceptual model, entity types are constructed from properties and describe the structure of top-level
concepts, such as a customers and orders in a business application. In the same way that a class definition in a
computer program is a template for instances of the class, an entity type is a template for entities. An entity
represents a specific object (such as a specific customer or order). Each entity must have a unique entity key
within an entity set. An entity set is a collection of instances of a specific entity type. Entity sets (and association
sets) are logically grouped in an entity container.
Inheritance is supported with entity types: that is, one entity type can be derived from another. For more
information, see Entity Data Model: Inheritance.
Association Type
An association type (also called an association) is the fundamental building block for describing relationships in
the Entity Data Model. In a conceptual model, an association represents a relationship between two entity types
(such as Customer and Order). Every association has two association ends that specify the entity types involved
in the association. Each association end also specifies an association end multiplicity that indicates the number
of entities that can be at that end of the association. An association end multiplicity can have a value of one (1),
zero or one (0..1), or many (*). Entities at one end of an association can be accessed through navigation
properties, or through foreign keys if they are exposed on an entity type. For more information, see foreign key
property.
In an application, an instance of an association represents a specific association (such as an association between
an instance of Customer and instances of Order). Association instances are logically grouped in an association
set. Association sets (and entity sets) are logically grouped in an entity container.
Property
Entity types contain properties that define their structure and characteristics. For example, a Customer entity
type may have properties such as CustomerId, Name, and Address.
Properties in a conceptual model are analogous to properties defined on a class in a computer program. In the
same way that properties on a class define the shape of the class and carry information about objects,
properties in a conceptual model define the shape of an entity type and carry information about entity type
instances.
A property can contain primitive data (such as a string, an integer, or a Boolean value), or structured data (such
as a complex type). For more information, see Entity Data Model: Primitive Data Types.
This representation, however, has some shortcomings when it comes to conveying some details about the
model. For example, property type and entity set information are not conveyed in the diagram. The richness of a
conceptual model can be conveyed more clearly with a domain-specific language (DSL ). The ADO.NET Entity
Framework uses an XML -based DSL called conceptual schema definition language (CSDL ) to define conceptual
models. The following is the CSDL definition of the conceptual model in the diagram above:
<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>
See also
Entity Data Model
Entity Data Model: Namespaces
4/8/2019 • 2 minutes to read • Edit Online
A namespace in the Entity Data Model (EDM ) is an abstract container for entity types, complex types, and
associations. Namespaces in the EDM are similar to namespaces in a programming language: they provide context
for the objects that they contain and they provide a way to disambiguate objects that have the same name (but are
contained in different namespaces).
Example
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL code uses a namespace to identify a type that
is defined in a different conceptual model. The example defines an entity type ( Publisher ) that has a complex type
property ( Address ) that is imported from the ExtendedBooksModel namespace. Note that the Using element
indicates that a namespace has been imported. Also note that the type of the Address property is defined by using
its fully qualified name ( ExtendedBooksModel.Address ), indicating that this type is defined in the ExtendedBooksModel
namespace.
<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">
<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>
See also
Entity Data Model Key Concepts
Entity Data Model
Entity Data Model: Primitive Data Types
4/8/2019 • 2 minutes to read • Edit Online
The Entity Data Model (EDM ) supports a set of abstract primitive data types (such as String, Boolean, Int32, and
so on) that are used to define properties in a conceptual model. These primitive data types are proxies for actual
primitive data types that are supported in the storage or hosting environment, such as a SQL Server database or
the common language runtime (CLR ). The EDM does not define the semantics of operations or conversions over
primitive data types; these semantics are defined by the storage or hosting environment. Typically, primitive data
types in the EDM are mapped to corresponding primitive data types in the storage or hosting environment. For
information about how the Entity Framework maps primitive types in the EDM to SQL Server data types, see
SqlClient for Entity FrameworkTypes.
NOTE
The EDM does not support collections of primitive data types.
For information about structured data types in the EDM, see entity type and complex type.
See also
Entity Data Model Key Concepts
Entity Data Model
Entity Data Model: Inheritance
4/8/2019 • 2 minutes to read • Edit Online
The Entity Data Model (EDM ) supports inheritance for entity types. Inheritance in the EDM is similar to
inheritance for classes in object-oriented programming languages. Like with classes in object-oriented languages,
in a conceptual model you can define an entity type (a derived type) that inherits from another entity type (the
base type). However, unlike classes in object-oriented programming, in a conceptual model the derived type
always inherits all the properties and navigation properties of the base type. You cannot override inherited
properties in a derived type.
In a conceptual model you can build inheritance hierarchies in which a derived type inherits from another derived
type. The type at the top of the hierarchy (the one type in the hierarchy that is not a derived type) is called the root
type. In an inheritance hierarchy, the entity key must be defined on the root type.
You cannot build inheritance hierarchies in which a derived type inherits from more than one type. For example, in
a conceptual model with a Book entity type, you could define derived types FictionBook and NonFictionBook that
each inherit from Book . However, you could not then define a type that inherits from both the FictionBook and
NonFictionBook types.
Example
The following diagram shows a conceptual model with four entity types: Book , FictionBook , Publisher , and
Author . The FictionBook entity type is a derived type, inheriting from the Book entity type. The FictionBook
type inherits the ISBN (Key) , Title , and Revision properties, and defines an additional property called Genre .
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines an entity type, FictionBook , that
inherits from the Book type (as in the diagram above):
See also
Entity Data Model Key Concepts
Entity Data Model
association end
4/28/2019 • 2 minutes to read • Edit Online
An association end identifies the entity type on one end of an association and the number of entity type instances
that can exist at that end of an association. Association ends are defined as part of an association; an association
must have exactly two association ends. Navigation properties allow for navigation from one association end to
the other.
An association end definition contains the following information:
One of the entity types involved in the association. (Required)
NOTE
For a given association, the entity type specified for each association end can be the same. This creates a self-
association.
An association end multiplicity that indicates the number of entity type instances that can be at one end of
the association. An association end multiplicity can have a value of one (1), zero or one (0..1), or many (*).
A name for the association end. (Optional)
Information about operations that are performed on the association end, such as cascade on delete.
(Optional)
Example
The diagram below shows a conceptual model with two associations: PublishedBy and WrittenBy . The
association ends for the PublishedBy association are the Book and Publisher entity types. The multiplicity of the
Publisher end is one (1 ) and the multiplicity of the Book end is many (* ), indicating that a publisher publishes
many books and a book is published by one publisher.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The CSDL below defines the PublishedBy association shown in
the diagram above. Note that the type, name, and multiplicity of each association end are specified by XML
attributes (the Type , Role , and Multiplicity attributes, respectively). Optional information about operations
performed on an end is specified in an XML element (the OnDelete element). In this case, if a publisher is deleted,
so are all associated books.
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
</Association>
See also
Entity Data Model Key Concepts
Entity Data Model
association end multiplicity
4/28/2019 • 2 minutes to read • Edit Online
Association end multiplicity defines the number of entity type instances that can be at one end of an association.
An association end multiplicity can have one of the following values:
one (1): Indicates that exactly one entity type instance exists at the association end.
zero or one (0..1): Indicates that zero or one entity type instances exist at the association end.
many (*): Indicates that zero, one, or more entity type instances exist at the association end.
An association is often characterized by its association end multiplicities. For example, if the ends of an association
have multiplicities one (1) and many (*), the association is called a one-to-many association. In the example below,
the PublishedBy association is a one-to-many association (a publisher publishes many books and a book is
published by one publisher). The WrittenBy association is a many-to-many association (a book can have multiple
authors and an author can write multiple books).
Example
The diagram below shows a conceptual model with two associations: PublishedBy and WrittenBy . The
association ends for the PublishedBy association are the Book and Publisher entity types. The multiplicity of the
Publisher end is one (1 ) and the multiplicity of the Book end is many (* ).
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines the PublishedBy association shown in
the diagram above:
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
</Association>
See also
Entity Data Model Key Concepts
Entity Data Model
association set
4/28/2019 • 2 minutes to read • Edit Online
An association set is a logical container for association instances of the same type. An association set is not a data
modeling construct; that is, it does not describe the structure of data or relationships. Instead, an association set
provides a construct for a hosting or storage environment (such as the common language runtime or a SQL
Server database) to group association instances so that they can be mapped to a data store.
An association set is defined within an entity container, which is a logical grouping of entity sets and association
sets.
A definition for an association set contains the following information:
The association set name. (Required)
The association of which it will contain instances. (Required)
Two association set ends.
Example
The diagram below shows a conceptual model with two associations: PublishedBy , and WrittenBy . Although
information about association sets is not conveyed in the diagram, the next diagram shows an example of
association sets and entity sets based on this model.
The following example shows an association set ( PublishedBy ) and two entity sets ( Books and Publishers )
based on the conceptual model shown above. Bi in the Books entity set represents an instance of the Book entity
type at run time. Similarly, Pj represents a Publisher instance in the Publishers entity set. BiPj represents an
instance of the PublishedBy association in the PublishedBy association set.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines an entity container with one
association set for each association in the diagram above. Note that the name and association for each association
set are defined using XML attributes.
<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>
It is possible to define multiple association sets per association, as long as no two association sets share an
association set end. The following CSDL defines an entity container with two association sets for the WrittenBy
association. Notice that multiple entity sets have been defined for the Book and Author entity types and that no
association set shares an association set end.
See also
Entity Data Model Key Concepts
Entity Data Model
foreign key property
association set end
4/28/2019 • 2 minutes to read • Edit Online
An association set end identifies the entity type and the entity set at the end of an association set. Association set
ends are defined as part of an association set; an association set must have exactly two association set ends.
An association set end definition contains the following information:
One of the entity types involved in the association set. (Required)
The entity set for the entity type involved in the association set. (Required)
Example
The diagram below shows a conceptual model with two associations: WrittenBy and PublishedBy .
The following diagram shows an association set ( PublishedBy ) and two entity sets ( Books and Publishers ) based
on the conceptual model shown above. The association set ends are the Books and Publishers entity sets. Bi in
the Books entity set represents an instance of the Book entity type at run time. Similarly, Pj represents a
Publisher instance in the Publishers entity set. BiPj represents an instance of the PublishedBy association in the
PublishedBy association set.
The ADO.NET Entity Framework uses a DSL called conceptual schema definition language (CSDL ) to define
conceptual models. The following CSDL defines an entity container with one association set for each association
in the diagram above. Note that association set ends are defined as part of each association set definition.
<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>
See also
Entity Data Model Key Concepts
Entity Data Model
association type
4/28/2019 • 2 minutes to read • Edit Online
An association type (also called an association) is the fundamental building block for describing relationships in
the Entity Data Model (EDM ). In a conceptual model, an association represents a relationship between two entity
types (such as Customer and Order ). In an application, an instance of an association represents a specific
association (such as an association between an instance of Customer and an instance of Order ). Association
instances are logically grouped in an association set.
An association definition contains the following information:
A unique name. (Required)
Two association ends, one for each entity type in the relationship. (Required)
NOTE
An association cannot represent a relationship among more than two entity types. An association can, however,
define a self-relationship by specifying the same entity type for each of its association ends.
Example
The diagram below shows a conceptual model with two associations: PublishedBy and WrittenBy . The
association ends for the PublishedBy association are the Book and Publisher entity types. The multiplicity of
the Publisher end is one (1) and the multiplicity of the Book end is many (*), indicating that a publisher
publishes many books and a book is published by one publisher.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines the PublishedBy association shown
in the diagram above:
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" />
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
</Association>
See also
Entity Data Model Key Concepts
Entity Data Model
complex type
4/28/2019 • 2 minutes to read • Edit Online
A complex type is a template for defining rich, structured properties on entity types or on other complex types.
Each template contains the following:
A unique name. (Required)
NOTE
The name of a complex type cannot be the same as an entity type name within the same namespace.
NOTE
A property of a complex type can be another complex type.
A complex type is similar to an entity type in that a complex type can carry a data payload in the form of primitive
type properties or other complex types. However, there are some key differences between complex types and
entity types:
Complex types do not have identities and therefore cannot exist independently. Complex types can only
exist as properties on entity types or other complex types.
Complex types cannot participate in associations. Neither end of an association can be a complex type, and
therefore navigation properties cannot be defined on complex types.
Example
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines a complex type, Address, with the
primitive type properties StreetAddress , City , StateOrProvince , Country , and PostalCode .
To define the complex type Address (above) as a property on an entity type, you must declare the property type in
the entity type definition. The following CSDL declares the Address property as a complex type on an entity type
(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>
See also
Entity Data Model Key Concepts
Entity Data Model
entity container
4/28/2019 • 2 minutes to read • Edit Online
An entity container is a logical grouping of entity sets, association sets, and function imports.
The following must be true of an entity container defined in a conceptual model:
At least one entity container must be defined in each conceptual model.
The entity container must have a unique name within each conceptual model.
An entity container can define entity sets or association sets that use entity types or associations defined in one or
more namespaces. For more information, see Entity Data Model: Namespaces.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author . See the
next example for more information.
Although the diagram does not convey entity container information, the conceptual model must define an entity
container. The ADO.NET Entity Framework uses a DSL called conceptual schema definition language (CSDL ) to
define conceptual models. The following CSDL defines an entity container for the conceptual model shown in the
diagram above. Note that the entity container name is defined in an XML attribute.
See also
Entity Data Model Key Concepts
Entity Data Model
entity key
4/28/2019 • 2 minutes to read • Edit Online
An entity key is a property or a set of properties of an entity type that are used to determine identity. The
properties that make up an entity key are chosen at design time. The values of entity key properties must
uniquely identify an entity type instance within an entity set at run time. The properties that make up an entity key
should be chosen to guarantee uniqueness of instances in an entity set.
The following are the requirements for a set of properties to be an entity key:
No two entity keys within an entity set can be identical. That is, for any two entities within an entity set, the
values for all of the properties that constitute a key cannot be the same. However, some (but not all) of the
values that make up an entity key can be the same.
An entity key must consist of a set of non-nullable, immutable, primitive type properties.
The properties that make up an entity key for a given entity type cannot change. You cannot allow more
than one possible entity key for a given entity type; surrogate keys are not supported.
When an entity is involved in an inheritance hierarchy, the root entity must contain all the properties that
make up the entity key, and the entity key must be defined on the root entity type. For more information,
see Entity Data Model: Inheritance.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author . The
properties of each entity type that make up its entity key are denoted with "(Key)". Note that the Author entity
type has an entity key that consists of two properties, Name and Address .
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The CSDL below defines the Book entity type shown in the
diagram above. Note that the entity key is defined by referencing the ISBN property of the entity type.
<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>
The ISBN property is a good choice for the entity key because an International Standard Book Number (ISBN )
uniquely identifies a book.
The CSDL below defines the Author entity type shown in the diagram above. Note that the entity key consists of
two properties, Name and 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>
Using Name and Address for the entity key is a reasonable choice, because two authors of the same name are
unlikely to live at the same address. However, this choice for an entity key does not absolutely guarantee unique
entity keys in an entity set. Adding a property, such as AuthorId , that could be used to uniquely identify an author
would be recommended in this case.
See also
Entity Data Model Key Concepts
Entity Data Model
entity set
4/28/2019 • 2 minutes to read • Edit Online
An entity set is a logical container for instances of an entity type and instances of any type derived from that
entity type. (For information about derived types, see Entity Data Model: Inheritance.) The relationship between
an entity type and an entity set is analogous to the relationship between a row and a table in a relational
database: Like a row, an entity type describes data structure, and, like a table, an entity set contains instances of a
given structure. An entity set is not a data modeling construct; it does not describe the structure of data. Instead,
an entity set provides a construct for a hosting or storage environment (such as the common language runtime
or a SQL Server database) to group entity type instances so that they can be mapped to a data store.
An entity set is defined within an entity container, which is a logical grouping of entity sets and association sets.
For an entity type instance to exist in an entity set, the following must be true:
The type of the instance is either the same as the entity type on which the entity set is based, or the type of
the instance is a subtype of the entity type.
The entity key for the instance is unique within the entity set.
The instance does not exist in any other entity set.
NOTE
Multiple entity sets can be defined using the same entity type, but an instance of a given entity type can only exist
in one entity set.
You do not have to define an entity set for each entity type in a conceptual model.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author .
The following diagram shows two entity sets ( Books and Publishers ) and an association set ( PublishedBy )
based on the conceptual model shown above. Bi in the Books entity set represents an instance of the Book
entity type at run time. Similarly, Pj represent a Publisher instance in the Publishers entity set. BiPj represents
an instance of the PublishedBy association in the PublishedBy association set.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines an entity container with one entity
set for each entity type in the conceptual model shown above. Note that the name and entity type for each entity
set are defined using XML attributes.
It is possible to define multiple entity sets per type (MEST). The following CSDL defines an entity container with
two entity sets for the Book entity type:
See also
Entity Data Model Key Concepts
Entity Data Model
entity type
4/28/2019 • 2 minutes to read • Edit Online
The entity type is the fundamental building block for describing the structure of data with the Entity Data
Model (EDM ). In a conceptual model, an entity type represents the structure of top-level concepts, such as
customers or orders. An entity type is a template for entity type instances. Each template contains the following
information:
A unique name. (Required.)
An entity key defined by one or more properties. (Required.)
Data in the form of properties. (Optional.)
Navigation properties that allow for navigation from one end of an association to the other end.
(Optional)
In an application, an instance of an entity type represents a specific object (such as a specific customer or
order). Each instance of an entity type must have a unique entity key within an entity set.
Two entity type instances are considered equal only if they are of the same type and the values of their entity
keys are the same.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author :
Note that the properties of each entity type that make up its entity key are denoted with "(Key)".
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines the Book entity type shown in the
diagram above:
<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>
See also
Entity Data Model Key Concepts
Entity Data Model
facet
facet
4/8/2019 • 2 minutes to read • Edit Online
A facet is used to add detail to a primitive type property definition. A property definition contains information
about the property type, but often more detail is necessary. For example, an entity type in a conceptual model
might have a property of type String whose value cannot be set to null. Facets allow you to specify this level of
detail.
The table below describes the facets that are supported in the EDM.
NOTE
The exact values and behaviors of facets are determined by the run-time environment that uses an EDM implementation.
ConcurrencyMode Indicates that the value of the property All primitive type properties
should be used for optimistic
concurrency checks.
Default Specifies the default value of the All primitive type properties
property if no value is supplied upon
instantiation.
Nullable Specifies whether the property can All primitive type properties
have a null value.
<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>
See also
Entity Data Model Key Concepts
Entity Data Model
foreign key property
4/8/2019 • 2 minutes to read • Edit Online
A foreign key property in the Entity Data Model (EDM ) is a primitive type property (or a set of primitive type
properties) on an entity type that contains the entity key of another entity type.
A foreign key property is analogous to a foreign key column in a relational database. In the same way that foreign
key columns are used in a relational database to create relationships between rows in tables, foreign key
properties in a conceptual model are used to establish associations between entity types. A referential integrity
constraint is used to define an association between two entity types when one of the types has a foreign key
property.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author . The Book
entity type has a property, PublisherId , that references the entity key of the Publisher entity type when you
define a referential integrity constraint on the PublishedBy association.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL uses the foreign key property PublisherId to
define a referential integrity constraint on the PublishedBy association shown in the conceptual model shown
above.
<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>
See also
Entity Data Model Key Concepts
Entity Data Model
model-declared function
4/28/2019 • 2 minutes to read • Edit Online
A model-declared function is a function that is declared in a conceptual model, but is not defined in that
conceptual model. The function might be defined in the hosting or storage environment. For example, a model-
declared function might be mapped to a function that is defined in a database, thus exposing server-side
functionality in the conceptual model.
The declaration of a model-declared function contains the following information:
The name of the function. (Required)
The type of the return value. (Optional)
NOTE
If no return value is specified, the return type is void.
Example
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. In CSDL, one implementation of a model-declared function is a
function import (using the FunctionImport element). The following CSDL defines an entity container with a
function import definition. Note that the return type for the function is void since no return type is specified.
<FunctionImport Name="UpdatePublisher">
<Parameter Name="PublisherId" Mode="In" Type="Int32" />
<Parameter Name="PublisherName" Mode="In" Type="String" />
</FunctionImport>
See also
Entity Data Model Key Concepts
Entity Data Model
model-defined function
4/28/2019 • 2 minutes to read • Edit Online
A model-defined function is a function that is defined in a conceptual model. The body of a model-defined function
is expressed in Entity SQL, which allows for the function to be expressed independently of rules or languages
supported in the data source.
A definition for a model-defined function contains the following information:
A function name. (Required)
The type of the return value. (Optional)
NOTE
If no return type is specified, the return value is void.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author .
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines a function in the conceptual model that
returns the numbers of years since an instance of a Book (in the diagram above) was published.
See also
Entity Data Model Key Concepts
Entity Data Model
Entity Data Model: Primitive Data Types
Navigation property
4/2/2019 • 2 minutes to read • Edit Online
A navigation property is an optional property on an entity type that allows for navigation from one end of an
association to the other end. Unlike other properties, navigation properties do not carry data.
A navigation property definition includes the following:
A name. (Required)
The association that it navigates. (Required)
The ends of the association that it navigates. (Required)
Note that navigation properties are optional on both entity types at the ends of an association. If you define a
navigation property on one entity type at the end of an association, you do not have to define a navigation
property on the entity type at the other end of the association.
The data type of a navigation property is determined by the multiplicity of its remote association end. For
example, suppose a navigation property, OrdersNavProp , exists on a Customer entity type and navigates a one-to-
many association between Customer and Order . Because the remote association end for the navigation property
has multiplicity of many (*), its data type is a collection (of Order ). Similarly, if a navigation property,
CustomerNavProp , exists on the Order entity type, its data type would be Customer , because the multiplicity of the
remote end is one (1).
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author . Navigation
properties, Publisher and Authors , are defined on the Book entity type. Navigation property Books is defined
on both the Publisher entity type and the Author entity type.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines the Book entity type shown in the
diagram above:
<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>
Note that XML attributes are used to communicate the information necessary to define a navigation property:
The attribute Name contains the name of the property, Relationship contains the name of the association it
navigates, and FromRole and ToRole contain the ends of the association.
See also
Entity Data Model Key Concepts
Entity Data Model
Relationships, navigation properties and foreign keys
property
4/28/2019 • 2 minutes to read • Edit Online
Properties are the fundamental building blocks of entity types and complex types. Properties define the shape
and characteristics of data that an entity type instance or complex type instance will contain. Properties in a
conceptual model are analogous to properties defined on a class. In the same way that properties on a class
define the shape of the class and carry information about objects, properties in a conceptual model define the
shape of an entity type and carry information about entity type instances.
NOTE
Properties, as described in this topic, are different from navigation properties. For more information, see navigation
properties.
NOTE
A complex type can, itself, have properties that are complex types.
Example
The diagram below shows a conceptual model with three entity types: Book , Publisher , and Author . Each
entity type has several properties, although type information for each property is not conveyed in the diagram.
Properties that are entity keys are denoted with (Key).
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines the Book entity type (as shown in
the diagram above) and indicates the type and name of each property by using XML attributes. An optional
facet, Nullable , is also defined by using an XML attribute.
<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>
It is possible that one of the properties shown in the diagram is a complex type property. For example, the
Address property on the Publisher entity type could be a complex type property composed of several scalar
properties, such as StreetAddress , City , StateOrProvince , Country , and PostalCode . The CSDL
representation of such a complex type would be as follows:
See also
Entity Data Model Key Concepts
Entity Data Model
referential integrity constraint
4/28/2019 • 2 minutes to read • Edit Online
A referential integrity constraint in the Entity Data Model (EDM ) is similar to a referential integrity constraint in a
relational database. In the same way that a column (or columns) from a database table can reference the primary
key of another table, a property (or properties) of an entity type can reference the entity key of another entity type.
The entity type that is referenced is called the principal end of the constraint. The entity type that references the
principal end is called the dependent end of the constraint.
A referential integrity constraint is defined as part of an association between two entity types. The definition for a
referential integrity constraint specifies the following information:
The principal end of the constraint. (An entity type whose entity key is referenced by the dependent end.)
The entity key of the principal end.
The dependent end of the constraint. (An entity type that has a property or properties that reference the
entity key of the principal end.)
The referencing property or properties of the dependent end.
The purpose of referential integrity constraints in the EDM is to ensure that valid associations always exist. For
more information, see foreign key property.
Example
The diagram below shows a conceptual model with two associations: WrittenBy and PublishedBy . The Book
entity type has a property, PublisherId , that references the entity key of the Publisher entity type when you
define a referential integrity constraint on the PublishedBy association.
The ADO.NET Entity Framework uses a domain-specific language (DSL ) called conceptual schema definition
language (CSDL ) to define conceptual models. The following CSDL defines a referential integrity constraint on the
PublishedBy association shown in the conceptual model above.
<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>
See also
Entity Data Model Key Concepts
Entity Data Model
Oracle and ADO.NET
5/18/2019 • 2 minutes to read • Edit Online
NOTE
The types in System.Data.OracleClient are deprecated. The types remain supported in the current version of.NET Framework
but will be removed in a future release. Microsoft recommends that you use a third-party Oracle provider.
This section describes features and behaviors that are specific to the .NET Framework Data Provider for Oracle.
The .NET Framework Data Provider for Oracle provides access to an Oracle database using the Oracle Call
Interface (OCI) as provided by Oracle Client software. The functionality of the data provider is designed to be
similar to that of the .NET Framework data providers for SQL Server, OLE DB, and ODBC.
To use the .NET Framework Data Provider for Oracle, an application must reference the System.Data.OracleClient
namespace as follows:
Imports System.Data.OracleClient
using System.Data.OracleClient;
You also must include a reference to the DLL when you compile your code. For example, if you are compiling a
C# program, your command line should include:
csc /r:System.Data.OracleClient.dll
In This Section
System Requirements
Describes requirements for using the .NET Framework Data Provider for Oracle, and describes a number of
issues to be aware when using it.
Oracle BFILEs
Describes the OracleBFile class, which is used to work with the Oracle BFILE data type.
Oracle LOBs
Describes the OracleLob class, which is used to work with Oracle LOB data types.
Oracle REF CURSORs
Describes support for the Oracle REF CURSOR data type.
OracleTypes
Describes structures you can use to work with Oracle data types, including OracleNumber and OracleString.
Oracle Sequences
Describes support for retrieving the server-generated key Oracle Sequence values.
Oracle Data Type Mappings
Lists Oracle data types and their mappings to the OracleDataReader.
Oracle Distributed Transactions
Describes how the OracleConnection object automatically enlists in an existing distributed transaction if it
determines that a transaction is active.
Related Sections
Securing ADO.NET Applications
Describes secure coding practices when using ADO.NET.
DataSets, DataTables, and DataViews
Describes how to create and use DataSets , typed DataSets , DataTables , and DataViews .
Retrieving and Modifying Data in ADO.NET
Describes how to work with data in ADO.NET.
SQL Server and ADO.NET
Describes how to work with features and functionality that are specific to SQL Server.
DbProviderFactories
Describes generic classes that allow you to write provider-independent code in ADO.NET.
See also
ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
System Requirements for the .NET Framework Data
Provider for Oracle
4/8/2019 • 2 minutes to read • Edit Online
The .NET Framework Data Provider for Oracle requires Microsoft Data Access Components (MDAC ) version 2.6
or later. MDAC 2.8 SP1 is recommended.
You must also have Oracle 8i Release 3 (8.1.7) Client or later installed.
Oracle Client software prior to version Oracle 9i cannot access UTF16 databases because UTF16 is a new feature
in Oracle 9i. To use this feature, you must upgrade your client software to Oracle 9i or later.
Working with the Data Provider for Oracle and Unicode Data
Following is a list of Unicode-related issues that you should consider when working with the .NET Framework
Data Provider for Oracle and Oracle client libraries. For more information, see your Oracle documentation.
Setting the Unicode Value in a Connection String Attribute
When working with Oracle, you can use the connection string attribute
Unicode=True
to initialize the Oracle client libraries in UTF -16 mode. This causes the Oracle client libraries to accept UTF -16
(which is very similar to UCS -2) instead of multi-byte strings. This allows the Data Provider for Oracle to always
work with any Oracle code page without additional translation work. This configuration only works if you are using
Oracle 9i clients to communicate with an Oracle 9i database with the alternate character set of AL16UTF16. When
an Oracle 9i client communicates with an Oracle 9i server, additional resources are required to convert the
Unicode CommandText values to the appropriate multi-byte character set that the Oracle9i server uses. This can
be avoided when you know that you have the safe configuration by adding Unicode=True to your connection
string.
Mixing Versions of Oracle Client and Oracle Server
Oracle 8i clients cannot access NCHAR, NVARCHAR2, or NCLOB data in Oracle 9i databases when the server's
national character set is specified as AL16UTF16 (the default setting for Oracle 9i). Because support for the UTF -
16 character set was not introduced until Oracle 9i, Oracle 8i clients cannot read it.
Working with UTF -8 Data
To set the alternate character set, set the Registry Key
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMEID\NLS_LANG to UTF8. See the Oracle Installation
notes on your platform for more information. The default setting is the primary character set of the language from
which you are installing the Oracle Client software. Not setting the language to match the national language
character set of the database to which you are connecting will cause parameter and column bindings to send or
receive data in your primary database character set, not the national character set.
OracleLob Can Only Update Full Characters.
For usability reasons, the OracleLob object inherits from the .NET Framework Stream class, and provides
ReadByte and WriteByte methods. It also implements methods, such as CopyTo and Erase, that work on
sections of Oracle LOB objects. In contrast, Oracle client software provides a number of APIs to work with
character LOBs (CLOB and NCLOB ). However, these APIs work on full characters only. Because of this difference,
the Data Provider for Oracle implements support for Read and ReadByte to work with UTF -16 data in a byte-
wise manner. However, the other methods of the OracleLob object only allow full-character operations.
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle BFILEs
4/28/2019 • 2 minutes to read • Edit Online
The .NET Framework Data Provider for Oracle includes the OracleBFile class, which is used to work with the
Oracle BFile data type.
The Oracle BFILE data type is an Oracle LOB data type that contains a reference to binary data with a maximum
size of 4 gigabytes. An Oracle BFILE differs from other Oracle LOB data types in that its data is stored in a
physical file in the operating system instead of on the server. Note that the BFILE data type provides read-only
access to data.
Other characteristics of a BFILE data type that distinguish it from a LOB data type are that it:
Contains unstructured data.
Supports server-side chunking.
Uses reference copy semantics. For example, if you perform a copy operation on a BFILE, only the BFILE
locator (which is a reference to the file) is copied. The data in the file is not copied.
The BFILE data type should be used for referencing LOBs that are large in size, and therefore, not practical to store
in the database. More client, server, and communication overhead is involved when using a BFILE data type
compared with the LOB data type. It is more efficient to access a BFILE if you only need to obtain a small amount
of data. It is more efficient to access database-resident LOBs if you need to obtain the entire object.
Each non-NULL OracleBFile object is associated with two entities that define the location of the underlying
physical file:
1. An Oracle DIRECTORY object, which is a database alias for a directory in the file system, and
2. The file name of the underlying physical file, which is located in the directory associated with the
DIRECTORY object.
Example
The following C# example demonstrates how you can create a BFILE in an Oracle table and then retrieve it in the
form of an OracleBFile object. The example demonstrates using the OracleDataReader object and the
OracleBFile Seek and Read methods. Note that in order to use this sample, you must first create a directory
named "c:\\bfiles" and file named "MyFile.jpg" on the Oracle server.
using System;
using System.IO;
using System.Data;
using System.Data.OracleClient;
connection.Close();
}
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle LOBs
4/8/2019 • 5 minutes to read • Edit Online
The .NET Framework Data Provider for Oracle includes the OracleLob class, which is used to work with Oracle
LOB data types.
An OracleLob may be one of these OracleType data types:
Blob An Oracle BLOB data type that contains binary data with a
maximum size of 4 gigabytes. This maps to an Array of type
Byte.
Clob An Oracle CLOB data type that contains character data, based
on the default character set on the server, with a maximum
size of 4 gigabytes. This maps to String.
An OracleLob differs from an OracleBFile in that the data is stored on the server instead of in a physical file in the
operating system. It can also be a read-write object, unlike an OracleBFile, which is always read-only.
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();
//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();
// WriteLobExample
public static void WriteLobExample(OracleCommand cmd)
{
//Note: Updating LOB data requires a transaction.
cmd.Transaction = cmd.Connection.BeginTransaction();
// Obtain a LOB.
OracleLob blob = reader.GetOracleLob(1/*0:based ordinal*/);
// 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;";
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();
}
}
OracleTransaction tx = conn.BeginTransaction();
tx.Commit();
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle REF CURSORs
4/28/2019 • 2 minutes to read • Edit Online
The .NET Framework Data Provider for Oracle supports the Oracle REF CURSOR data type. When using the
data provider to work with Oracle REF CURSORs, you should consider the following behaviors.
NOTE
Some behaviors differ from those of the Microsoft OLE DB Provider for Oracle (MSDAORA).
For performance reasons, the Data Provider for Oracle does not automatically bind REF CURSOR data
types, as MSDAORA does, unless you explicitly specify them.
The data provider does not support any ODBC escape sequences, including the {resultset} escape used to
specify REF CURSOR parameters.
To execute a stored procedure that returns REF CURSORs, you must define the parameters in the
OracleParameterCollection with an OracleType of Cursor and a Direction of Output. The data provider
supports binding REF CURSORs as output parameters only. The provider does not support REF
CURSORs as input parameters.
Obtaining an OracleDataReader from the parameter value is not supported. The values are of type DBNull
after command execution.
The only CommandBehavior enumeration value that works with REF CURSORs (for example, when
calling ExecuteReader) is CloseConnection; all others are ignored.
The order of REF CURSORs in the OracleDataReader depends on the order of the parameters in the
OracleParameterCollection. The ParameterName property is ignored.
The PL/SQL TABLE data type is not supported. However, REF CURSORs are more efficient. If you must
use a TABLE data type, use the OLE DB .NET Data Provider with MSDAORA.
In This Section
REF CURSOR Examples
Contains three examples that demonstrate using REF CURSORs.
REF CURSOR Parameters in an OracleDataReader
Demonstrates how to execute a PL/SQL stored procedure that returns a REF CURSOR parameter, and reads the
value as an OracleDataReader.
Retrieving Data from Multiple REF CURSORs Using an OracleDataReader
Demonstrates how to execute a PL/SQL stored procedure that returns two REF CURSOR parameters, and reads
the values using an OracleDataReader.
Filling a DataSet Using One or More REF CURSORs
Demonstrates how to execute a PL/SQL stored procedure that returns two REF CURSOR parameters, and fills a
DataSet with the rows that are returned.
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
REF CURSOR Examples
4/8/2019 • 2 minutes to read • Edit Online
The REF CURSOR examples are comprised of the following three Microsoft Visual Basic examples that
demonstrate using REF CURSORs.
SAMPLE DESCRIPTION
REF CURSOR Parameters in an OracleDataReader This example executes a PL/SQL stored procedure that returns
a REF CURSOR parameter, and reads the value as an
OracleDataReader.
Retrieving Data from Multiple REF CURSORs Using an This example executes a PL/SQL stored procedure that returns
OracleDataReader two REF CURSOR parameters, and reads the values using an
OracleDataReader.
Filling a DataSet Using One or More REF CURSORs This example executes a PL/SQL stored procedure that returns
two REF CURSOR parameters, and fills a DataSet with the rows
that are returned.
To use these examples, you may need to create the Oracle tables, and you must create a PL/SQL package and
package body.
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;
See also
Oracle REF CURSORs
ADO.NET Managed Providers and DataSet Developer Center
REF CURSOR Parameters in an OracleDataReader
4/8/2019 • 2 minutes to read • Edit Online
This Microsoft Visual Basic example executes a PL/SQL stored procedure that returns a REF CURSOR parameter,
and reads the value as an 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
See also
Oracle REF CURSORs
ADO.NET Managed Providers and DataSet Developer Center
Retrieving Data from Multiple REF CURSORs Using
an OracleDataReader
4/8/2019 • 2 minutes to read • Edit Online
This Microsoft Visual Basic example executes a PL/SQL stored procedure that returns two REF CURSOR
parameters, and reads the values using an 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
See also
Oracle REF CURSORs
ADO.NET Managed Providers and DataSet Developer Center
Filling a DataSet Using One or More REF CURSORs
4/8/2019 • 2 minutes to read • Edit Online
This Microsoft Visual Basic example executes a PL/SQL stored procedure that returns two REF CURSOR
parameters, and fills a DataSet with the rows that are returned.
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
ds.Relations.Add("EmpDept", ds.Tables("Dept").Columns("Deptno"), _
ds.Tables("Emp").Columns("Deptno"), False)
DataGrid1.DataSource = ds.Tables("Dept")
End Using
See also
Oracle REF CURSORs
ADO.NET Managed Providers and DataSet Developer Center
OracleTypes
4/28/2019 • 2 minutes to read • Edit Online
The .NET Framework Data Provider for Oracle includes several structures you can use to work with Oracle data
types. These include OracleNumber and OracleString.
NOTE
For a complete list of these structures, see System.Data.OracleClient.
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.
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle Sequences
4/28/2019 • 3 minutes to read • Edit Online
The .NET Framework Data Provider for Oracle provides support for retrieving the server-generated key Oracle
Sequence values after performing inserts by using the OracleDataAdapter.
SQL Server and Oracle support the creation of automatically incrementing columns that can be designated as
primary keys. These values are generated by the server as rows are added to a table. In SQL Server, you set the
Identity property of a column; in Oracle you create a Sequence. The difference between auto-increment columns in
SQL Server and sequences in Oracle is that:
In SQL Server, you mark a column as an auto-increment column and SQL Server automatically generates
new values for the column when you insert a new row.
In Oracle, you create a sequence to generate new values for a column in your table, but there is no direct
link between the sequence and the table or column. An Oracle sequence is an object, like a table or a stored
procedure.
When you create a sequence in an Oracle database, you can define its initial value and the increment between its
values. You can also query the sequence for new values before submitting new rows. That means your code can
recognize the key values for new rows before you insert them into the database.
For more information about creating auto-increment columns by using SQL Server and ADO.NET, see Retrieving
Identity or Autonumber Values and Creating AutoIncrement Columns.
Example
The following C# example demonstrates how you can retrieve new sequence values from Oracle database. The
example references the sequence in the INSERT INTO query used to submit the new rows, and then returns the
sequence value generated using the RETURNING clause introduced in Oracle10g. The example adds a series of
pending new rows in a DataTable by using ADO.NET’s auto-increment functionality to generate "placeholder"
primary key values. Note that the increment value ADO.NET generated for the new row is just a "placeholder".
That means the database might generate different values from the ones ADO.NET generates.
Before submitting the pending inserts to the database, the example displays the contents of the rows. Then, the
code creates a new OracleDataAdapter object and sets its InsertCommand and the UpdateBatchSize properties.
The example also supplies the logic to return the server-generated values by using output parameters. Then, the
example executes the update to submit the pending rows and displays the contents of the DataTable.
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);
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle Data Type Mappings
4/8/2019 • 3 minutes to read • Edit Online
The following table lists Oracle data types and their mappings to the OracleDataReader.
The following table lists Oracle data types and the .NET Framework data types (System.Data.DbType and
OracleType) to use when binding them as parameters.
FLOAT Single, Double, Decimal Float, Double, Number Size determines the
System.Data.DBType and
OracleType.
INTEGER SByte, Int16, Int32, Int64, SByte, Int16, Int32, Size determines the
Decimal Number System.Data.DBType and
OracleType.
UNSIGNED INTEGER Byte, UInt16, UInt32, Byte, UInt16, Uint32, Size determines the
UInt64, Decimal Number System.Data.DBType and
OracleType.
The InputOutput, Output, and ReturnValue ParameterDirection values used by the Value property of the
OracleParameter object are .NET Framework data types, unless the input value is an Oracle data type (for
example, OracleNumber or OracleString). This does not apply to REF CURSOR, BFILE, or LOB data types.
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
Oracle Distributed Transactions
4/8/2019 • 2 minutes to read • Edit Online
The OracleConnection object automatically enlists in an existing distributed transaction if it determines that a
transaction is active. Automatic transaction enlistment occurs when the connection is opened or retrieved from the
connection pool. You can disable auto-enlistment in existing transactions by specifying
Enlist=false
See also
Oracle and ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
ADO.NET Entity Framework
2/7/2019 • 2 minutes to read • Edit Online
The docs.microsoft.com/ef/ site is now the main location for the Entity Framework content.
The content for this topic is now available on the following page: Introducing Entity Framework.
SQL Server and ADO.NET
4/8/2019 • 2 minutes to read • Edit Online
This section describes features and behaviors that are specific to the .NET Framework Data Provider for SQL
Server (System.Data.SqlClient).
System.Data.SqlClient provides access to versions of SQL Server, which encapsulates database-specific protocols.
The functionality of the data provider is designed to be similar to that of the .NET Framework data providers for
OLE DB, ODBC, and Oracle. System.Data.SqlClient includes a tabular data stream (TDS ) parser to communicate
directly with SQL Server.
NOTE
To use the .NET Framework Data Provider for SQL Server, an application must reference the System.Data.SqlClient
namespace.
In This Section
SQL Server Security
Provides an overview of SQL Server security features, and application scenarios for creating secure ADO.NET
applications that target SQL Server.
SQL Server Data Types and ADO.NET
Describes how to work with SQL Server data types and how they interact with .NET Framework data types.
SQL Server Binary and Large-Value Data
Describes how to work with large value data in SQL Server.
SQL Server Data Operations in ADO.NET
Describes how to work with data in SQL Server. Contains sections about bulk copy operations, MARS,
asynchronous operations, and table-valued parameters.
SQL Server Features and ADO.NET
Describes SQL Server features that are useful for ADO.NET application developers.
LINQ to SQL
Describes the basic building blocks, processes, and techniques required for creating LINQ to SQL applications.
For complete documentation of the SQL Server Database Engine, see SQL Server Books Online for the version
of SQL Server you are using.
SQL Server Books Online
See also
Securing ADO.NET Applications
Data Type Mappings in ADO.NET
DataSets, DataTables, and DataViews
Retrieving and Modifying Data in ADO.NET
ADO.NET Managed Providers and DataSet Developer Center
DataSets, DataTables, and DataViews
4/28/2019 • 2 minutes to read • Edit Online
The ADO.NET DataSet is a memory-resident representation of data that provides a consistent relational
programming model regardless of the source of the data it contains. A DataSet represents a complete set of data
including the tables that contain, order, and constrain the data, as well as the relationships between the tables.
There are several ways of working with a DataSet, which can be applied independently or in combination. You
can:
Programmatically create a DataTable, DataRelation, and Constraint within a DataSet and populate the
tables with data.
Populate the DataSet with tables of data from an existing relational data source using a DataAdapter .
Load and persist the DataSet contents using XML. For more information, see Using XML in a DataSet.
A strongly typed DataSet can also be transported using an XML Web service. The design of the DataSet makes
it ideal for transporting data using XML Web services. For an overview of XML Web services, see XML Web
Services Overview. For an example of consuming a DataSet from an XML Web service, see Consuming a
DataSet from an XML Web Service.
In This Section
Creating a DataSet
Describes the syntax for creating an instance of a DataSet.
Adding a DataTable to a DataSet
Describes how to create and add tables and columns to a DataSet.
Adding DataRelations
Describes how to create relations between tables in a DataSet.
Navigating DataRelations
Describes how to use the relations between tables in a DataSet to return the child or parent rows of a parent-
child relationship.
Merging DataSet Contents
Describes how to merge the contents of one DataSet, DataTable, or DataRow array into another DataSet.
Copying DataSet Contents
Describes how to create a copy of a DataSet that can contain schema as well as specified data.
Handling DataSet Events
Describes the events of a DataSet and how to use them.
Typed DataSets
Discusses what a typed DataSet is and how to create and use it.
DataTables
Describes how to create a DataTable, define the schema, and manipulate data.
DataTableReaders
Describes how to create and use a DataTableReader.
DataViews
Describes how to create and work with DataViews and work with DataView events.
Using XML in a DataSet
Describes how the DataSet interacts with XML as a data source, including loading and persisting the contents of
a DataSet as XML data.
Consuming a DataSet from an XML Web Service
Describes how to create an XML Web service that uses a DataSet to transport data.
Related Sections
What's New in ADO.NET
Introduces features that are new in ADO.NET.
ADO.NET Overview
Provides an introduction to the design and components of ADO.NET.
Populating a DataSet from a DataAdapter
Describes how to load a DataSet with data from a data source.
Updating Data Sources with DataAdapters
Describes how to resolve changes to the data in a DataSet back to the data source.
Adding Existing Constraints to a DataSet
Describes how to populate a DataSet with primary key information from a data source.
See also
ADO.NET
ADO.NET Managed Providers and DataSet Developer Center