Você está na página 1de 328

Introduction to ADO.

NET Using C# 2010


Learning Objectives
After completing this topic, you should be able to

identify the features, capabilities, and benefits of ADO.NET identify the components and uses of the ADO.NET object model, the DataSet object, and data providers identify the uses and benefits of LINQ and the Entity Framework

1. An overview of ADO.NET
Developing applications that can interact with multiple data sources simultaneously is a challenge, as each data source uses a different protocol and language to permit access. But with ADO.NET 4, this challenge is easily overcome. ADO.NET provides an object-oriented set of class libraries that allows applications to connect to various data sources, making it possible to access or manipulate any data from different databases. These data sources can be as varied as SQL Server and XML, or use data access methods such as SQL Client, ODBC, or OLE DB. ADO.NET is designed to permit data retrieval or modification activities in both connected and disconnected environments. In a connected environment, you stay connected to a data source for as long as it runs. A connected environment has the benefits of ensuring data integrity. The changes you make are made directly to the data source and locking records is straightforward. In addition, you can track which applications are connected and determine their access rights. However, a connected environment is unable to handle surges in demand for shared data, given the number of users who may access it over the Internet. In a disconnected environment, you stay connected to the data source only for as long as it takes to retrieve or update data. Because connections are freed up quickly after use, this environment allows applications accessing data to scale up better by using fewer system resources and using them effectively. Also, load balancing becomes possible because different servers can easily handle connections that are opened and closed regularly. This kind of scalability is particularly useful for web services because demand surges on the web are unpredictable. In this case, after a user finishes with a connection, it is freed up for other users.

For these reasons, web-based applications use ADO.NET in a disconnected architecture.

Question
What are the advantages of disconnected data-access environments? Options: 1. 2. 3. 4. Provides security Ensures data integrity Enables greater scalability Is suitable to the World Wide Web

Answer
Suitability to the World Wide Web and greater scalability are advantages of a disconnected dataaccess environment. Option 1: Incorrect. A connected environment is more secure because it's easier to determine which applications are connected and to determine their access rights. Option 2: Incorrect. A connected environment safeguards data more efficiently than a disconnected one because changes are made directly to the data source, unlike in a disconnected environment. Option 3: Correct. Applications accessing data in a disconnected environment scale up better because connections are used and freed up quickly. Option 4: Correct. Web-based applications must have a disconnected architecture because Internet connections cannot be guaranteed. Correct answer(s): 3. Enables greater scalability 4. Is suitable to the World Wide Web ADO.NET data-access applications use data-access models that are divided into tiers for accessing data. Each tier is a discrete, logical subdivision of application functionality. The most typical tiers of modern data-access applications are the client tier The client tier comprises the user interface. the business-logic tier The business-logic tier contains logic for defining functionality of the system. the data-services tier, and

The data-services tier comprises the data used by the business logic tier. the interoperability tier The interoperability tier comprises the logic that enables interactivity between applications residing on different operating systems. It also comprises the logic that enables use of different kinds of data. Application functionality tiers can be grouped and placed together into a one-data tier or separated into multiple tiers. Thus, an ADO.NET application model may have one to n data tiers. Depending upon the number of tiers within a data-access application, the operation of applications will vary. 1-tier This monolithic model bundles the user interface, business logic, and data services into a single tier, typically residing on one computer. An example of this model is a single-user Microsoft Access database application. This model simplifies access to the stored data, because it is stored at only one location. However, this model is difficult to scale because every change necessitates a recompiled version of the entire application for additional users. 2-tier In the 2-tier model, the user interface and business logic cohabit one tier, while data services resides in another. Quite often, business logic straddles both tiers with some of it crossing over to the data-services tier as stored procedures. This model separates functionality better than the 1-tier model. However, it is still difficult to scale because the client is a "fat client" containing two layers. 3-tier In the 3-tier model, user interface, business logic, and data services each reside in their own tier. This model separates functionality more effectively than its 1-tiered or 2-tiered counterparts. Because the client is a "thin client" containing just one layer, this model is more scalable. However, due to its complexity, the 3-tier model can be more difficult to maintain and secure. n-tier The n-tier model allows you to add as many tiers to an application as you wish. It is highly scalable and it enables applications residing on different platforms to interoperate. However, as the number of tiers increase, the complexity of your application also increases, and security becomes more difficult to implement. n-tier with Internet The n-tier with Internet model is a version of the n-tier model that's enabled for the Internet and Intranets. In this model, the services are distributed, clients deploy seamlessly, and the only updating required occurs at the server level.

The only key disadvantage in using this model is the difficulty in maintaining security over the Internet. You can build Windows-based applications and Web services and Web applications as data access applications with ADO.NET to provide or form part of an enterprise solution. ADO.NET applications need to be robust, as they may be deployed over the Internet. Internet messaging cannot rely on an always-open client/server connection. Connections are not always guaranteed to function, even when messaging occurs. And messages that reach their destination may still have to get through firewalls and cross platforms, if they are to have their desired effect. So when developing data-access applications using ADO.NET, you can use web-based standards such as XML. Because it runs over HTTP, XML allows you to send structured data, even through firewalls. And XML, being a web-based standard is platform independent.

Question
Data-access models describe the architecture behind a data-access application. Which statements are true about data-access models? Options: 1. The number of tiers within a data-access application affects the way an application operates 2. Tiers represent discrete, logical sections of application functionality 3. The 2-tier model enables applications residing on different platforms to interoperate 4. The business-logic tier comprises the user interface

Answer
Option 1: Correct. Data-access models can have one to n tiers, and accordingly affect the way an application operates. A 1-tier model only has a single tier that includes the client, business logic, and data services. Whereas an n-tier lets you add as many tiers to the application as you want. Option 2: Correct. You can have a client tier comprising of the user interface, a business logic tier for system functionality, or a data services tier for data among others representing specific sections in the application. Option 3: Incorrect. The n-tier model enables applications residing on different platforms to interoperate. Option 4: Incorrect. The business logic tier comprises the logic that enables an application to interact with its data source, validate data, and compute results.

Correct answer(s): 1. The number of tiers within a data-access application affects the way an application operates 2. Tiers represent discrete, logical sections of application functionality

2. The architecture of ADO.NET


The ADO.NET data access framework uses a disconnected data access model required by ntiered web-based applications. This is further reflected in the ADO.NET architecture, which supports web-based and distributed applications. To address the needs of these applications, ADO.NET uses a set of classes or namespaces within the .NET Framework. These namespaces, which are grouped beneath the .NET Frameworks System.Data, provide data access and data manipulation capabilities to ADO.NET applications. There are two central components that facilitate data access and data manipulation in a disconnected scenario. DataSet The DataSet object is the core component for data access. It is a miniature in-memory database of data and associated metadata from a back-end database. Because it is maintained independently, it can be used with multiple data sources, thus minimizing system overload and improving scalability. It contains data retrieved from a data source containing tables and their relations. Datasets also let you manage XML data. .NET Framework Data Providers If you want to connect to a database in order to manipulate or retrieve data in the form of datasets, you use the .NET Framework data providers. They are class libraries that ADO.NET provides for communication with data sources. Because different data sources use different protocols for interaction, there are a number of .NET framework data providers. Their names reflect the data source or protocol they help you interact with for data retrieval or manipulation. The DataSet object is derived from the System.Data namespace. This object is logically linked to a DataAdapter object on the data provider, which is used to query the data source and populate datasets from it. Because the DataSet is a miniature version of a data source, it contains the structure of the original database. And so, it comprises 1-n tables with rows, columns, and constraints, and 0-n relations between tables. The DataTable component, which maps to the tables of the original database tables, contains DataRowCollection, DataColumnCollection, ConstraintCollection, and DataRelationCollection. .NET framework data providers are found in System.Data.dll, and are integrated with the XML classes found in System.xml.dll.

You use the data provider to administer commands that help you access or manipulate the data. Retrieved data is then placed in a DataSet and processed directly to the user or routed between tiers. The data providers you use depend on the design and data source of your application. This is because the names and functionality of data provider classes are database dependent. ADO.NET supports data providers for SQL Server The .NET framework data provider for SQL Server uses its own protocol to communicate with SQL Server. Its performance is optimized because it accesses the database without adding an OLE DB or ODBC layer. OLE DB The OLE DB data provider uses native OLE DB through COM Interop to enable data access from sources that use the OLE DB method. ODBC When a data source uses the ODBC method for data access, you use the .NET framework data provider for ODBC to enable data access. This data provider uses the native ODBC Driver Manager. Oracle deprecated, and Though the types in this namespace are supported in .NET 4, they're deprecated and will be removed in future releases. So Microsoft recommends that you use a third-party provider instead of the .NET data provider for Oracle. New applications using this namespace will generate warnings when compiled in .NET 4. Once compiled, they will run without errors or warnings. Entity/Client To access data based on an Entity Data Model, also known as EDM, you use the Entity/Client Provider that uses Entity SQL to communicate with the underlying data provider. It does not interact directly with a data source. The ADO.NET data providers contain four key components with generic names. In code, these objects take prefixes such as Sql or OleDb.
DataReader The DataReader

object uses DbDataReader as its base class and lets you access datasource data in forward-only, read-only mode. It comes into being as the return value from the ExecuteReader method of the Command class. object enables SQL queries and stored procedures to travel over the wire to either populate or make changes to the data source or the application data. The objects used for this include the Command, the Connection, and the DataSet objects. The base class for the DataAdapter object is the DbDataAdapter class. There is a separate DataAdapter for each DataTable within a DataSet. If the

DataAdapter The DataAdapter

DataAdapter Command

is used, database connections can be opened implicitly. Otherwise, you need to call the Open method of the Connection object. The Command object executes a data manipulation statement or a query against a data source. To execute queries, you use one of the Execute methods of the Command object: SELECT, UPDATE, INSERT, or DELETE. Queries are transmitted over the wire through reference to the Connection object. They are then either made accessible through the DataReader object or transported to DataSet objects with the help of DataAdapter objects. The base class for the Command object is the DbCommand class.
Connection The Connection

object handles connections to a specific data source. By specifying the correct connection string, this object helps identify data sources and authenticate the user.
Connection

objects also reference queries to the data source encapsulated in the Command

object. The base class for the Connection object is the DbConnection class.

Question
Identify statements specific to the components of the ADO.NET data access model. Options: 1. Data providers manage communication between an application and a database 2. Connection and Command objects, the DataReader, and the DataAdapter are components of DataSets 3. DataSets and Data Providers are components of the ADO.NET model 4. DataSets depend on the database and manage data from a single data source

Answer
Option 1: Correct. Data providers manage communication by letting you connect to databases and execute commands to retrieve information. Option 2: Incorrect. DataReader, DataAdapter, Command, and Connection are the four key objects of a data provider. Option 3: Correct. DataSets and the Data Providers, the two key components of ADO.NET architecture, help access and manipulate data. Option 4: Incorrect. DataSets are maintained independently of the original database, and therefore can be used with multiple data sources.

Correct answer(s): 1. Data providers manage communication between an application and a database 3. DataSets and Data Providers are components of the ADO.NET model

Question
Match each data provider supported by ADO.NET with its description. Options: 1. 2. 3. 4. .NET Framework Data Provider for Oracle .NET Framework Data Provider for OLE DB Entity/Client Provider .NET Framework Data Provider for SQL Server

Targets: 1. 2. 3. 4. It is deprecated in .NET Framework 4 Optimized to access SQL Server directly Enables data access by using native OLE DB through Com Interop Does not interact directly with a data source

Answer
The types in System.Data.OracleClient are deprecated in .NET 4 and they will be removed in future. So Microsoft recommends you to use a third-party provider instead of using a .Net provider for Oracle as new applications using this provider generate warnings. This provider accesses its database directly and efficiently without adding connection layers such as OLE DB or ODBC. The data provider for OLE DB uses native OLE DB through Com Interop to enable data access, and supports both local and distributed transactions. The Entity/Client Provider uses Entity SQL to communicate with the underlying data provider. Correct answer(s): Target 1 = Option A Target 2 = Option D Target 3 = Option B Target 4 = Option C

3. LINQ and the Entity Framework


Traditionally, when ADO.NET application developers developed data-access applications on the .NET platform, they had to be familiar with both the language used for developing the database application and the language used for querying the data sources. These complexities of bridging disparate data representations made developing data-access applications a tedious and time-consuming task. With ADO.NET evolving with .NET Framework 3.5, two new technologies that help raise the level of abstraction for data programming were developed the Language-Integrated Query or LINQ and the Entity Framework. LINQ provides native language syntax for data-access application programming. Entity Framework allows developers to write less data-access code and abstract the structure of the data into a more business-friendly (and less normalized) manner. LINQ was developed to access and modify data from data sources with a set of general purpose standard query operators. LINQ can be used with several data sources such as SQL, XML, Data Set, and Business objects. Because it uses a unified syntax regardless of the type of data, developers using LINQ-enabled languages do not need to know database-specific programming languages to query databases. All that is required is a LINQ provider that maps your LINQ queries to the data source being queried. The LINQ provider translates your queries into commands that data source can execute, and then retrieves the query results. The LINQ to ADO.NET providers include LINQ to SQL The LINQ to SQL provider enables you to query a SQL database. When you use this provider, the relational database's data model is mapped to the object model you developed in your language. An Object Relational Designer, also known as O/R Designer, helps in this mapping. The LINQ to SQL provider translates the LINQ query into SQL before sending it to the SQL database for execution. And when the results are returned, they are translated back to objects you defined in your own programming language. LINQ to DataSet To query, aggregate, and update data cached in an ADO.NET DataSet, or to query data consolidated from one or more data sources, you use the LINQ to DataSet provider. Instead of writing custom queries for the DataSet, you can write queries in the programming language to query a populated DataSet.

Because you are querying an enumeration of DataRow objects and not a custom type, you can use any of the members of the DataRow class. LINQ to Entities, and Using the LINQ to Entities provider, you query databases in the same language used to construct the business logic. This provider uses the Object Services infrastructure. Here when you construct a generic ObjectQuery instance through the ObjectContext, it represents a query that returns an instance or collection of typed entities. The retrieved results are located in the object context and can be updated. LINQ to XML LINQ to XML provides an interface to work with XML directly from within C#. It does this by providing an in-memory version of the XML document, in a similar way to DOM. LINQ to XML enables you to load XML files, create XML from scratch, write queries on the Xml Document, and create, manipulate and transform XML trees. You can also take advantage of typing, compile-time checking and debugging functions available because of LINQ's integration with Visual C#. Querying using LINQ provides you with many benefits. You do not need to be familiar with datasource-specific languages to query data sources because LINQ comes with native language syntax. You can write your queries in C# or VB itself. You can write code faster because LINQ comes with full type safety, IntelliSense and compiletime checking support. Because you write a query in C# or VB, your query result will be returned as strongly-typed objects. You will also be able to trap errors in your queries during the compile time, instead of waiting until run time to be able to do so. LINQ provides support for varied data sources. Because of this, you can query almost any data source including in-memory arrays and collections, SQL Server database, ADO.NET datasets, XML files, or any other remote or local data sources that support LINQ. Because LINQ is controls bound, users can easily view and modify query results to suit any format they want.

Question
What are the benefits and uses of LINQ? Options: 1. 2. 3. 4. Supports native language syntax Provides the IntelliSense feature Allows for run-time checking Helps perform relational database mapping

Answer

Option 1: Correct. This feature of LINQ lets you write queries in your own application programming language. You do not need to write code in a datasource-specific language. Option 2: Correct. Because of this feature, when you write a query, you will receive results as strongly-typed objects. Option 3: Incorrect. LINQ supports compile-time checking. You do not have to wait until runtime to check for errors in the code. Option 4: Incorrect. LINQ does not provide for database mapping. The Entity Framework does so. Correct answer(s): 1. Supports native language syntax 2. Provides the IntelliSense feature Previously, when a data-access application was developed, it was connected to the database in such a way that altering either the application or the database would affect the other if they were not in sync. The application would not function as desired, and in some cases, would lead to application failure. Rectifying changes related to either business logic or table schema changes took a lot of time and effort on the part of the developer. Entity Framework provided a solution to developers, where changes made to code at one location were automatically reflected in the application. This helped abstract the application from the changes made to the relational database schema. The Entity Framework was developed to allow developers to view data in a logical model, which provides flexibility. With this framework, developers can write programs against classes that are generated from the conceptual schema. The Entity framework generates a conceptual model that developers can write code against. The Entity Framework is an Object-Relational Mapping Framework that comprises a data model and services for working with application data during design and run-time. At its core is a conceptual data model, the EDM, which can be used to model the data of any given domain. This model consists of three layers: conceptual, logical, and mapping. These three layers, defined using XML files, allow data to be mapped from a relational database to a more object-oriented business model. This helps reduce the coding and maintenance effort required for data-access applications. Benefits of using Entity Framework include reduction of code creation and code maintenance Code can be reduced because the layers of the EDM help bridge the gap between the application and the relational database model. Similarly, code can be maintained when

the XML mapping file with its table properties and relationships to the database tables is generated. This helps reflect table schema changes without making major changes to the application. The result is less code maintenance. greater flexibility, and The Entity Framework provides greater flexibility both in defining objects and optimizing the logical model. It does this by mapping relational tables, columns, and foreign key constraints in logical models to entities and relationships in conceptual models. avoidance of hard code dependency When using the Entity Framework, you can avoid hard code dependency to a specific schema or a specific data storage engine. This is made possible because there is a layer of abstraction between the logical and conceptual schema of the application. Other benefits of using Entity Framework are access to multiple database systems Entity Framework developers are able to access data from multiple storages using a consistent application object model because the application is free from hard code data storage dependencies. business-friendly abstraction of the data structure, and With data presented in an object-oriented way, data structure is abstracted from the application to the relational database. support for LINQ LINQ offers a compile-time syntax checking for queries against the conceptual model. EDM provides easy access to data using LINQ.

Question
What are the benefits and uses of the Entity Framework? Options: 1. 2. 3. 4. Reduces code maintenance and code creation Enables hard coding of the application to a specific schema Uses native language syntax Uses a consistent application model to access multiple databases

Answer
Option 1: Correct. The XML mapping file generated when the EDM is created is modified to reflect the table schema changes without making any major changes in the application. Option 2: Incorrect. There is a layer of abstraction between the logical and conceptual schema of the application. Because of this, there is no requirement to hard code the application to a specific data storage engine or schema.

Option 3: Incorrect. The Entity Framework does not use native language syntax for interacting with data sources. It is LINQ that uses native language syntax. Option 4: Correct. Developers using the Entity Framework can access data from multiple storages using a consistent application object model. They can do so because the application is free from hard code data storage dependencies. Correct answer(s): 1. Reduces code maintenance and code creation 4. Uses a consistent application model to access multiple databases

Summary
ADO.NET is a set of classes that helps access data from multiple data sources. It follows the disconnected architecture, where the user stays connected to the data source only for as long as it takes to retrieve or update the data. This offers scalability, flexibility, and reusability, and is especially useful for Web applications. The ADO.NET data-access model is subdivided into tiers ranging from a single tier to n-tiers. Two main components of ADO.NET are the .NET Framework Data Providers and the DataSet. Data providers help connect the application to the data source, enabling you to retrieve data in the form of DataSets. Each of these components comprises core objects. LINQ and Entity Framework are ADO.NET technologies that facilitate generic data access solutions for different types of data sources.

Table of Contents

Defining an ADO.NET Connection Object Using C# 2010


Learning Objectives
After completing this topic, you should be able to

select the appropriate .NET data provider in a given scenario identify the steps for setting the ConnectionString property identify the features of the ConnectionString property's provider-specific attributes

1. Selecting a .NET data provider

Some of the primary components of the ADO.NET Architecture are data providers. A data provider connects the .NET Framework to a data source in order to access and modify the data stored in the data source. Providers act as channels of communication between application code and the data source. They help retain all functionality while increasing performance. Some commonly used data sources are SQL Server, OLE DB and ODBC data sources. Depending on the data source to which you want to connect, you use one of the available .NET Framework data providers. Some available .NET Framework data providers are the SQL Server .NET data provider The .NET Framework data provider for SQL Server uses the System.Data.SqlClient namespace and the SQL Server native Tabular Data Stream or TDS protocol to provide efficient and feature-rich access. Because it accesses SQL Server directly without adding another layer such as OLE DB or ODBC, the SQL Server data provider enhances performance. However, if you change the data source, you need to modify your ADO.NET code because this data provider provides features specific to SQL Server. the OLE DB .NET data provider The OLE DB data provider uses the System.Data.OleDb namespace and is a generic OLE DB data provider. This data provider uses two layers OleDb provider and OleDb service component for accessing the database. The data provider for OLE DB does not work with the OLE DB provider for ODBC. Although it works with SQL Server 7.0 and later versions, the OLE DB provider lacks many of the features that the SQL Server provider offers. the ODBC .NET data provider, and The .NET Framework data provider for ODBC uses the System.Data.ODBC namespace. This data provider uses the native ODBC Driver Manager, also known as DM, to provide data access. the Oracle .NET data provider - deprecated The Oracle .NET data provider enables data access to Oracle data sources through client connectivity software. It uses the System.Data.OracleClient namespace that needs to be explicitly referenced. You need the Oracle client software version 8.1.7 or later versions to connect to the data sources. Although the types in System.Data.OracleClient are supported in .NET 4, they are deprecated and will be removed in future releases. Microsoft recommends therefore, that you use a third party provider for Oracle. In addition to the data providers integrated into ADO.NET Framework 4, there is a range of third-party providers that you can use with ADO.NET. These include the Oracle Data Provider for .NET, also known as ODP.NET, from Oracle. Suppose you are an ADO.NET programmer building a data application. When building the data application, your first task is to select a data provider. While it is possible to use different

providers to access the same source, choosing a native provider is better, performance-wise, whenever possible. In addition to selecting an appropriate data provider, you need to specify provider objects. All data sources have some common objects and related classes, which include
Connection The Connection object enables you to establish a connection to a data source. Command You can use the Command object to execute commands against a data source. You

use this object to read, update, or delete data from a data source. DataReader, and If you want to access records in a read-only, forward-only mode, you use the DataReader object. This object helps you efficiently process a large list of results one record at a time.
DataAdapter

You use the DataAdapter object to transfer data between a data source and a DataSet object.

Question
You are using .NET Framework 4 to retrieve data from a SQL Server 2008 database. Which is the most effective .NET Framework data provider to use? Options: 1. 2. 3. 4. ODBC .NET Framework data provider OLE DB .NET Framework data provider SQL Server. NET Framework data provider Oracle .NET Framework data provider

Answer
Option 1: Incorrect. The ODBC .NET Framework data provider is best suited for accessing data from ODBC databases. Option 2: Incorrect. While you could use the OLE DB provider it is more efficient to use the provider specifically configured for SQL Server data. Option 3: Correct. The .NET Framework data provider for SQL Server can be used only if your data source is SQL Server 7.0 or a later version. Option 4: Incorrect. The .NET Framework data provider for Oracle is suited for Oracle client software 8.1.7 or a later version. However, the types for this provider are deprecated, and it's recommended that you use a third-party provider such as ODP.NET to access Oracle data.

Correct answer(s): 3. SQL Server. NET Framework data provider

Question
Match the .NET Framework data providers with their requirements. Options: 1. 2. 3. 4. Oracle data provider OLE DB data provider ODBC data provider SQL Server data provider

Targets: 1. 2. 3. 4. The namespace for this provider needs to be explicitly referenced This data provider needs the TDS protocol to efficiently access a data source This data provider uses two layers for accessing a database This data provider requires the native DM to access data

Answer
The namespace for the Oracle data provider must be explicitly referenced. This data provider enables access to Oracle data sources through client connectivity software. The .NET Framework data provider for SQL Server requires the SQL Server native TDS protocol to provide efficient, feature-rich access to a data source. The OLE DB data provider is a generic data provider that uses two layers OleDb provider and OleDb service component for accessing a data source. The .NET Framework data provider for ODBC requires the native ODBC DM to access data from ODBC databases. Correct answer(s): Target 1 = Option A Target 2 = Option D Target 3 = Option B Target 4 = Option C

2. Setting the ConnectionString property


You use an ADO.NET Connection object to open a connection to a data source. This Connection object inherits from the DbConnection class and a provider-specific ConnectionString property. This property contains the data provider-specific Connection string syntax. Data providers use connection strings to pass initialization information to a data source as a parameter. So you need to ensure you code your connection string syntax according to the data provider you are using and use the correct ConnectionString property. To help you construct syntactically valid connection strings at run time, recent versions of ADO.NET contain Connection string builders for .NET Framework data providers. As a result, you don't need to manually link together connection string values in your code. These Connection string builders include
SqlConnectionStringBuilder

for System.Data.SqlClient for System.Data.OleDb, and

OleDbConnectionStringBuilder OdbcConnectionStringBuilder

for System.Data.Odbc

Suppose you want to open a connection to access employees' data. To do this, you first prepare a string within which you set various provider attributes. After this, you assign the string to the ConnectionString property of the Connection object. In this case, the System.Data.SqlClient namespace is the .NET Framework data provider for SQL Server. This data provider uses a SqlConnection object to define a unique connection to the SQL data source.

Graphic
The code to declare a ConnectionString property is: SqlConnection myConn;

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn;

string selectString; selectString = "Select * from SampleTable";

Then you select the string and specify the data that you want to access from the database.

Graphic
The code to select the string and specify the data to access from the database is: string selectString; selectString = "Select * from SampleTable";

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable";

Try It
After specifying the data you want to retrieve, you want to create a connection object. The existing code is: using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new MISSING CODE; To complete the task 1. Type SqlConnection() and click the Enter key provided Type SqlConnection() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A new SqlConnection is created to connect to the specified data source. You then assign the string to the ConnectionString property of the Connection object. The ConnectionString property is composed of attribute names, with their respective values in pairs, and separated by semicolons. Some of these attributes are common to all .NET Framework data providers and others are specific to each data provider. These are some common attributes of ConnectionString properties.

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=False"); myConn.Open(); Data Source=(local) The Data Source

attribute specifies the data source that is used when a connection is open. When using SQL Server, this attribute is set to the name or network address of the Server instance to which it is connected. For example, for a local server instance, you should refer to the attribute as local. In the case of SQL Server, the Data Source attribute uses the Server, Addr, Address or Network Address as the keys within the connection string. attribute sets the name of the OLE DB or SQL database. In this case, the SQL database name is set to Samples.

Initial Catalog=Samples The Initial Catalog

Integrated Security=True The Integrated Security attribute determines whether the connection is secure. The Integrated Security attribute is set to True, which indicates the authentication mode

as Windows.
Connection Timeout=20 The Connection Timeout Persist Security Info=False

attribute specifies the time interval in seconds, after which a request, if not processed, will be terminated with an error. The default value is 15. If a connection is open or has been in an open state, the Persist Security Info attribute determines whether security-sensitive information, such as the password, is returned as part of the connection.

The Persist Security Info attribute is set to False by default, so sensitive information such as passwords aren't returned as part of the connection. You can also set the Integrated Security attribute of the SqlConnectionString to False. This indicates the Windows identity will not be used for authenticating a user.

Graphic
The code to set the Integrated Security to False is: Integrated Security=False

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass2 { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples; " + "Integrated Security=False;User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=False"); myConn.Open();

If instead of Windows Authentication, SQL Server Authentication is required, you can validate a user's identity by specifying the User ID and Password attributes. The User ID attribute specifies the SQL Server login account, whereas the Password attribute specifies the login password for the SQL Server account.

Graphic
The code to set the User ID and Password is: User ID=myUser;Password=myPassword

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass2 { public static void Main()

{ SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples; " + "Integrated Security=False;User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=False"); myConn.Open();

Finally, you either implicitly or explicitly call the Open method of the Connection object. This method causes the Connection object to pass the ConnectionString property to the relevant .NET data provider. After receiving the property, it attempts to connect to the specified data source. You can access the state of a connection using the object's State property.

Graphic
The code to open the connection is: myConn.Open();

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass2 { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples; " + "Integrated Security=False;User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=False"); myConn.Open();

Question
You want to retrieve product details from the products table stored in a SQL Server database. You are setting the ConnectionString property to connect to SQL Server. What would you do to ensure sensitive information is not returned as part of the connection? Options:

1. 2. 3. 4.

Set the Persist Security Info attribute to False Set the Persist Security Info attribute to True Set the Integrated Security attribute to True Set the Integrated Security attribute to False

Answer
Option 1: Correct. When the Persist Security Info attribute is set to False, sensitive information isn't returned from the database. Option 2: Incorrect. You set the Persist Security Info attribute to True, when you want to return sensitive information such as password details, with the connection. Option 3: Incorrect. If you're using the Windows Authentication mode, you set the Integrated Security attribute to True. Option 4: Incorrect. If you don't want to use Windows Authentication for identifying users, you set the Integrated Security attribute to False. Correct answer(s): 1. Set the Persist Security Info attribute to False

3. Identifying provider-specific attributes


In addition to common attributes such as User ID and Password, there are some providerspecific attributes of the ConnectionString property. Some attributes maintain default values, so you often won't need to specify them explicitly. However, some attributes must be specified, for example, the server name for a SqlConnection.ConnectionString. The provider-specific attributes take different values for SqlConnection and OleDbConnection. If you're using an OleDbConnection, you must specify the Provider attribute. This attribute sets or returns the name of the provider for the connection and can only be used for establishing a connection to an OLE DB data source. In this example, the Provider attribute is set to Microsoft.ACE.OLEDB.12.0, which represents a Microsoft Access database using the ACE provider.

Code
public class OleDbProviderClass { public static void Main() {

OleDbConnection myOleDbConn; string selectString; selectString = "Select EmpName from SampleTable"; myOleDbConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" + "Data Source=C:\\Samples.accdb"); myOleDbConn.Open(); OleDbCommand myOleDbCommand; myOleDbCommand = new OleDbCommand(selectString, myOleDbConn); OleDbDataReader myOleDbDataReader; myOleDbDataReader = myOleDbCommand.ExecuteReader(); while (myOleDbDataReader.Read()) { Console.WriteLine(myOleDbDataReader[0]); } Console.ReadLine(); myOleDbConn.Close(); } }

If you are creating the SqlClient data provider to connect to SQL Server, you can set attributes such as FailoverPartner, Encrypt, and TrustServerCertificate. The FailoverPartner attribute automatically redirects a connection when database mirroring failover occurs. This is useful when you connect SqlClient to a database being mirrored. To do this, you must specify the initial principal server and database in the connection string and the failover partner server using the FailoverPartner attribute. In this example, you specify the principal server as myServerA and the failover partner server as myServerB.

Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Server=myServerA; FailoverPartner=myServerB;"

In addition to FailoverPartner, you can set the Integrated Security attribute for Windows Authentication. The Integrated Security attribute used for the ConnectionString property varies by data provider. If you're using the SqlClient data provider, the Integrated Security can be set to true,

false,

or SSPI. In this case, the Integrated Security is set to false. This indicates the Windows identity will not be used for authenticating users.

Graphic
The code to set the value of the Integrated Security attribute to SSPI is: Integrated Security=SSPI

Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Server=myServerA; FailoverPartner=myServerB;" + "Integrated Security=false; User ID=myUsername; Password=myPwd;"

But for the OleDb provider, the Integrated Security attribute is always set to SSPI. If this attribute is set to true when used with OleDb, it throws an exception.

Graphic
The code to set the value of the Integrated Security attribute to SSPI is: Integrated Security=SSPI

Code
using System.Data; using System.Data.OleDb; public class OleDbProviderClass { public static void Main() { OleDbConnection myOleDbConn; string selectString; selectString = "Select EmpName from SampleTable"; myOleDbConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" + "Integrated Security = SSPI" + "Data Source=C:\\Samples.accdb");

The Integrated Security attribute takes different values for an Oracle database. When used with the OracleClient provider, the Integrated Security attribute is set to yes for providing Windows Authentication.

Code
using System.Data; using System.Data.OracleClient; public class OracleConnectionClass { public static void Main() { OracleConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new OracleConnection("Server=myServerA; FailoverPartner=myServerB;" + "Integrated Security=yes; User ID=myUsername; Password=myPwd;"

If you want to check Windows Authentication in an ODBC database, you set the Trusted_Connection attribute to yes.

Graphic
The code to set the value of the Trusted_Connection attribute to yes is: Trusted_Connection=yes

Code
using System.Data; using System.Data.Odbc; public class OdbcProviderClass { public static void Main() { OdbcConnection myOdbcConn; string selectString; selectString = "Select EmpName from SampleTable"; myOdbcConn = new OdbcConnection("Driver={Microsoft Access Driver (*.mdb)};" +"DBQ=C:\\Samples\\Northwind.mdb;" +"Trusted Connection=yes");

The TrustServerCertificate attribute is specified only when you use the SqlClient data provider to connect to a SQL Server 2005 or later instance with a valid certificate. The TrustServerCertificate attribute sets a value that indicates whether the database connection will be encrypted while bypassing the server certificate to validate trust.

Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Server=myServerA;" +"FailoverPartner=myServerB;" + "Integrated Security=false;" + "User ID=myUsername; Password=myPwd; " + "Encrypt=true; TrustServerCertificate=true"); myConn.Open();

If the TrustServerCertificate and Encrypt attributes are set to true, you can use SSL to encrypt an application even when a server certificate isn't created. You can also encrypt the application when the Force Protocol Encryption attribute isn't configured for the client. If the TrustServerCertificate attribute is set to true and the Encrypt attribute is set to false, the database connection is not encrypted.

Graphic
The code to set the value of the TrustServerCertificate and Encrypt attributes to true is: Encrypt=true; TrustServerCertificate=true

Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Server=myServerA; FailoverPartner=myServerB;" + "Integrated Security=false; User ID=myUsername; Password=myPwd; " + "Encrypt=true; TrustServerCertificate=true"); myConn.Open();

In addition to the TrustServerCertificate attribute, User Instance can be used with SQL Server Express 2005 or later connections. The User Instance attribute is specific to the SqlClient data provider and works only when you use Microsoft SQL Server 2005 Express

Edition. The User Instance attribute allows users without administrative privileges to run a local version of SQL Server. With a User Instance, non-administrators can also have database owner privileges within the instance running in their account. The User Instance attribute is only specified in a single-user mode, so only one user can access a database running on the User Instance attribute.

Note
The User Instance attribute isn't required for users with administrator rights. Moreover, this provider attribute doesn't allow multiple users to connect to a database. You can enable the User Instance attribute when a parent instance of SQL Server Express Edition is running. If SQL Server Express is installed, the User Instance is enabled by default. If required, you can also enable or disable the User Instance attribute by executing the sp_configure system stored procedure on the parent instance. To enable the User Instance attribute, you set sp_configure to 1. And to disable this attribute, you set sp_configure to 0.

Code
-- Enable user instances. sp_configure 'user instances enabled','1' -- Disable user instances. sp_configure 'user instances enabled','0'

Try It
You can manage the User Instance attribute by setting the User Instance connection string keyword in SQLClient to true or false. When set to true, the User Instance attribute is activated, connecting the .NET Framework to the database. If this attribute is set to false, no User Instance support is in effect and original behavior is preserved. You want to activate the User Instance attribute to enable a non-administrative user to access a database. Complete the code to do this. The existing code is: using System.Data; using System.Data.SqlClient; public class SqlConnectionClass2 { public static void Main() {

SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=.\\SQLEXPRESS;Database=Samples;Integrated Security=true;" + "AttachDBFileName=C:\\Samples.mdf;MISSING CODE"); myConn.Open(); To complete the task 1. Type User Instance=true and click the Enter key provided Type User Instance=true in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The User Instance attribute is set to true and is activated. Because each User Instance attribute generates a separate instance of SQL Server for each user, non-administrative users can access the database in isolation. This allows users to access databases without compromising another user's rights.

Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass2 { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=.\\SQLEXPRESS;Database=Samples;Integrated Security=true;" + "AttachDBFileName=C:\\Samples.mdf;User Instance=true"); myConn.Open();

Note
Because Windows Authentication is required to connect to a User Instance attribute, Integrated Security is set to true. The User Instance attribute is also supported by the SqlConnectionStringBuilder and AttachDBFilename properties. The AttachDBFileName connection property attaches the primary database file in this case, Samples.mdf which must include the full path name. AttachDBFileName also corresponds to the "extended properties" and "initial file name" keys within a SqlConnection string.

Graphic
The code to attach the primary database file using the AttachDBFileName connection property is: AttachDBFileName=C:\\Samples.mdf

Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass2 { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=.\SQLEXPRESS;Database=Samples;Integrated Security=true;" + "AttachDBFileName=C:\\Samples.mdf;User Instance=true"); myConn.Open();

Question
You are connecting to a SQL Server instance with a valid certificate. You want to ensure the database connection is encrypted when bypassing the server certificate. Which attribute would you use for this? Options: 1. 2. 3. 4. Set the Integrated Security attribute to true Set the User Instance attribute to true Set the FailoverPartner attribute to true Set the TrustServerCertificate value to true

Answer
Option 1: Incorrect. The Integrated Security attribute allows you to indicate whether Windows Authentication will be used for authenticating users. Option 2: Incorrect. The User Instance attribute allows users without administrative privileges to access an instance of a SQL Server Express database on their system. Option 3: Incorrect. The FailoverPartner attribute enables you to redirect a connection when you find a database mirroring failover.

Option 4: Correct. When connecting to an instance of SQL Server, you can set the TrustServerCertificate attribute to true. This setting encrypts the database connection while bypassing the server certificate. Correct answer(s): 4. Set the TrustServerCertificate value to true

Question
You're setting a connection for retrieving employee names from the employees table in Access database. You now want to complete the code to create the connection. Specify the appropriate class instance to create a suitable connection for an Access database. Code
public class OleDbProviderClass { public static void Main() { //Declare variables conn = new INSERT THE MISSING CODE("Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=Sample.mdb;"); conn.Open();

Answer
You use an OleDbConnection for connecting to an OLE DB data source, such as an Access database. Correct answer(s): 1. OleDbConnection

Question
You're connecting to a SQL Server 2008 instance without a valid certificate. You've set the Encrypt attribute to true. You now want to use SSL to encrypt the database connection without using the server certificate for trust validation. Set the TrustServerCertificate value to an appropriate value. Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass {

public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Server=myServerA; FailoverPartner=myServerB;" + "Integrated Security=false; User ID=myUsername; Password=myPwd; " + "Encrypt=true; TrustServerCertificate=INSERT THE MISSING CODE"); myConn.Open();

Answer
Setting the TrustServerCertificate and the Encrypt attributes to true enables you to encrypt a database connection when a server certificate isn't created. Correct answer(s): 1. true

Question
You are using the SqlClient connection string to connect to a SQL Server 2008 Express Edition. You want to allow non-administrative users to access SQL Server locally on their system. Specify an attribute with the appropriate value to do this. Code
using System.Data; using System.Data.SqlClient; public class SqlConnectionClass2 { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=.\\SQLEXPRESS;Database=Samples;Integrated Security=true;" + "AttachDBFileName=C:\\Samples.mdf;INSERT THE MISSING CODE"); myConn.Open();

Answer

You can invoke the User Instance attribute by setting the User Instance connection string keyword to true. This allows non-administrative users to access SQL Server locally on their system. Correct answer(s): 1. User Instance=true

Summary
ADO.NET allows you to establish connections to different data sources using managed data providers. The providers currently available include the SQL Server .NET Data Provider, the OLE DB .NET Data Provider, and the ODBC .NET Data Provider. ADO .NET 4 includes a provider for Oracle, which is System.Data.OracleClient. However, it is deprecated and will be removed in future releases. Connection objects are used to open a connection with an appropriate data source. The SqlConnection objects are used with SQL Server .NET data providers and the OleDbConnection objects are used with OLE DB .NET data providers. All connections require you to first prepare a connection string, within which you set various attributes, and then assign it to the ConnectionString property of the Connection object. Some attributes are common to all .NET providers, while others are provider specific. Attributes you may set include the server name, database filename, provider name, connection timeout, security options, and user instances.

Table of Contents
| Print | Contents | Close |

Managing Connections Using Visual C# 2010


Learning Objectives
After completing this topic, you should be able to

identify ways to open and close connections recognize ways for handling connection events recognize the code for handling typical exceptions

1. Opening and closing connections

To connect to a data source from within an ADO.NET application, you use connection objects. Using these objects, you first open the connection, process the transactions on the database, and then release the connection by closing it. When using connection objects, you also use events of these objects to retrieve useful information from the data source. You can use this information to use the connection optimally. Additionally, you need to handle exceptions that the data source throws when encountering errors. This helps ensure smooth functioning of the ADO.NET application. In ADO.NET, you can open and close connections with a data source in two ways: Explicitly and To explicitly open or close the connection with a data source, you use the Connection object with its methods, Open and Close. For example, to connect to Microsoft SQL Server 7.0 or later, you use the SqlConnection object of the .NET Framework Data Provider for SQL Server. Implicitly You use the DataAdapter object to implicitly open or close the connection with a data source. And to do this, you use the Fill method of the DataAdapter object. You want to back up data from your SQL Server 2008 database using an ADO.NET application. To do this, you first open a connection with SQL Server. To open the connection, you first declare the connection variable. In this example, you declare the variable, myConn.

Code
SqlConnection myConn;

Next you use the myConn connection variable to create a SqlConnection object using the new keyword.

Code
SqlConnection myConn; myConn = new SqlConnection();

After you create the new SqlConnection object, you specify a valid connection string to create the SqlConnection object. A connection string specifies the database name and the user credentials with which you want to connect to the database.

Code

SqlConnection myConn; myConn = new SqlConnection("Data Source=(local); " + "Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=false");

Note
You provide the user credentials if you use the SQL Server authentication to connect to the database. You want to connect to the Samples database using the SQL Server authentication with the username, myUser and password, myPassword. In this example, the integrated security parameter is set to false. To use integrated security, you can set this parameter to true.

Graphic
The code for the connection string is: ("Data Source=(local); " + "Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=false");

Code
SqlConnection myConn; myConn = new SqlConnection("Data Source=(local); " + "Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=false");

Note
Unlike SQL Server authentication, the integrated security parameter uses the security identity of the executing process to connect to the database.

Try It
You have specified the SQL connection string. Now you want to open the connection. To do this, you use the Open method. Complete the code to do this. The existing code is: SqlConnection myConn;

myConn = new SqlConnection("Data Source=(local); " + "Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=false"); myConn.MISSING CODE(); To complete the task 1. Type Open and click the Enter key provided Type Open in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to explicitly open a connection is completed. When you use the Open method, you need to ensure you provide all the details in the connection string accurately. Otherwise the Open method throws exceptions. There are many instances when the Open method throws an exception:

Code
SqlConnection myConn; myConn = new SqlConnection("Data Source=(local); " + "Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword; " + "Connection Timeout=20;Persist Security Info=false"); myConn.Open();

Invalid connection attributes provided Occurrence of a timeout before the connection is opened Invalid user ID or password provided, and User ID or password rejected for security reasons

Try It
You must always close the connection after using it. Closing a connection rolls back any pending transactions. You now want to close the myConn connection. The existing code is: SqlConnection myConn; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;" + "Integrated Security=true");

myConn.Open(); myConn.MISSING CODE(); To complete the task 1. Type Close and click the Enter key provided Type Close in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to explicitly close a connection is completed. In addition to closing a connection, you must return the connection to the pool. This will release the unmanaged resources of the SqlConnection object and thus enable efficient use of available resources.

Graphic
Here is the code again.

Code
SqlConnection myConn; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword;Connection Timeout=20;Persist Security Info=false"); myConn.Open(); myConn.Close();

You want to return the myConn connection to the pool. You use the Dispose method of the SqlConnection object to do this.

Graphic
The code to release the unmanaged resources is: myConn.Dispose();

Code
SqlConnection myConn; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword;Connection Timeout=20;Persist Security Info=false"); myConn.Open();

myConn.Close(); myConn.Dispose();

Returning the connection does not always close it. Garbage collector doesn't close it. To ensure the connection closes, you must explicitly mark the connection object for garbage collection. To do this, you set the connection to null.

Graphic
The code to mark the connection object explicitly for garbage collection is: myConn = null;

Code
SqlConnection myConn; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword;Connection Timeout=20;Persist Security Info=false"); myConn.Open(); myConn.Close(); myConn.Dispose(); myConn = null;

Question
You are explicitly connected to a SQL Server data source using the SqlConnection object. You have closed the connection and want to return the connection to the pool for optimum use of resources. Complete the code to do this. Code
SqlConnection cn; cn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=false;" + "User ID=myUser;Password=myPassword;Connection Timeout=20;Persist Security Info=false"); cn.Open(); INSERT THE MISSING CODE

Options: 1. cn.Dispose(); 2. cn.Fill() 3. cn.Close()

Answer

Option 1: Correct. The Dispose method of the SqlConnection object returns the connection to the pool to release the unmanaged resources for optimum use of resources. Option 2: Incorrect. The Fill method implicitly opens and closes connections. Option 3: Incorrect. The Close method only rolls back any pending transactions. It does not release the resources. Correct answer(s): 1. cn.Dispose(); You are developing an ADO.NET application to retrieve data from the EmpInfo table of the Samples SQL Server database. You find it tedious to make explicit calls to the Open and Close methods to open and close the connection to the database. So you want to connect implicitly. To implicitly connect to SQL Server, you use the DataAdapter object. To connect to the server using the DataAdapter object, you first declare the data adapter variable, myAdapter.

Graphic
The code to declare the data adapter variable myAdapter is: SqlDataAdapter myAdapter;

Code
string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); }

Next you assign a new DataAdapter object to the myAdapter variable. And you use the selectString to specify the SQL query to extract data from the EmpInfo table.

Graphic
The code to create a new DataAdapter object is: myAdapter = new SqlDataAdapter(selectString, myConn);

Code
string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); }

You also declare a data set, myDataSet, to hold the data retrieved from the EmpInfo table.

Graphic
The code to declare the data set myDataSet is: DataSet myDataSet;

Code
string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); }

Next you instantiate the myDataSet data set using the new keyword to later use it for processing the data.

Graphic
The code to instantiate the myDataSet data set is: myDataSet = new DataSet();

Code
string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); }

If the connection is not already open, after instantiating the data set object, you use the Fill method of the DataAdapter object to implicitly open and close the connection. The Fill method takes the data set variable as input to hold the retrieved data.

Graphic
Here is the code again.

Code
string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); }

If a connection is already open, you can use the connection. However, in this case, the connection is not closed automatically after the Fill method completes its functions.

Try It

You have not established a connection with the database. So you want to use the Fill method to implicitly open and close the connection. Complete the code to do this. The existing code is: string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples; Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); myAdapter.MISSING CODE; } To complete the task 1. Type Fill(myDataSet) and click the Enter key provided Type Fill(myDataSet) in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to implicitly open and close the connection using the Fill method is completed. After you call the Fill method, you can loop through the data set to read the rows in the EmpInfo table using the foreach loop.

Graphic
The code to loop through the data set is: foreach (DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); }

Code

string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); myAdapter.Fill(myDataSet); foreach (DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.ReadLine(); }

And the Fill method implicitly closes the connection after the data set has read all the rows in the EmpInfo table.

Code
string selectString; SqlConnection myConn; SqlDataAdapter myAdapter; DataSet myDataSet; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=Samples;" + "Integrated Security=true"); try { myAdapter = new SqlDataAdapter(selectString, myConn); myDataSet = new DataSet(); myAdapter.Fill(myDataSet); foreach (DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.ReadLine(); }

You now want to access data from two tables, EmpInfo and JobType. You can use multiple DataAdapter objects to do this. When using multiple DataAdapter objects using the same connection, you can increase efficiency by explicitly opening the connection once, calling each Fill method, and then explicitly closing the connection.

Code
string selectString1; string selectString2; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter1; SqlDataAdapter myAdapter2; selectString1 = "Select * from EmpInfo"; selectString2 = "Select * from JobType"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks; " + "Integrated Security=true"); try { myConn.Open(); myDataSet = new DataSet(); myAdapter1 = new SqlDataAdapter(selectString1, myConn); myAdapter2 = new SqlDataAdapter(selectString2, myConn); myAdapter1.Fill(myDataSet, "EmpInfo"); myAdapter2.Fill(myDataSet, "JobType"); myConn.Close(); }

myConn.Open(); The Open method of the SqlConnection object explicitly opens the connection. myAdapter1.Fill(myDataSet, "EmpInfo"); This is the call to the Fill method of the first DataAdapter object. It takes the data

set

variable as input to hold data from the EmpInfo table.


myAdapter2.Fill(myDataSet, "JobType"); This is the call to the Fill method of the myConn.Close(); The Close method

second DataAdapter object. It takes the data set variable as input to hold data from the JobType table. of the SqlConnection object explicitly closes the connection.

Question
You want to access vendor information from the Vendors database. You have defined a connection called myConn, but you don't want to establish an explicit connection. You have also defined a data adapter called myAdapter and a data set variable called VendorData to hold vendor details. Which method will you call to open the connection? Options: 1. myConn.Open 2. myAdapter.Fill(Vendor) 3. myAdapter.Dispose

4. myAdapter.Open

Answer
Option 1: Incorrect. You use the Open method to explicitly open a connection to the database. Option 2: Correct. If a connection is not already open, you use the Fill method to retrieve data from the data source. This method opens and closes the connection implicitly. Option 3: Incorrect. The Dispose method releases unmanaged resources held by the DataAdapter object. Option 4: Incorrect. The myAdapter object doesn't support the Open method. Moreover, you use the Open method to explicitly open a connection to the database. Correct answer(s): 2. myAdapter.Fill(Vendor)

2. Handling connection events


When using Connection objects to connect to the data source, you can use events of these objects to retrieve useful information from the data source. You can then use this information to avoid any connection deadlocks and use the connection optimally to retrieve data. The events that the Connection object of all the .NET Framework data providers include are
StateChange and The StateChange InfoMessage The InfoMessage

event triggers when the state of a connection changes, for example, when its state changes from open to closed. event triggers when a data source returns messages that do not throw exceptions. These messages are informational messages and not errors.

You can use the StateChange event to determine the original and current state of a connection. The six states in which a connection can be in are
Closed Open Broken Connecting

Executing Fetching

Note
The SqlConnection object however, supports only the Open and Closed states. You are developing an application to retrieve data from a SQL Server data source. You want to ensure the connection is open before you execute any transaction on the database. So you want to determine the current state of the connection.

Try It
To determine the current state of a connection, you first declare an event handler for the connection. This also enables you to specify the action you want to take after checking the connection state. You now want to declare an event handler for the myConn connection. Complete the code to do this. The existing code is: myConn.MISSING CODE += new StateChangeEventHandler( OnStateChange); To complete the task 1. Type StateChange and click the Enter key provided Type StateChange in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to declare the event handler for the connection, myConn is completed. After declaring the event handler, you write the code for handling the event. The StateChange event receives a StateChangeEventArgs object as an argument. You can use the properties of this object to determine the current state of the connection.

Code
myConn.StateChange += new StateChangeEventHandler(OnStateChange); private static void OnStateChange(object mySender, StateChangeEventArgs eventArgs) { }

Try It

You use the CurrentState property of the StateChangeEventArgs argument to determine the current state of the connection. You want to ensure the myConn connection is open before you execute any transaction. Complete the code to determine the connection's current state. The existing code is: myConn.StateChange += new StateChangeEventHandler( OnStateChange); private static void OnStateChange(object mySender, StateChangeEventArgs eventArgs) { Console.WriteLine(""); Console.WriteLine("The current connection state is " + "{0}.", eventArgs.MISSING CODE); Console.ReadLine(); } To complete the task 1. Type CurrentState and click the Enter key provided Type CurrentState in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to determine the current state of the connection is completed. Suppose you want to determine the state of the connection before it changed. To do this, you use the OriginalState property of the StateChangeEventArgs argument.

Graphic
The code to determine the original state of the connection is: eventArgs.OriginalState

Code
myConn.StateChange += new StateChangeEventHandler(OnStateChange); private static void OnStateChange(object mySender, StateChangeEventArgs eventArgs) { Console.WriteLine(""); Console.WriteLine(("The connection state has " + "changed from {0} to {1}."), eventArgs.OriginalState, eventArgs.CurrentState);

Console.ReadLine(); }

Question
You are connected to a database. Before performing any transactions, you want to ensure the connection is available. To do this, you use the StateChange event of the connection object. What will the event do? Options: 1. The StateChange event will trigger when the connection state changes 2. The StateChange event will return messages that do not result in an exception being thrown 3. The StateChange method will determine the original and current state of a connection 4. The SqlInfoMessageEventArgs argument of the StateChange event will determine the current and original state of the connection

Answer
Option 1: Correct. The StateChange event triggers when the connection state changes, for example from open to closed. Option 2: Incorrect. The InfoMessage event triggers when the data source returns messages that do not result in an exception being thrown. Option 3: Correct. You can use the StateChange method to determine the original and current state of a connection using the OriginalState and CurrentState properties. Option 4: Incorrect. You use the StateChangeEventArgs argument of the StateChange event to determine the current and original state of the connection. Correct answer(s): 1. The StateChange event will trigger when the connection state changes 3. The StateChange method will determine the original and current state of a connection After you have confirmed the current connection state is open, you execute the transaction to retrieve data. However, you realize the transaction speed is slow. So you want to retrieve informational messages from the SQL Server data source to determine the cause and ensure optimal retrieval of data. You can use the InfoMessage event of the SqlConnection object to capture the informational messages.

The InfoMessage event triggers when SQL Server returns these messages. With the SQL Server data source, errors with a severity level of 10 or less are informational messages. Errors returned with a severity level of 11 through 16 cause an exception to be thrown, and the InfoMessage event doesn't capture these errors. Errors greater than 20 close the connection. To determine the reason for slow transaction speed, you want to capture the errors with a severity level of 10 or less. To do this, you first declare the event handler using this code.

Code
myConn.InfoMessage += new SqlInfoMessageEventHandler(OnInfoMessage);

Next you write the procedure to handle the event.

Code
myConn.InfoMessage += new SqlInfoMessageEventHandler(OnInfoMessage); private static void OnInfoMessage(object mySender, SqlInfoMessageEventArgs eventArgs) {}

The InfoMessage event receives a SqlInfoMessageEventArgs object as an argument. And you use the Errors collection of this object to retrieve the messages.

Code
private static void OnInfoMessage(object mySender, SqlInfoMessageEventArgs eventArgs) {}

Try It
Now you want to retrieve messages from the SQL Server data source. To do this, you loop through the Errors collection using the foreach loop to access each SqlError object that is returned from the data source. The existing code is: myConn.InfoMessage += new SqlInfoMessageEventHandler( OnInfoMessage); private static void OnInfoMessage(object mySender,SqlInfoMessageEventArgs eventArgs) {

foreach( SqlError myErr in MISSING CODE) { Console.WriteLine("The {0} has received a" + " severity {1}, state {2}, " + "error number {3} on line {4}, " + "on server {5}: {6}", myErr.Source, myErr.Class, myErr.State, myErr.Number, myErr.LineNumber, myErr.Server, myErr.Message); } } To complete the task 1. Type eventArgs.Errors and click the Enter key provided Type eventArgs.Errors in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to retrieve informational messages from the SQL Server data source is completed. In the foreach loop, you can use different properties of the Errors collection to get more details about the messages. You want to print the name of the data source from where the messages came, the severity level of the errors, the state of the connection, the error and the line number from where the messages came, the name of the server, and the message texts. You use this code.

Code
foreach (SqlError myErr in eventArgs.Errors) { Console.WriteLine("The {0} has received a" + " severity {1}, state {2}, " + "error number {3} on line {4}, " + "on server {5}: {6}", myErr.Source, myErr.Class, myErr.State, myErr.Number, myErr.LineNumber, myErr.Server, myErr.Message }; } Source

The Source property specifies the name of the data source from where the messages come.
Class

The Class property specifies the severity level of the errors.


State

The State property specifies the connection state.


Number

The Number property specifies the error number from where the message came.

LineNumber The LineNumber property specifies the line number from Server The Server property specifies the name of the server. Message The Message property specifies the message text.

where the message came.

Question
While you retrieve data from SQL Server, it throws an error of severity level 6. You want to retrieve the error text. Which option will you use? Options: 1. 2. 3. 4. The SqlInfoMessageEventArgs object of the InfoMessage event The StateChangeEventArgs object of the InfoMessage event The Errors collection of the StateChange event The CurrentState property of the InfoMessage event

Answer
Option 1: Correct. You can use the Errors collection of the SqlInfoMessageEventArgs object to retrieve the warning messages. Option 2: Incorrect. You use the StateChangeEventArgs object of the StateChange event to determine the connection state. Option 3: Incorrect. The InfoMessage event provides the Errors collection that you can use to capture a warning or informational message generated by the data source. Option 4: Incorrect. You use the CurrentState property of the StateChange event to determine the current state of the connection. Correct answer(s): 1. The SqlInfoMessageEventArgs object of the InfoMessage event

Question
You are connected to a SQL Server data source and want to retrieve the message text for all the warnings that the server generates. Complete the code to do this.

Code
myConn.InfoMessage += new SqlInfoMessageEventHandler(OnInfoMessage); private static void OnInfoMessage(object mySender, SqlInfoMessageEventArgs InfoEventArgs) { foreach (SqlError myErr in InfoEventArgs.Errors) { Console.WriteLine("The {0} has received a" + " severity {1}, state {2}, " + "error number {3} on line {4}, " + "on server {5}: {6}", myErr.Source, myErr.Class, myErr.State, myErr.Number, myErr.LineNumber, myErr.Server, INSERT THE MISSING CODE); } }

Answer
You use the Message property of the Errors collection of the SqlInfoMessageEventArgs object to print the message text. Correct answer(s): 1. myErr.Message

3. Handling typical exceptions


While working with connections in ADO.NET, in addition to handling events of the Connection object, you need to handle exceptions that the data source throws when encountering errors. This ensures your ADO.NET application runs without any unexpected errors and abnormal termination. If a data source returns a warning or an error, a corresponding provider-specific exception, such as SqlException, OleDbException, or ODBCException, is thrown. When SQL Server encounters errors of severity levels from 17 through 25, such as a request to open a nonexistent database, it throws a SqlException exception.

Note
Errors of severity levels from 17 through 25 indicate software or hardware errors. When an error of severity level 17, 18, or 19 occurs, you may not be able to execute a particular statement. However, you can continue to work because the SqlConnection remains open when the severity level is 19 or less.

When the severity level of the error is 20 or greater, the server closes the SqlConnection. However, you can reopen the connection and continue to work. Whether the severity level is greater than 19 or not, you should catch all possible exceptions when closing a connection and while setting a ConnectionString property. You want to connect to the InvestmentWorks database to retrieve employee details. You have defined a connection string.

Graphic
The code to set the ConnectionString property is: ccc.myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentxxxWorks;" + "Integrated Security=true");

Code
ConnCloseClass ccc = new ConnCloseClass(); SqlCommand myCommand; string selectString; selectString = "Select * from EmpInfo"; ccc.myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentxxxWorks;" + "Integrated Security=true"); ccc.myConn.Open();

If the ConnectionString string is incorrect, the system throws an InvalidOperationException at the time you set the ConnectionString property.

Graphic
In this example, the database name is set incorrectly as InvestmentxxxWorks.

Code
ConnCloseClass ccc = new ConnCloseClass(); SqlCommand myCommand; string selectString; selectString = "Select * from EmpInfo"; ccc.myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentxxxWorks;" + "Integrated Security=true"); ccc.myConn.Open();

So you should include the assignment of ConnectionString with a Try block to catch these inadvertent errors.

Graphic
The code in the try block is: catch (InvalidOperationException ex) { Console.WriteLine("A major connection error " + "was encountered, " + ex.Message + "SQL Exception"); Console.ReadLine(); }

Code
ConnCloseClass ccc = new ConnCloseClass(); SqlCommand myCommand; string selectString; selectString = "Select * from EmpInfo"; try { ccc.myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentxxxWorks;" + "Integrated Security=true"); ccc.myConn.Open(); } catch (InvalidOperationException ex) { Console.WriteLine("A major connection error " + "was encountered, " + ex.Message + "SQL Exception"); Console.ReadLine(); } finally { ccc.myConn.Close(); }

Try It
Now you want to retrieve specific information on the error that has occurred. You use the SqlException class to do this. You use the Catch statement with a switch construct. And you also use the Number property of the SqlException object. Complete the code to retrieve the specific information about the error. The existing code is: try { ccc.myConn = new SqlConnection("Data Source=(local);"

+ "Initial Catalog=InvestmentxxxWorks;" + "Integrated Security=true"); ccc.myConn.Open(); } catch(SqlException ex) { Console.WriteLine("SQL Error opening connection: " + ex.Message + "SQL Exception"); switch(MISSING CODE) { //case statements for invalid server name, //user name, password, database default: Console.WriteLine("Unknown source of sql" + "exception"); Console.ReadLine(); break; To complete the task 1. Type ex.Number and click the Enter key provided Type ex.Number in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to retrieve information about the generated error is completed. You should also write a general Catch statement to catch all errors as exceptions thrown may not be restricted to those returned by the underlying provider.

Code
catch(Exception ex) { Console.WriteLine(ex.Message); Console.ReadLine(); }

Question
You want to retrieve data from the Pubs SQL Server database. You want to retrieve specific information on the errors. Which option will you use? Options:

1. 2. 3. 4.

OleDbException A general Exception InvalidOperationException SqlException

Answer
Option 1: Incorrect. You use the OleDbException to catch exceptions from an OLE DB data source. Option 2: Incorrect. You use a general Exception to catch all errors. Option 3: Incorrect. You use InvalidOperationException to catch exceptions while specifying the connection string. Option 4: Correct. You use the Message property of SqlException to catch exceptions from a SQL server data source and retrieve specific information about the error. Correct answer(s): 4. SqlException

Question
You are connected to a SQL Server data source. You want to define a SqlException ex to catch the exception that SQL Server throws if the server name is invalid. Complete the code to catch this exception. Code
try { ccc.myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentxxxWorks;" + "Integrated Security=true"); ccc.myConn.Open(); } catch (INSERT THE MISSING CODE) { Console.WriteLine("SQL Error opening connection: " + ex.Message + "SQL Exception"); switch(ex.Number) { //case statements for invalid user and server names default: Console.WriteLine("Unknown source of sql " + "exception"); Console.ReadLine(); break;

} }

Answer
You use the code, SqlException ex, to define the SqlException - ex. Correct answer(s): 1. SqlException ex

Summary
You can open and close connections implicitly or explicitly. The Open and Close methods of the SqlConnection object explicitly open and close the connections. The Fill method of the DataAdapter object implicitly opens and closes the connection. When a connection changes state, for example from Closed to Open, it triggers a StateChange event. And the InfoMessage event triggers when a data source returns messages that do not result in an exception being thrown. When SQL Server encounters a problem it cannot solve, it throws a SqlException exception. You can write a general Catch statement to catch all errors, and a Catch statement with a switch construct, to catch specific error types.

Table of Contents
| Print | Contents | Close |

Managing ADO.NET Connections with Visual C# 2010


Learning Objectives
After completing this topic, you should be able to

select a data provider configure a connection encrypt data and add a user instance handle connection events

Exercise Overview

In this exercise, you're required to manage ADO.NET connections with Visual C# 2010. This involves the following tasks:

selecting a data provider configuring a connection encrypting data and adding a user instance handling connection events

Select a data provider


As a .NET developer, you're building a web application for displaying the details of all employees in your company. Using Visual C# 2010, you want to manage the connection between the data source and the application.

Question
You work with a small company that uses an Access database to store employee data, and you want to use a .NET application to retrieve and manipulate the data. Your first task is to select the appropriate data provider. Which .NET Framework data provider would you choose to retrieve data from an Access database? Options: 1. 2. 3. 4. Oracle .NET Framework data provider SQL Server .NET Framework data provider OLE DB .NET Framework data provider ODBC .NET Framework data provider

Answer
Option 1: Incorrect. The .NET Framework data provider for Oracle is best suited for Oracle client software 8.1.7 or a later version. Option 2: Incorrect. The .NET Framework data provider for SQL Server can be used only if your data source is SQL Server 7.0 or a later version. Option 3: Correct. If you are accessing data from an OLE DB data source, such as an Access database, you use the OLE DB .NET Framework data provider. Option 4: Incorrect. The ODBC .NET Framework data provider is best suited for accessing data from ODBC databases. Correct answer(s):

3. OLE DB .NET Framework data provider

Question
Before proceeding to use a data provider, you want to identify the requirements for using them. Match the .NET Framework data providers with the requirements for using each. Options: 1. 2. 3. 4. SQL Server data provider ODBC data provider OLE DB data provider Oracle data provider

Targets: 1. 2. 3. 4. It is deprecated in .NET Framework 4 This data provider uses two layers for accessing a database This data provider requires the native DM to provide data access This data provider needs TDS protocol to efficiently access a data source

Answer
The Oracle data provider, System.Data.OracleClient, is supported in .NET 4. However, it is deprecated and will be removed in future releases. In order to use this provider, the namespace must be explicitly referenced. This data provider enables access to Oracle data sources through client connectivity software. The OLE DB data provider is a generic data provider that uses two layers OleDb provider and OleDb service component for accessing a data source. The .NET Framework data provider for ODBC requires the native ODBC DM to provide data access to ODBC databases. The .NET Framework data provider for SQL Server requires the SQL Server native TDS protocol to provide efficient, feature-rich data access. Correct answer(s): Target 1 = Option D Target 2 = Option C Target 3 = Option B

Target 4 = Option A

Question
Because employee data is stored in an OLE DB database, you need to specify a data provider that connects the .NET Framework to the OLE DB data source to access and modify the data. Complete the code to access and modify data from an OLE DB data source. Code
using System.Data; using System.Data.INSERT THE MISSING CODE;

Options: 1. OleDb 2. Odbc 3. SqlClient

Answer
Option 1: Correct. You use the OleDb data provider for accessing and modifying data from the OLE DB database. Option 2: Incorrect. You use the Odbc data provider for accessing and modifying data from the ODBC database. Option 3: Incorrect. You use the SqlClient data provider for accessing and modifying data from the SQL Server database. Correct answer(s): 1. OleDb

Configure a connection
After selecting the OleDb data provider, you now want to configure a connection to the database. To do this, you need to return the name of the provider for the OleDb connection and assign values to other provider-specific attributes.

Question
You want to specify an attribute specific to the OleDb provider.

Establish a connection between the OleDbConnection and the Microsoft database using the Jet provider. Code
using System.Data; using System.Data.OleDb; public class SQLProviderClass { public static void Main() { OleDbDataReader myDataReader; OleDbConnection myConn; string selectString; selectString = "Select * FROM Customers"; myConn = new OleDbConnection ("INSERT THE MISSING CODE=Microsoft.Jet.OLEDB.4.0;" +

Answer
You set the Provider attribute to Microsoft.Jet.OLEDB.4.0. This attribute establishes a connection to the Microsoft Access database using the Jet provider. Correct answer(s): 1. Provider

Question
You also want to check Windows Authentication for the OleDb database. Set the Trusted_Connection attribute to an appropriate value to enable Windows Authentication. Code
using System.Data; using System.Data.OleDb; public class SQLProviderClass { public static void Main() { OleDbDataReader myDataReader; OleDbConnection myConn; string selectString; selectString = "Select * FROM Customers"; myConn = new OleDbConnection ("Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=AccessDBSample.mdb;Trusted_Connection=INSERT THE MISSING CODE INSERT THE MISSING CODE;" +

Answer

You set the Trusted_Connection attribute to yes to check Windows Authentication for the OleDb database. Correct answer(s): 1. yes

Question
You want to ensure the security of the OleDb connection by using Windows Authentication. Assign a value to the Integrated Security attribute to ensure the OleDb connection is secure. Code
using System.Data; using System.Data.OleDb; public class SQLProviderClass { public static void Main() { OleDbDataReader myDataReader; OleDbConnection myConn; string selectString; selectString = "Select * FROM Customers"; myConn = new OleDbConnection ("Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=AccessDBSample.mdb;Trusted_Connection=yes;" + "Integrated Security = INSERT THE MISSING CODE");

Answer
To ensure the OleDb connection uses Windows Authentication, Integrated Security is set to SSPI. Correct answer(s): 1. SSPI;

Question
You want to close the connection by releasing unmanaged resources such as files, streams, and handles. Complete the code to close the connection by releasing resources. Code
public class SQLProviderClass { public static void Main()

{ OleDbDataReader myDataReader; OleDbConnection myConn; string selectString; selectString = "Select * FROM Customers"; myConn = new OleDbConnection ( "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=AccessDBSample.mdb;" + "Trusted_Connection=yes;" + "Integrated Security = SSPI;"); myConn.Open(); OleDbCommand myCommand; myCommand = new OleDbCommand(selectString, myConn); myDataReader = myCommand.ExecuteReader(); while(myDataReader.Read()) { Console.WriteLine(myDataReader[0] + " " + myDataReader[1] + " " + myDataReader[2]); } myConn.INSERT THE MISSING CODE();

Answer
You use the Dispose method to close a connection by releasing unmanaged resources such as files, streams, and handles. Correct answer(s): 1. Dispose

Use additional connection attributes


Suppose you have configured a connection to a SQL Server 2008 instance. You now want to use a SqlClient-specific data provider to encrypt the database connection when you don't have a valid server certificate.

Question
You now want to encrypt a database connection even when a server certificate is not created. Set a value of the TrustServerCertificate attribute that indicates the database connection is encrypted. Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() {

SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection ("Data Source=(local);Initial Catalog=Samples;Integrated Security=true;" + "Connection Timeout=20;Encrypt=true;TrustServerCertificate=INSERT THE MISSING CODE INSERT THE MISSING CODE;

Answer
If the TrustServerCertificate and Encrypt attributes are set to true, you can encrypt a database connection even without a valid server certificate. Correct answer(s): 1. true After encrypting application data, you want to allow users who don't have administrative privileges to run a local version of SQL Server Express. To do this, you want to add a user instance.

Question
Complete the C# code to have a connection string to a SQL Server Express 2008 database that uses a user instance. Complete the code to do this. Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection ("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Encrypt=True;TrustServerCertificate=True; " + "Persist Security Info=false;User Instance=INSERT THE MISSING CODE INSERT THE MISSING CODE");

Answer

You set the connection string keyword in SqlClient to true to connect to a SQL Server database that is a user instance. Correct answer(s): 1. true After encrypting application data and adding a user instance, you now want to handle connection events.

Handle connection events


Suppose you have defined and given properties to a SQL connection. You now want to ensure your code handles possible errors that may arise when building the application.

Question
Type the name of the event that is thrown when the data source generates a warning or an informational message for the user. Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=" + ".\\SQLEXPRESS;Initial Catalog=Samples;" + "Integrated Security=True;" + "Connection Timeout=20;" + "Persist Security Info=false;"); myConn.INSERT THE MISSING CODE += new

Answer
The InfoMessage event is displayed after a warning or an informational message is generated for the user. Correct answer(s): 1. InfoMessage

Question

The event argument of InfoMessage is incomplete. Type the code that completes the event argument. Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=" + ".\\SQLEXPRESS;Initial Catalog=Samples;" + "Integrated Security=True;" + "Connection Timeout=20;" + "Persist Security Info=false;"); myConn.InfoMessage += new INSERT THE MISSING CODE(OnInfoMessage);

Answer
The InfoMessage event receives a SqlInfoMessageEventArgs object as an argument. Correct answer(s): 1. SqlInfoMessageEventHandler

Question
Type the name of the event that is triggered when a connection opens or closes. Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection("Data Source=" + ".\\SQLEXPRESS;Initial Catalog=Samples;" + "Integrated Security=True;" + "Connection Timeout=20;" + "Persist Security Info=false;"); myConn.InfoMessage += new SqlInfoMessageEventHandler(OnInfoMessage);

myConn.INSERT THE MISSING CODE += new StateChangeEventHandler(OnStateChange);

Answer
The event that is triggered when a connection changes from open to closed or vice versa is StateChange. Correct answer(s): 1. StateChange

Question
You want to cycle through the warnings generated by the data source, which are stored in the SqlInfoMessageEventArgs argument. The se variable represents an individual warning. Complete the definition of the se variable. Code
mySqlCommand.CommandText = "RAISERROR('My test Error', 10, 2)"; Console.WriteLine("Raise a SQL error:"); mySqlCommand.ExecuteNonQuery(); Console.WriteLine(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine("Here is the table data:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader(); Console.WriteLine(); Console.WriteLine("Close the connection:"); myConn.Close(); Console.ReadLine(); } private static void OnInfoMessage(object sender, SqlInfoMessageEventArgs args) { INSERT THE MISSING CODE se;

Answer
The se variable points to the SqlClient.SqlError class. Correct answer(s): 1. SqlError

Question

Specify the collection name that stores the individual warnings in args. Code
SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine("Here is the table data:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader(); Console.WriteLine(); Console.WriteLine("Close the connection:"); myConn.Close(); Console.ReadLine(); } private static void OnInfoMessage(object sender, SqlInfoMessageEventArgs args) { foreach(SqlError se in args.INSERT THE MISSING CODE)

Answer
The Errors collection stores the individual warnings in args. Correct answer(s): 1. Errors

Question
Type the property you access to read the current state of the connection. Code
foreach (SqlError se in args.Errors) { Console.WriteLine("A severity {0}, state {1} " + "error has occurred: {2}", se.Class, se.State, se.Message); } } protected static void OnStateChange(object sender, StateChangeEventArgs args) { Console.WriteLine("The Connection state has " + "changed from {0} to {1}.", args.OriginalState, args.INSERT THE MISSING CODE); } }

Answer

To read the current state of a connection, you access its CurrentState property. Correct answer(s): 1. CurrentState You have successfully selected a data provider, configured a new connection, and used the attributes of the ConnectionString class to encrypt data and add a user instance. You have also added the code to handle connection events.

Table of Contents
| Print | Contents | Close |

Using ADO.NET Commands and DataReaders With C# 2010


Learning Objectives
After completing this topic, you should be able to

recognize the steps for creating a command to retrieve a single value from a data source recognize the uses of DataReader properties and methods and the steps for retrieving a result set from a database name the different ways of processing rows of read-only data identify the code for using ExecuteXmlReader for returning an XML stream

1. Working with Command objects


objects an important component of any data application are used to issue queries against a database. Command objects are used after you've established a connection to the database. You can use Command objects to add, update, or delete some data.
Command Command

objects can be used to execute SQL commands or stored procedures on the database.

You can use Command objects in conjunction with DataReaders for connected access, or with DataSets and DataAdapters, as part of a disconnected access strategy. You use a Command object in conjunction with a DataReader object which acts like a readonly, forward-only cursor to read once through single or multiple result sets.

A single Command object also allows you to perform the SQL SELECT, INSERT, DELETE, and UPDATE operations directly on rows of data in a database table. When used for disconnected access with a DataAdapter, four different types of Command objects are provided. The properties of the DataAdapter allow you to perform the SELECT, INSERT, UPDATE, and DELETE operations on rows of data in a database table.

Question
Identify the uses of ADO.NET Command objects. Options: 1. 2. 3. 4. Allows you to update data directly in a data source Enables you to establish a connection to the database Retrieves data and displays it directly to the user Provides read and write access to data using a DataReader object

Answer
Option 1: Correct. You can use Command objects directly to access data and to update, modify, or delete data. Option 2: Incorrect. Command objects are used after you've established a connection to the database. You can use Command objects to modify the database. Option 3: Correct. When you use a Command object in a connected access strategy, you can retrieve data and then display it directly to the user. Option 4: Incorrect. Because a DataReader object acts like a read-only, forward-only cursor, using it in conjunction with a Command object provides you read-only access to data. Correct answer(s): 1. Allows you to update data directly in a data source 3. Retrieves data and displays it directly to the user Each separate .NET data provider within the .NET Framework includes a Command object that derives from DbCommand SqlCommand, OleDbCommand, and OdbcCommand. These objects provide different methods to execute commands, which are based on the type of command and the value returned. These methods are
ExecuteScalar()

The ExecuteScalar() method returns a result set that contains the value of the first column of the first row. Essentially, it returns a single value rather than a table or data stream.
ExecuteXmlReader() The ExecuteXmlReader() object executes a SQL query and returns the result in an XmlReader instance. This method is implemented by the SqlCommand object only. ExecuteNonQuery(), and The ExecuteNonQuery() method executes a command that doesn't return rows, for instance an Insert command. ExecuteReader() The ExecuteReader() method enables you to retrieve a DataReader object from the

data source to enable read-access to data.


Command

objects include the following properties:

Connection The Connection property can be used to get or to set the connection object that is used. CommandText The CommandText property is used to get or set the SQL statement or stored procedure

that is to be executed. CommandType, and The CommandType property is used to get or set a value determining how the CommandText property is understood. The default is Text but will be StoredProcedure if a stored procedure is being used.
Transaction The Transaction property is Command object executes.

used to get or set the transaction object within which the

You can create a command by using an appropriate Command constructor or by using methods that create the object for you. Suppose your company maintains employee data on a computer running SQL Server. You want to retrieve the number of records in the Sample table. To do this, you first create a connection to the SQL Server using a SqlConnection object.

Graphic
The code to create a connection to SQL Server using a SqlConnection object is: SqlConnection myConn;

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass

{ public static void Main() { int numRecords; SqlConnection myConn;

Then you specify a SQL query to retrieve data from the table to a new string. In this example, you return the result of an aggregate function COUNT(*) from the table.

Graphic
This is the code to return the result of an aggregate function COUNT(*) from Sample Table. string selectString; selectString = "Select Count(*) from Sample";

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from Sample";

After creating the SqlConnection object, you open the connection. To do this, you instantiate the SqlConnection object by specifying a new SqlConnection string.

Graphic
The code to instantiate the SqlConnection object is: myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open();

Code
int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from Sample"; myConn = new SqlConnection("Data Source=(local);Initial

Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open();

Try It
You now declare a Command object. The existing code is: int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from Sample"; myConn = new SqlConnection( "Data Source=(local);Initial Catalog=Samples;" + "Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); MISSING CODE myCommand; To complete the task 1. Type SqlCommand and click the Enter key provided Type SqlCommand in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The Command object to return queries from the SQL Server is created. There are four overloaded constructors you can use to create the Command object:

Code
int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from Sample"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn);

a no-argument constructor

a constructor that takes the text of the query or the name of a stored procedure a constructor that takes either the query text or the stored procedure and the connection object to use a constructor that takes the query text or the stored procedure and a connection object, and also specifies a transaction object

In this case, the SqlCommand constructor specifies the SQL query string and the connection object.

Graphic
the code specifying the SQL query string is: myCommand = new SqlCommand(selectString, myConn)

Code
int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from Sample"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn);

You can also create a Command object associated with a specific connection by using the CreateCommand() method. And you can use an existing Command object's Connection, Transaction, or CommandText property to specify a connection, SQL query, or transaction for the object.

Graphic
The code to create a Command object is: SqlCommand myCommand; myCommand = myConn.CreateCommand(); myCommand.CommandText=selectString; myCommand.Connection=myConn;

Code

int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from Sample"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = myConn.CreateCommand(); myCommand.CommandText=selectString; myCommand.Connection=myConn;

Try It
Suppose you used a constructor to create the Command object and now want to return the number of records in the table. To do this, you use a method of the Command object that returns only a single value. The existing code is: SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); numRecords = myCommand.MISSING CODE(); To complete the task 1. Type ExecuteScalar() and click the Enter key provided Type ExecuteScalar() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The ExecuteScalar() method is entered to return the first row from the first column of the employee database. Finally, you use the Close() method to close the SqlConnection.

Graphic
The code to close a SqlConnection is Close().

Code
SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); numRecords = int.Parse(myCommand.ExecuteScalar().ToString()); Console.WriteLine(numRecords);

Console.ReadLine(); myConn.Close(); } }

Note
You can also use the ExecuteScalar() method to return a column value from a row, for instance the employee ID from an Employee table, using the SQL query Select EmpID from Employee.

Question
You want to retrieve only the first customer ID from the customer database stored on SQL Server. Arrange the steps in the correct sequence to create a SqlCommand object that retrieves a single value from SQL Server. Assume that the SQL query string is assigned to a String variable. Options: 1. 2. 3. 4. Specify the ExecuteScalar() method Create the Command object using the CreateCommand() method or a constructor Specify a SQL query Close the connection

Answer
Correct answer(s): Specify a SQL query is ranked as the first step. You specify a SQL query that is used by the Command object to retrieve data from the customer table. Create the Command object using the CreateCommand() method or a constructor is ranked as the second step. Creating a Command object using the specified connection and SQL query string enables you to issue queries against a database. Specify the ExecuteScalar() method is ranked as the third step. The ExecuteScalar() method enables you to return a single value from the database. Close the connection is ranked as the fourth step. The Close() method enables you to close the SqlConnection.

Question
You're retrieving a single value from the employees table on SQL Server and have already created and configured a SqlConnection.

Enter the constructor to create the object used to issue queries against the database. Code
public class SQLProviderClass { public static void Main() { int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from EmployeesTable"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); INSERT THE MISSING CODE myCommand;

Answer
You use the SqlCommand constructor to create a SqlCommand object to issue queries against the employees table. Correct answer(s): 1. SqlCommand

Question
You want to execute a command so only the value in the first column of the first row is returned. Complete the code using an appropriate method. Code
int numRecords; SqlConnection myConn; string selectString; selectString = "Select Count(*) from SampleTable"; myConn = new SqlConnection("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); numRecords =int.Parse(myCommand.INSERT THE MISSING CODE().ToString());

Answer

You use the ExecuteScalar() method of the Command object to return a single value from a database. Correct answer(s): 1. ExecuteScalar

2. Using DataReader methods and properties


Once you've connected to a database and executed a query, you need to access the returned result set. A DataReader object implements the System.Data.IDataReader interface and provides fast, forward-only access to your data. You can use the DataReader object to retrieve a read-only stream of data returned from the Command object. It retrieves data on a per-row basis, which means that it reads one row at a time as it loops through a result set. Unlike a DataSet that stores your data in an in-memory database model, the DataReader object stores the results in the network buffer until you return the result set. This is useful when you want to return a single table but don't want the overhead of a multitable, in-memory database. You can use a DataReader object to retrieve data from different types of databases, including OLE DB, SQL Server and ODBC. The DataReader object is a core component of each of the available .NET Framework data providers. A SqlDataReader, for example, is used to provide direct read-only access to SQL Server content.
DataReader Close()

objects include methods such as

The Close() method closes the DataReader object and must be invoked to release the connection when youve finished reading the data.
GetName()

You use the GetName() GetDataTypeName()


Read()

method to retrieve the name of a column or row.

To retrieve the data type name for values from a column, you use the GetDataTypeName() method with an ordinal column index value. Using the Read() method, you move the row cursor to the next row in the data stream. This method is called before the first row of data.
GetOrdinal() The GetOrdinal()

method gets the numeric position of a column based on the column

name specified.
GetValues() The GetValues()

method lets you save the values of the current row in an array. The number of fields saved depends on the size of the array passed to this method. IsCommandBehavior(), and

You use the IsCommandBehavior() method to find if the CommandBehavior you chose matches that of the SqlDataReader object.
IsDBNull() The IsDBNull()

method is invoked when you want to find if a column is null and without default values.

The SqlDataReader object uses a number of properties, including


Connection The Connection

property of the DataReader method retrieves the SqlConnection associated with the SqlDataReader object.

FieldCount The FieldCount property returns the number of columns in the current row. IsClosed The IsClosed property returns the value True if the SqlDataReader object is closed. HasRows, and To check if SqlDataReader contains one or more rows, you use the HasRows property. RecordsAffected

You can check if records were modified by an executed SQL query using the RecordsAffected property. You create a SqlDataReader object using the Command object's ExecuteReader() method. This method creates a SqlDataReader object by sending the CommandText to the SqlConnection. This method also has a version that takes a CommandBehavior enumeration argument. This argument describes the results of a query and the effect on the database being queried.

Note
You can't directly use a constructor to create a SqlDataReader object. The CommandBehavior enumeration takes different values that can be combined bitwise. Some of the commonly used CommandBehavior values are CloseConnection, Default, KeyInfo, and SingleResult.

Question
You retrieve data from a SQL Server using the SqlDataReader object. Which method do you use when you've completed reading data from the data source? Options: 1. GetOrdinal() 2. Close() 3. IsCommandBehavior()

4. IsDBNull()

Answer
Option 1: Incorrect. The GetOrdinal() method returns the ordinal position of the named column. Option 2: Correct. The Close() method closes the open connection used by the SqlDataReader object, and is used once you finish reading data. Option 3: Incorrect. The IsCommandBehavior() method is invoked to determine whether the selected CommandBehavior matches that of the SqlDataReader object. Option 4: Incorrect. You use the IsDBNull() method to check whether a column is null and contains no default value. Correct answer(s): 2. Close() The ADO.NET SqlDataReader lets you access a SQL Server data source directly. You can create a SqlDataReader by simply calling the ExecuteReader() method on a SqlCommand object. Suppose you are developing a new application payroll system for your company. For processing salaries, the company maintains employee details on a SQL Server database. You want to use the SqlDataReader object to generate a result set containing the employee details. To do this, you first create a SqlCommand object, associating it with a SqlConnection object and a SQL query.

Graphic
The code to create a SqlCommand object and associate it with a SqlConnection object is: myConn = new SqlConnection ("Data Source=(local);Initial Catalog=Samples;" +"Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn);

Code

using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select * from SampleTable"; myConn = new SqlConnection ("Data Source=(local);Initial Catalog=Samples;" +"Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn);

Try It
To create the SqlDataReader object that reads a forward-only stream of rows from the database, you specify the Command object's ExecuteReader() method. The existing code is: SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine ("Here is the entire table using " + "CommandBehavior.SingleRow"); Console.WriteLine ("and CommandBehavior.CloseConnection:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = MISSING CODE; To complete the task 1. Type myCommand.ExecuteReader() and click the Enter key provided Type myCommand.ExecuteReader() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The ExecuteReader() method is specified as the command. The ExecuteReader() method can use the CommandBehavior enumeration, which has a number of values that can be used to control the query.

In this example, you want to retrieve a single row only, so you pass the CommandBehaviour.SingleRow value and the query is optimized to return a single row.

Graphic
The code to return a single row using the CommandBehavior property is: CommandBehavior.SingleRow

Code
SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine ("Here is the entire table using " + "CommandBehavior.SingleRow"); Console.WriteLine ("and CommandBehavior.CloseConnection:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader (CommandBehavior.SingleRow | CommandBehavior.CloseConnection);

You also add the CommandBehavior.CloseConnection value to close the connection once SqlDataReader closes.

Graphic
The value used to close the connection is: CommandBehavior.CloseConnection

Code
SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine ("Here is the entire table using " + "CommandBehavior.SingleRow"); Console.WriteLine ("and CommandBehavior.CloseConnection:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader (CommandBehavior.SingleRow | CommandBehavior.CloseConnection);

To iterate through the employee data one row at a time, you use the Read() method of the DataReader object. When the Read() method is called the first time, the SqlDataReader object moves to the first row in the result set. For subsequent calls, the object moves to the next row. The method returns a Boolean value to indicate whether the SqlDataReader object has another

row available. If the Read() method returns True, the SqlDataReader object moves to the next available row. When the method returns False, it indicates that youve reached the end of the query results.

Graphic
The code to use the Read method of the DataReader object is: myDataReader.Read()

Code
SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine ("Here is the entire table using " + "CommandBehavior.SingleRow"); Console.WriteLine ("and CommandBehavior.CloseConnection:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader (CommandBehavior.SingleRow | CommandBehavior.CloseConnection); while(myDataReader.Read()) { Console.WriteLine(myDataReader[0] + " " + myDataReader[1] + " " + myDataReader[2]); }

Try It
You now want to use the FieldCount property to determine how many fields your query returned. The existing code is: while( myDataReader.Read()) { Console.WriteLine(myDataReader[0] + " " + myDataReader[1] + " " + myDataReader[2]); } Console.WriteLine(); Console.WriteLine("Number of columns in table:" + Convert.ToString(MISSING CODE)); To complete the task 1. Type myDataReader.FieldCount and click the Enter key Type myDataReader.FieldCount in the MISSING CODE field.

The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The FieldCount property is entered. If you need to determine the name of a field, you can call the GetName() method of the SqlDataReader object. This method accepts an integer, specifying the fields ordinal value, and returns the name in a string.

Graphic
The code to use the GetName() method to determine the name of the first three fields is: Console.WriteLine("The column names are: " + myDataReader.GetName(0) + ", " + myDataReader.GetName(1) + ", " + myDataReader.GetName(2));

Code
while(myDataReader.Read()) { Console.WriteLine(myDataReader[0] + " " + myDataReader[1] + " " + myDataReader[2]); } Console.WriteLine(); Console.WriteLine("Number of columns in table:" + Convert.ToString(myDataReader.FieldCount)); Console.WriteLine("The column names are: " + myDataReader.GetName(0) + ", " + myDataReader.GetName(1) + ", " + myDataReader.GetName(2));

If you need to determine the data type for a field, you can call the GetDataTypeName() method. The method accepts the fields ordinal value as an integer, and it returns a string with the name of the fields data type in the database.

Code
Console.WriteLine("The column names are: " + myDataReader.GetName(0) + ", " + myDataReader.GetName(1) + ", " + myDataReader.GetName(2)); Console.WriteLine("Data Type of the first column is: " + myDataReader.GetDataTypeName(0)); Console.WriteLine("Data Type of the second column is: " + myDataReader.GetDataTypeName(1)); Console.WriteLine("Data Type of the third column is: " + myDataReader.GetDataTypeName(2));

Try It
No other object can use the connection myDataReader is using until the DataReader object is closed, so you want to close the myDataReader object. The existing code is: Console.WriteLine("Data Type of the first column is: " + myDataReader.GetDataTypeName(0)); Console.WriteLine("Data Type of the second column is: " + myDataReader.GetDataTypeName(1)); Console.WriteLine("Data Type of the third column is: " + myDataReader.GetDataTypeName(2)); MISSING CODE; To complete the task 1. Type myDataReader.Close() and click the Enter key Type myDataReader.Close() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The Close()method is used to close the DataReader object. Finally, you verify that the DataReader object is closed by checking its IsClosed property.

Graphic
In this code, the myDataReader object is closed using the myDataReader.IsClosed property.

Code
myDataReader.Close(); if(myDataReader.IsClosed) { Console.WriteLine("DataReader is closed"); } if(myConn.State == ConnectionState.Closed) { Console.WriteLine("Connection is closed"); } else { Console.WriteLine("Connection is still open"); }

Console.ReadLine();

You also check that the SqlConnection has been automatically closed once the SqlDataReader is closed, as you have specified the CommandBehavior.CloseConnection value.

Graphic
In this code the myDataReader object is automatically closed using ConnectionState.Closed property.

Code
myDataReader.Close(); if(myDataReader.IsClosed) { Console.WriteLine("DataReader is closed"); } if(myConn.State == ConnectionState.Closed) { Console.WriteLine("Connection is closed"); } else { Console.WriteLine("Connection is still open"); } Console.ReadLine();

You can also use DataReader to get multiple result sets from the database. This helps when you want to query the database with more than two queries at the same time without spending time processing rows of data. Suppose you want to return two result sets from the SQL Server one with employee ID and name and the other with employee name and department ID.

Code
public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select EmpID, EmpName from SampleTable; Select EmpName, DeptID from SampleTable"; myConn = new SqlConnection ("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;"

+ "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection);

To do this, you specify a SQL query to retrieve data from the employee table to a new string. In this example, the SelectString is specified, which includes two SELECT statements.

Graphic
The code to retrieve two result sets from the SampleTable is: selectString = "Select EmpID, EmpName from SampleTable; Select EmpName, DeptID from SampleTable";

Code
public class SQLProviderClass { public static void Main() { SqlConnection myConn; string selectString; selectString = "Select EmpID, EmpName from SampleTable; Select EmpName, DeptID from SampleTable"; myConn = new SqlConnection ("Data Source=(local);Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection);

With the SQL query specified, you create a SqlCommand and a new SqlDataReader object. You can also use the HasRows property to avoid any unwanted error messages due to null or missing values in the retrieved data. The HasRows property determines if the SqlDataReader contains one or more rows. It returns a Boolean value of True if it does and False if it does not.

Graphic

The code to use the HasRows() method is: myDataReader.HasRows

Code
SqlDataReader myDataReader; myDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection); Console.WriteLine("Two result sets:"); Console.WriteLine(); if(myDataReader.HasRows) { while(myDataReader.Read()) { Console.WriteLine(myDataReader[0] + " " + myDataReader[1]); } Console.WriteLine(); Console.WriteLine();

Try It
When working with multiple result sets, you want to step through the result sets that are returned. So you decide to use the NextResult() method to move to the next result set. The existing code is: Console.WriteLine("Two result sets:"); Console.WriteLine(); if( myDataReader.HasRows) { while( myDataReader.Read()) { Console.WriteLine(myDataReader[0] + " " + myDataReader[1]); } Console.WriteLine(); Console.WriteLine(); MISSING CODE; To complete the task 1. Type myDataReader.NextResult() and click the Enter key Type myDataReader.NextResult() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The method to advance the myDataReader object to the next result set is entered. The NextResult() method is used when you send a group of SQL queries to the database all at once, and the resulting DataReader object contains more than one result set to scan. This method positions the DataReader object at the beginning of the next result set. Finally, you close the DataReader object using the Close() method.

Code
myDataReader.NextResult(); } Console.WriteLine(); myDataReader.Close(); if(myDataReader.IsClosed) { Console.WriteLine("DataReader is closed");} if(myConn.State == ConnectionState.Closed) { Console.WriteLine("Connection is closed");} else { Console.WriteLine("Connection is still open"); } Console.ReadLine();

Question
You want to generate a result set for which you've created a SqlDataReader object to read a forward-only stream of rows from the product database. Which method of the DataReader object would you use to iterate through the data one row at a time? Options: 1. 2. 3. 4.
ExecuteReader() Read() FieldCount() NextResult()

Answer
Option 1: Incorrect. ExecuteReader() is a method of the Command object and is used to create a SqlDataReader object. Option 2: Correct. The Read() method iterates through the database one row at a time. When this method is called the first time, the SqlDataReader object moves to the first row in the result set and subsequently to the next row.

Option 3: Incorrect. The FieldCount() property determines the number of columns in the current row retrieved from a query. Option 4: Incorrect. The NextResult() method advances the reader to the next result set and is used when the resulting DataReader object contains more than one result set to scan. Correct answer(s): 2. Read()

Question
You're creating a new SqlDataReader object to read a forward-only stream of rows from the customer database on a SQL Server. Complete the code to create the SqlDataReader object. Code
SqlConnection myConn; string selectString; selectString = "Select * from CustomerTable"; myConn = new SqlConnection ("Data Source=(local);" + "Initial Catalog=Samples;Integrated Security=True;" + "Connection Timeout=20;Persist Security Info=false"); myConn.Open(); SqlCommand myCommand; myCommand = new SqlCommand(selectString, myConn); Console.WriteLine("Here is the entire table:"); Console.WriteLine(); SqlDataReader myDataReader; myDataReader = INSERT THE MISSING CODE();

Answer
You use the Command object's ExecuteReader() method to create a SqlDataReader object. Correct answer(s): 1. myCommand.ExecuteReader

3. Using ExecuteXmlReader
At times, you may want to access database table data in the XML format. You can do this by using the ExecuteXmlReader() method of a Command object. This method executes a query containing a FOR XML clause, and returns an instance of the System.Xml.XmlReader type. This method determines the type of command to execute by using the CommandText property,

and then builds an XmlReader object. The CommandText property can specify either a TransactSQL statement or a stored procedure. When you use ExecuteXmlReader() with SQL Server 2005 or later, you can retrieve an XML result set that contains a single row and single column. Suppose your company is planning to introduce a promotional offer for existing customers. For this, it requires the list of customers, which is stored in a SQL Server 2008 database. You want to execute a query against the database and retrieve the customer list in an XML stream. To do this, you need to invoke the ExecuteXmlReader() method, using a SELECT FOR XML command against the SQL Server database. You begin by creating a new SqlConnection and a new SqlCommand. In this instance, you use the SqlCommand constructor with no arguments and associate the two, using the Connection property of the Command object.

Code
using System; using System.Data; using System.Data.SqlClient; using System.Xml; class Module1 { void static void Main() { try { SqlConnection conn = new SqlConnection(); conn.ConnectionString = "server=(local)\\SQLEXPRESS;" + "database=MyDatabase;" + "Integrated Security=SSPI;"; conn.Open(); SqlCommand comd = new SqlCommand(); comd.Connection = conn;

Once the SqlCommand instance is created and the connection opened, you use the CommandText property to associate the SQL query with the command. You specify the FOR XML clause in the SQL query to return the results as XML. The mode of the XML can be AUTO, RAW, or EXPLICIT this setting determines the format of the XML returned. In this case, you select the records from the customers table to return as an XML stream. You specify the mode as AUTO to return the XML data in a simple nested XML tree. When the XMLDATA option is included in the statement, it results in the schema being included in the result set.

Graphic

The code to select the records from the customers table is: "SELECT * FROM customers FOR XML AUTO, XMLDATA"

Code
SqlCommand comd = new SqlCommand(); comd.Connection = conn; comd.CommandText = "SELECT * FROM customers FOR XML AUTO, XMLDATA";

Note
You can also associate the connection and SQL query with the Command object when it is being created by using an overloaded version of the constructor.

Try It
You want to send the specified CommandText to the SqlConnection object and return an XmlReader. To do this, you use the ExecuteXmlReader() method of the Command object. The existing code is: comd.CommandText = "SELECT * FROM customers FOR XML AUTO, XMLDATA"; XmlReader reader; reader = comd.MISSING CODE; To complete the task 1. Type ExecuteXmlReader() and click the Enter key provided Type ExecuteXmlReader() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The method to execute a query containing the specified CommandText is specified. The ExecuteXmlReader() method returns an instance of XmlReader. Similar to a data reader, the XmlReader object exposes a Read() method that reads the next node in the XML stream.

Code
reader.Read(); while(reader.ReadState != ReadState.EndOfFile) { Console.WriteLine(reader.ReadOuterXml()); }

When reading the XML stream, you can use the ReadOuterXml() method to read the content. You can also read the markup of each node and its children.

Code
reader.Read(); while(reader.ReadState != Xml.ReadState.EndOfFile) { Console.WriteLine(reader.ReadOuterXml()); } Console.ReadLine(); } catch(Exception ex) { Console.WriteLine(ex); } }

Question
You're retrieving data from a product database on SQL Server in the XML format. Which method do you use to send the specified query containing a FOR XML clause to the SqlConnection object? Code
SqlConnection conn = new SqlConnection(); conn.ConnectionString = "server=(local)\\SQLEXPRESS;database=MyDatabase;" + "Integrated Security=SSPI;"; conn.Open(); System.Data.SqlClient.SqlCommand comd = new System.Data.SqlClient.SqlCommand(); comd.Connection = conn; comd.CommandText = "SELECT * FROM product "+ " FOR XML AUTO, XMLDATA"; XmlReader reader; reader = INSERT THE MISSING CODE; reader.Read(); while(reader.ReadState != ReadState.EndOfFile) { Console.WriteLine(reader.ReadOuterXml()); }

Options: 1. comd.ExecuteXmlReader() 2. reader.Read() 3. reader.ReadOuterXml()

Answer

Option 1: Correct. The Command object's ExecuteXmlReader() method executes a query containing a FOR XML clause and sends it to the SqlConnection object. Option 2: Incorrect. The Read() method is used to iterate through the employee data one row at a time. It enables you to read the next node in the XML stream. Option 3: Incorrect. The ReadOuterXml() method allows you to read the content and the markup of each node and its children in the XML stream. Correct answer(s): 1. comd.ExecuteXmlReader()

Summary
ADO.NET 4 provides the Command and DataReader objects in addition to the Connection objects. Command objects are used to execute stored procedures or SQL commands against a database. You can use a Command object's ExecuteScalar method to return a single value from a data source.
DataReader

objects act like a read-only, forward-only cursor, and allow you to read one row of a result set at a time. DataReaders contain several methods and properties to manipulate a result set Close(), Read(), Connection, IsCommandBehavior(), FieldCount, and HasRows. The ExecuteXmlReader() method is used to query a SQL Server database and return data in an XML stream.

Table of Contents

Working with ADO.NET Commands Using Visual C# 2010


Learning Objectives
After completing this topic, you should be able to

work with Command objects use a DataReader object retrieve a single value from a data source retrieve an XML data stream

Exercise Overview
In this exercise, you're required to work with ADO.NET Commands and DataReaders using Visual C# 2010. This involves the following tasks:

creating a Command object using a DataReader object retrieving a single value from a data source retrieving an XML data stream

Creating a Command object


Your company maintains its employee database on SQL Server. The HR Department of the company wants to install new software for all HR employees, so it needs a complete employee list. You need to retrieve the first and last names of all the employees in the HR department to compile this list. You have already declared a SqlConnection object to connect to the RedRockMountainCycling database. You also created a SQL query to return the first and last names of all employees from the HR.EMP table. You now want to issue the query against the database by creating a Command object.

Question
Enter the proper class name to declare the object used to issue queries against the database. Code
public partial class Form1 : Form { private void btnStart_Click(System.object sender, System.EventArgs e) { Regex objPattern = new Regex("[^0-9]"); if(objPattern.IsMatch(txtNum.Text)) { return; } lstEmployees.Items.Clear(); SqlConnection conn; conn = new SqlConnection("Data Source=win-uxt\\SQLExpress;" +"Initial Catalog=REDRockMountainCycling;Integrated Security=true"); string sqlQuery; sqlQuery = "select first_name + ' ' +"

+ "last_name as name from hr.emp "; INSERT THE MISSING CODE cmdEmployees;

Answer
You use the SqlCommand constructor to create a SqlCommand object to issue queries against the employees table. Correct answer(s): 1. SqlCommand

Using a DataReader object


You've connected to a database and queried its result set. You now want to create a SqlDataReader object so that you can read the data as a forward-only stream. You've used the CommandBehavior enumeration argument to close the DataReader connection.

Question
Complete the code to create the SqlDataReader object. Code
try { cmdEmployees = new SqlCommand(); cmdEmployees.CommandType = CommandType.Text; cmdEmployees.CommandText = sqlQuery; cmdEmployees.Connection = conn; cmdEmployees.Connection.Open(); drList = cmdEmployees.INSERT THE MISSING CODE(CommandBehavior.CloseConnection);

Answer
You use the Command object's ExecuteReader() method to create the SqlDataReader object that reads a forward-only stream of rows from the database. Correct answer(s): 1. ExecuteReader You now want to manipulate the data from the employee table. For this, you decide to use various DataReader methods and properties in the code.

Question

You want to retrieve the number of columns in the last row. Complete the code to specify the number of columns in the last row of the result set. Code
try { cmdEmployees = new SqlCommand(); cmdEmployees.CommandType = CommandType.Text; cmdEmployees.CommandText = sqlQuery; cmdEmployees.Connection = conn; cmdEmployees.Connection.Open(); drList = cmdEmployees.ExecuteReader(CommandBehavior.CloseConnection); string strColumns, strData; strColumns =string.Empty; strData =string.Empty; int i , limit; limit = drList.INSERT THE MISSING CODE - 1;

Answer
You use the FieldCount property to specify the number of columns in the current row that you want the query to retrieve. Correct answer(s): 1. FieldCount

Question
You also want to determine the column name that is currently being retrieved. Complete the code to retrieve the name of the column being retrieved. Code
drList = cmdEmployees.ExecuteReader(CommandBehavior.CloseConnection); string strColumns, strData; strColumns = string.Empty; strData = string.Empty; int i , limit; limit = drList.FieldCount - 1; for(i = 0; i<=limit; i++) { strColumns += drList.INSERT THE MISSING CODE(i); }

Answer

You use the GetName() method to retrieve the name of a column or row. Correct answer(s): 1. GetName

Question
You want to read the employee data from the HR.EMP table one row at a time. Complete the code to read the employee data one row at a time. Code
cmdEmployees.ExecuteReader(CommandBehavior.CloseConnection); string strColumns, strData; strColumns = string.Empty; strData = string.Empty; int i , limit; limit = drList.FieldCount - 1; for( i = 0; i<=limit; i++) { strColumns += drList.GetName(i); if( i < limit) { strColumns += ", "; } } this.lstEmployees.Items.Add(strColumns); while(drList.INSERT THE MISSING CODE())

Answer
Using the Read() method, you iterate through the employee data one row at a time in the result set. Correct answer(s): 1. Read

Question
You also want to determine whether the column is without values. Enter a DataReader method that enables you to determine if the column contains null values. Code
while( drList.Read()) { for(i = 0;i<limit; i++)

{ if(drList.INSERT THE MISSING CODE(i)) { strData += "--NULL--"; } else { strData += drList.GetValue(i).ToString(); } if(i < limit) { strData += ", "; } } lstEmployees.Items.Add(strData); strData = string.Empty; }

Answer
You use the IsDBNull() method when you want to find if a column is null and without default values. Correct answer(s): 1. IsDBNull

Question
After retrieving the data, you want to close the DataReader object. Complete the code to close the DataReader object. Code
while(drList.Read()) { for(i = 0; i<=limit; i++) { if(drList.IsDBNull(i)) { strData += "--NULL--"; } else { strData += drList.GetValue(i).ToString(); } if( i < limit) { strData += ", "; } } lstEmployees.Items.Add(strData);

strData = string.Empty; } drList.INSERT THE MISSING CODE();

Answer
You use the Close() method to close the DataReader object. This method is invoked to release the connection when youve finished reading the data. Correct answer(s): 1. Close

Retrieving a single value


You now want to retrieve an employee's last name a single value from the employee database using the employee's ID. You've already created a SqlConnection and set the query string to get the last name from the HR.EMP table based on the EmpID.

Question
Complete the code using a method of the Command object that returns the first row of the result and ignores the rest. Code
string sql ="SELECT LastName FROM Employees WHERE EmployeeID=" + empID; using(SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd = new SqlCommand(sql, conn); try { conn.Open(); newLastName = cmd.INSERT THE MISSING CODE().ToString(); } catch(Exception ex) { Console.WriteLine(ex.Message); } } return newLastName;

Answer
You use the ExecuteScalar() method of the Command object to return a single value from a database.

Correct answer(s): 1. ExecuteScalar

Retrieving an XML data stream


You also decide to retrieve an XML result set of the names of all the employees of your company. You've already created a SqlConnection and SqlCommand object and associated them. You've also specified a SQL query containing the FOR XML AUTO clause to return the XML data in a nested XML tree.

Question
Complete the code to retrieve the employee data in an XML stream. Code
using System.Data.SqlClient; class Module1 { public static void Main() { string connString = "Data Source=.\\SQLEXPRESS;" + "AttachDbFilename=C:\\SQL Server 2000 Sample Databases\\REDRockMountainCycling.mdf;" + "Integrated Security=True;Connect Timeout=30;User Instance=True"; string sql ="SELECT LastName FROM Employees WHERE EmployeeID=7 FOR XML AUTO"; using(SqlConnection connection = new SqlConnection(connString)) { connection.Open(); SqlCommand command = new SqlCommand(sql, connection); System.Xml.XmlReader reader = command.INSERT THE MISSING CODE(); } } }

Answer
The Command object's ExecuteXmlReader() method executes a query containing a FOR XML clause to retrieve the employee data in an XML stream. Correct answer(s): 1. ExecuteXmlReader

You've created a Command object and a DataReader object. You've also used the methods and properties of the DataReader object. Additionally, you've used the ExecuteScalar() method to return a single value from a data source and retrieved an XML data stream.

Optimizing Data Connections with C# 2010


Learning Objectives
After completing this topic, you should be able to

identify ways to pool connections recognize the steps for retrieving a connection string from a configuration file identify the steps for monitoring connection resources

1. Using connection pooling


Data connections are very resource intensive. They use up server memory and CPU time when opened and closed, and often, there are limits to the number of connections that can be simultaneously open. If data connections are closed each time they're used, they need to be re-created and initialized when a client requests it. So opening and maintaining a data connection for each user, especially requests made to a dynamic database-driven web site application, is costly and uses up extensive resources. To solve the problem of extensive use of costly resources, ADO.NET provides connection pooling that helps optimize data connections. It keeps multiple connections open and available for use, when required, in a connection pool. When a client requests the data connection, the pooler tries to obtain active connection from a pool immediately, rather than opening a new connection. When the application closes the connection by calling the Close method, it is returned to a pool but is not closed. Connections are segregated into separate pools based on connection strings only connections with the same configuration are pooled together. They are also separated based on the transaction context and by Windows identity, if integrated security is specified. When a connection pool is first created, the pooler creates a number of connection objects to satisfy the minimum number required by the pool. The manner in which you handle connection pooling depends on the .NET Framework data provider you use. The .NET data providers include

ODBC The ODBC Driver Manager manages the connection pooling for data providers for ODBC and is independent of the .NET Framework Data Provider for ODBC. You can enable or disable connection pooling by using the ODBC Data Source Administrator in the Administrative Tools folder of Control Panel. OLE DB, and The OLE DB .NET Data Provider uses session pooling to automatically pool connections. You can use the connection string arguments to enable or disable various OLE DB services. SqlClient The .NET provider for SQL Server provides connection pooling by default. You can use different settings to disable pooling, configure an active connection period, or specify reset behavior when a connection returns to a pool.

The .NET provider for SQL Server offers a greater degree of control over connection pooling than the other data providers. Suppose a client seeks a data connection from the connection pool in a web application. You begin writing the code in the Visual C# source file by importing the required namespaces, inheriting the required class, and loading the page.

Code
using System.Data; using System.Data.SqlClient; partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, System.EventArgs e) {

You then add the attributes of the SqlConnection.ConnectionString property.

Code
string conString = ("Data Source=.\\SQLExpress;" + "Initial Catalog=RedRock;" + "Integrated Security=True;" + "Max Pool Size=50;" + "Min Pool Size=5;" + "Pooling=True;" + "Enlist=False;" + "Connection Lifetime=0;" + "Connection Reset=True;" + "Load Balance Timeout=150;");

Data Source=.\\SQLExpress

The Data Source attribute specifies the SQL Server instance name to which the application needs to connect. Depending on where the data you want to access is stored, you can specify the Server, Address, Addr, or Network Address instead of the Data Source. In addition, you can also specify a port number, whether it is a local instance, and if a specific protocol needs to be used. In this example, you want to connect to SQLExpress.
Initial Catalog=RedRock

The Initial Catalog attribute, which can also be called Database, specifies the name of the database you want to connect to. In this example, you want to connect to the RedRock database.
Integrated Security=True

Setting Integrated Security specifies whether or not the connection should be secure. In this example, the Integrated Security attribute is set to true. You can also set this attribute to false, and to sspi, which is equivalent to true. By default, this attribute is set to false.
Max Pool Size=50

You can set the Max Pool Size attribute to specify the maximum number of connections allowed in the pool. Although the default Max Pool Size is 100, it is set to 50 in this example. Specifying a larger value for this attribute could lead to an increased number of open connections in the pool causing increased memory usage and resulting in poor performance.
Min Pool Size=5

The Min Pool Size attribute helps you set the minimum number of connections allowed in the pool. In this example, it is set to 5.

Other attributes of the SqlConnection.ConnectionString property include Pooling, Enlist, and Timeout.

Code
string conString = ("Data Source=.\\SQLExpress;" + "Initial Catalog=RedRock;" + "Integrated Security=True;" + "Max Pool Size=50;" + "Min Pool Size=5;" + "Pooling=True;" + "Enlist=False;" + "Connection Lifetime=0;" + "Connection Reset=True;" + "Load Balance Timeout=150;"); Pooling=True

If the Pooling attribute is set to true, it helps retrieve the SQLConnection object from the appropriate pool, or creates and adds the SQLConnection object to the appropriate pool, if required. This attribute is set to true by default. Other values that you can set for this attribute are false, yes, and no. The attribute is explicitly included here for demonstration purposes but it doesn't need to be included in the string if the default value is required.
Enlist=False

The Enlist attribute helps automatically enlist the connection in the current transaction context of the creation thread. This attribute is set to true by default.
Connection Lifetime=0

Using the Connection Lifetime attribute helps you compare the creation time with the current time. If the time span exceeds the creation time, the connection is destroyed. Specifying this attribute is useful in a clustered configuration where you want to enforce load balancing between an existing server and the one that was just brought online. If the Connection Lifetime attribute is set to 0, the connection never times out.
Connection Reset=True

The Connection Reset attribute helps determine whether the state of the data connection drawn from the pool before use is reset to the state at login time. This attribute is set to true by default. You retain the default value in this example.
Load Balance Timeout=150

The Load Balance Timeout attribute sets the minimum time, in seconds, for the connection to be in the pool before it is destroyed. The default value for this attribute is 0. However, in this example, it is set to 150. If the attribute is set to 0, the connection remains alive forever.

There are two methods available to clear a connection pool ClearPool and ClearAllPools. The ClearPool method is used to clear a pool associated with a specific connection, while the ClearAllPools method is used to clear the pools associated with a specific provider. If connections are in use when either of these methods are called, they are marked and then discarded when the connections are closed.

Code
SqlConnection connection1 = new SqlConnection(conString); connection1.Open(); SqlConnection.ClearPool(connection1);

Question
Before you request data, you want to ensure that pooling is enabled and you want to specify that the connection should remain in the pool for two minutes before it is destroyed.

What attributes of the SqlConnection.ConnectionString property will be used to ensure this? Options:
1. 2. 3. 4.
Pooling = true Load Balance Timeout = 120 Min Pool Size = 120 Connection Reset = true

Answer
Option 1: Correct. If the Pooling attribute is set to true, you can retrieve the SQLConnection object from the appropriate connection pool. This attribute is set to true by default. Option 2: Correct. If the Load Balance Timeout=120, it means that the connection will remain in the pool for two minutes before it is destroyed. The default value for this attribute is 0, which means the connection is alive forever. Option 3: Incorrect. The Min Pool Size attribute helps you set the minimum number of connections allowed in the pool. Option 4: Incorrect. The Connection Reset attribute helps determine whether the state of the data connection drawn from the pool before use is reset to the state at login time. Correct answer(s): 1. Pooling = true 2. Load Balance Timeout = 120

Question
You want to specify the connection string that is used to open a SQL Server database. For this, you need to specify various attributes of the SqlConnection.ConnectionString property. Match the attributes of the SqlConnection.ConnectionString property with their descriptions. Options:
1. 2. 3. 4.
Initial Catalog Data Source Pooling Load Balance Timeout

Targets:

1. 2. 3. 4.

Specifies the SQL Server instance name to which the application needs to connect Specifies the database you want to connect to Sets the minimum time, in seconds, before which the connection in the pool is destroyed Retrieves the SQLConnection object from the appropriate pool if the attribute is set to true

Answer
You can use the Data Source attribute to specify the Server, Address, Addr, or Network Address instead of the Data Source based on where the data is stored. The Initial Catalog attribute is also referred to as the Database attribute. The default value for the Load Balance Timeout attribute is 0. The Pooling attribute is set to true by default. Correct answer(s): Target 1 = Option B Target 2 = Option A Target 3 = Option D Target 4 = Option C

2. Accessing connection strings


Connection strings contain connection information that includes the database server name, a user name, and a password. Storing this information in the application code could pose security risks. A malicious user could use it to gain unauthorized access. Also when the application code is compiled, the information can be viewed using tools, such as the MSIL Disassembler tool. Therefore, it is recommended that you store connection string details in the application configuration file app.config or web.config or external configuration files. Configuration files cannot be viewed from a web browser, and you can easily add or modify the settings without recompiling the code. Application configuration files contain application-specific settings. Every web application has a web.config file, and every Windows application has an app.config file that can be used to store data connections. To do this, you add the connectionStrings section to the configuration element in the configuration file.

Code

<?xml version='1.0' encoding='utf-8'?> <configuration> <connectionStrings> <clear />

External configuration files are separate files that contain only the connectionStrings element of the configuration file. This configuration file is stored in a separate physical location and is referenced by the main configuration file. Although rare, in some situations you'd want to edit the configuration file settings after application deployment. If an ASP.NET configuration file is modified, it causes an application restart and information state could be lost. In such cases, it is useful to store the connectionStrings element in a separate file and reference it from the main configuration file. Suppose you've created a Windows-based application that requires access to various data sources at run time. For faster data access and to reduce the wait time for users, you decide to store multiple connections in the app.config file. To do this, you add the connectionStrings element within the configuration. The connectionStrings element can contain child elements add, clear, and remove. This example uses the clear and the add elements.

Code
<connectionStrings> <clear/> <add name="SqlConnect1"

Suppose you're storing two connection strings SqlConnect1 and SqlConnect2. Within the add element, you include the name, connectionString, and the providerName attributes. The name attribute uniquely identifies the connection string. The connectionString attribute stores the connection strings as key and value pairs and the providername attribute specifies the .NET data provider name. This information is stored in the machine.config file. You then pass values for the Data Source and Initial Catalog attributes in the connectionString attribute and specify System.Data.SqlClient as the providerName attribute.

Code
<clear/> <add name="SqlConnect1" connectionString="Data Source=myServer;Initial Catalog=MyDatabase" providerName="System.Data.SqlClient" /> <add name="SqlConnect2" connectionString="Data Source=myServer;Initial Catalog=RedRock" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>

You now want to retrieve the connection strings from the app.config file. To access configuration file information for a client application, you use the ConfigurationManager class. If you want to retrieve configuration file information for a web application, you use the WebConfiguration class. Because this example uses a Windows-based application, you use the ConfigurationManager class to retrieve the configuration information. To retrieve the connection strings from the configuration file, you use the ConnectionStringSettingsCollection class. It contains a collection of objects of the ConnectionStringSettings class. Each object in the collection represents a connection entry in the connectionStrings section in the app.config file.

Try It
You now want to obtain the connection strings collection. For this, you need to create an object of the ConnectionStringSettingsCollection class. The existing code is: using System.Configuration; class Module1 { static void Main() { MISSING CODE conns = ConfigurationManager.ConnectionStrings(); To complete the task
1. Type ConnectionStringSettingsCollection and click the Enter key provided Type ConnectionStringSettingsCollection in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to create an object of the ConnectionStringSettingsCollection class is entered. You then declare the foreach loop to search and obtain the connection string details name, provider name, and connection string and display them on the console using the Console.WriteLine method.

Code
foreach (ConnectionStringSettings conn in conns) { string name = conn.Name; string provider = conn.ProviderName;

string connStr = conn.ConnectionString; Console.WriteLine("Name: {0}", name); Console.WriteLine("Connection string: {0}", connStr); Console.WriteLine("Provider: {0}", provider); }

If there are no connection strings stored in the configuration file, a message is displayed.

Code
else { Console.WriteLine("No connection string in configuration file."); } } }

Consider another situation where you have created an online shopping web site that needs to process various user requests. You need to access different data sources. In this situation, you use the WebConfigurationManager class to retrieve the connection strings from the web.config file. To do this, you first declare the GetConnectionStrings sub-routine within the ReadConfig class.

Code
using System.Web.Configuration; public class ReadConfig { static void GetConnectionStrings() {

Try It
You then retrieve the connectionStrings section from the configuration file using the GetSection method. The existing code is: using System.Web.Configuration; public class ReadConfig { static void GetConnectionStrings() { ConnectionStringsSection connStrsSection =

WebConfigurationManager.MISSING CODE("connectionStrings"); To complete the task


1. Type GetSection and click the Enter key provided Type GetSection in the MISSING CODE field and click the Enter key provided. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The GetSection method is declared. You've stored multiple connection strings so you retrieve the connectionStrings section collection using the ConnectionStringSettingsCollection class.

Code
public class ReadConfig { static void GetConnectionStrings() { ConnectionStringsSection connStrsSection = WebConfigurationManager.GetSection("connectionStrings"); ConnectionStringSettingsCollection connStrs = connStrsSection.ConnectionStrings;

You then retrieve the collection's enumerator. An enumerator is used to list objects from a collection. In this example, the connStrsEnum object of the IEnumerator interface is created to display the connection string objects using the GetEnumerator method of the ConnectionStringSettingsCollection class.

Code
public class ReadConfig { static void GetConnectionStrings() { ConnectionStringsSection connStrsSection = WebConfigurationManager.GetSection("connectionStrings"); ConnectionStringSettingsCollection connStrs = connStrsSection.ConnectionStrings; IEnumerator connStrsEnum = connStrs.GetEnumerator();

You then define a while loop to display the name and value of the connection string data source, initial catalog, and provider name. This information is then displayed on the console using the Console.WriteLine method.

Code

public class ReadConfig { static void GetConnectionStrings() { ConnectionStringsSection connStrsSection = WebConfigurationManager.GetSection("connectionStrings"); ConnectionStringSettingsCollection connStrs = connStrsSection.ConnectionStrings; IEnumerator connStrsEnum = connStrs.GetEnumerator(); int i = 0; Console.WriteLine("Connection Strings"); while (connStrsEnum.MoveNext()) { string name = connStrs[i].Name; Console.WriteLine("Name: {0} Value: {1}", name, connStrs(name)); i++; } Console.WriteLine(); } }

Data connection information is one of the most critical information types stored in the configuration files. And an important aspect of securing a data access application is to secure access to the data source. If connection strings are stored as plain text, they could pose security risks. It is, therefore, recommended to encrypt connection strings and store them in configuration files. The .NET Framework provides the protected configuration feature that helps you encrypt the connectionStrings section in the configuration file. The configProtectionProvider element specifies which protected configuration provider is used to encrypt the connection strings. During encryption, it generates the Cipher value, which is used to decrypt connection strings. The Cipher value is added in the EncryptedData section of the configuration file. This example uses DataProtectionConfigurationProvider as the configProtectionProvider.

Code
<connectionStrings configProtectionProvider= "DataProtectionConfigurationProvider"> <EncryptedData> <CipherData> <CipherValue>AQAAANCMnd8BFdERjHoAwE/ Cl+sBAAAAgd6aFWwZeUqgvWRsgfnujgQAAAACAAAAAAADZgAAqAAAAB AAAADPB2XrO1QWYYbr4ODEXV9NAAAAAASAAACgAAAAEAAAAINYpIJA6 XelRVLZPTTPTAG4AgAAb8Sd31A2CFnFUbl24N6RVifdv7wvgC7GOTqjj Km92iGkPnTuqqGwbiyG4zPmsnoed9tQwz5C7S7pa1LmG78tafGb55l8 4j9Ib2mXiIJtY5ryvgYEozsslfOMC08+x8fDc9ES0FA+a81CKzqjXv3 97gDLIjlsvtW8l/jPmEBmT4KHgs2xmnM9oJSnWqJkCTEeGqAhFjXqGk 47b8qVfOgdQ+oVJrWFMoJTfHh8FSkkoigB6hNqytwaeZXIXwfvF68Wv 2u7iW04bXbKimbP9lqEZPUS7M5Z94a1DO3OylmVnafAEvvW9P456ng0 zEH+PeTNE391KBcDgwbw8hwLtHHtj4fSiJYxM4IcgVAhCBhVYLjboPB

L8o2ac9DV7z5AUrUm2r/FNn86ToTo5Ge/znGaF9Bn8uaC0DlkyKuPaH 2wnVDrUA1zt9iO60sYQNJPYwW7V0/ZSCjQk7ZWzAgffqs5COTQARUbd lQ3MjJS/j0LGx/f3cjIY+5quEn6JQFo/Z1OHvL8n8rrla4Zlo1qa7Ud KsLdMP+hEM9b2i3NXIBYz8bODURVi9l719TOQPy8zv5bwmiPknHbjDs dZfGb5GaSncyBgHx7zd6AModgCw8HDbxZ7Jqh1usNTy7vfLwm9HzV1r YsPq6cK5cz+u8P7Ag+op2kKGTodRWFmRjJdDZPqGJ8li8rUCxAqlgw1 m28dup8iYCTVwgkR3DpJlJxovg3ZZx5d2NJOZviym5NLWFNtI973Kur RykNVKLRd856L1UZbIph68urmCoZWcBkHfOHeDE4VSrEXjT/whk4Zjh IYgbITgGl1NM31uuoCeLhbcb//v2fybbnLRrkvkBe2tLxR0kxikwh4 ZZjSyXzSpd0bY3iYNTAbfuwEixXKyy5VsjGfC1HYM//X1IGKkglGF4 HkBAhO2jqDRBgFAAAAC+fHNGMlpnA/JzoONYNcr64TS0c </CipherValue>

The protected configuration providers are stored in the configProtectedData section of the machine.config file of the local computer.

Code
<configProtectedData defaultProvider="RsaProtectedConfigurationProvider"> <providers>

The .NET Framework provides two protected configuration providers RsaProtectedConfigurationProvider and DataProtectionConfigurationProvider. You can either use these, configure additional protected providers, or create your own protected configuration providers using the ProtectedConfigurationProvider base class.

Code
<configProtectedData defaultProvider="RsaProtectedConfigurationProvider"> <providers> <add name="RsaProtectedConfigurationProvider" type="System.Configuration.RsaProtectedConfig urationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="NetFrameworkConfigurationKey" cspProviderName="" useMachineContainer="true" useOAEP="false"/> <add name="DataProtectionConfigurationProvider" type="System.Configuration.DpapiProtectedConf igurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses CryptProtectData and CryptUnProtectData Windows APIs to encrypt and decrypt"

useMachineProtection="true" keyEntropy=""/> </providers> </configProtectedData>

You can also encrypt the connection strings using application code. Suppose you want to encrypt the connection strings from within a Windows application. Before that, you want to check whether the connection strings are already encrypted. To do this, you first retrieve the configuration file that contains the connection strings by using the OpenExeConfiguration method of the ConfigurationManager class.

Code
void Main() { Configuration config = ConfigurationManager. OpenExeConfiguration("");

You then use the GetSection method to retrieve the connectionStrings section from the configuration file.

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

You then check whether the IsProtected property is set to True. If it is not set to True, then it calls the ProtectSection method to encrypt the connection strings. The status of the connection string is then displayed on the console.

Code
if (section.SectionInformation.IsProtected != true) { section.SectionInformation.ProtectSection( "DataProtectionConfigurationProvider"); } config.Save(); Console.WriteLine("Protected status={0}", section.SectionInformation.IsProtected); } }

Question

You want to retrieve a connection string from the app.config file. Complete the code to do this by selecting the appropriate class. Code
void Main() { ConnectionStringSettingsCollection conns = ConfigurationManager.INSERT THE MISSING CODE; if (conns.Count != 0) { Console.WriteLine("Connection strings:"); foreach (ConnectionStringSettings conn in conns) { string name = conn.Name; string provider = conn.ProviderName; string connStr = conn.ConnectionString; Console.WriteLine("Name: {0}", name); Console.WriteLine("Connection string: {0}", connStr); Console.WriteLine("Provider: {0}", provider); } } else { Console.WriteLine("No connection string in configuration file."); }

Options:
1. ConnectionStrings 2. WebConfigurationManager 3. ConnectionStringSettingsCollection

Answer
Option 1: Correct. To retrieve the connection strings from the application configuration files, you use the ConnectionStrings class that contains a collection of the ConnectionStringSettings objects. Option 2: Incorrect. You use the WebConfigurationManager class to access the configuration files of the web application. Option 3: Incorrect. You use the ConnectionStringSettingsCollection method to retrieve the connection strings from the configuration files into a collection. Correct answer(s):
1. ConnectionStrings

Question
You want to retrieve the connection strings from the configuration file. Enter the code to instantiate the appropriate class. Code
protected void GetConnectionStrings() { INSERT THE MISSING CODE connections = ConfigurationManager.ConnectionStrings; if (connections.Count != 0) { foreach (ConnectionStringSettings connection in connections) { string name = connection.Name; string provider = connection.ProviderName; string connectionString = connection.ConnectionString; lblName.Text = name; lblConString.Text = connectionString; lblProvider.Text = provider; } } else { lblName.Text = "No connection string is defined."; } } }

Answer
You store the connections in the ConnectionStringSettingsCollection collection for retrieving them later. Correct answer(s):
1. ConnectionStringSettingsCollection

Question
What are the uses of a configuration file? Options:
1. The machine.config file stores details about the protected configuration providers for use in string encryption 2. Configuration files help protect critical information including connection strings 3. Configuration files store connection string information in the connectionStrings section which can be retrieved using the GetConnectionStrings user-defined function

4. Configuration files help retrieve connection strings from a Windows-based application using the WebConfigurationManager class

Answer
Option 1: Correct. During encryption, a Cipher value is generated that is used to decrypt the connection strings. Option 2: Correct. You can store connection string details within the connectionStrings section of the configuration file, which if stored in the application code, could pose security risks. Option 3: Incorrect. You retrieve the connectionStrings section from the configuration file using the GetSection method. Option 4: Incorrect. A Windows-based application retrieves the connection strings from the configuration files using the ConfigurationManager class. Correct answer(s): 1. The machine.config file stores details about the protected configuration providers for use in string encryption 2. Configuration files help protect critical information including connection strings

3. Building connection strings


Sometimes, only part of the connection information is available in the configuration file and you need to provide the remaining information at runtime. For example, you may want user details to be submitted at runtime. You can request a user to provide this information at runtime and then construct the connection string. However, using simple string concatenation at runtime to build a connection string could result in a connection string injection attack. For example, an attacker could substitute an original value but ADO.NET provides strongly-typed string builder classes for each data provider, which protect your application. Each string builder class contains methods and properties that correspond to the connection string key/value pairs associated with each data provider. Suppose you've defined a partial connection string. You want to build a complete connection string by providing the remaining information data source, user name, and password at runtime. To do this, you declare a sub-routine called CreateConnections and pass the data source, user name, and password as its parameters.

Code

private void CreateConnections(string dataSource, string userName, string userPassword) {

You then retrieve the partial connection string from the configuration file.

Code
private void CreateConnections(string dataSource, string userName, string userPassword) { ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings ["MyConnectionString"];

Try It
Based on the partial connection string retrieved from the configuration file, you create a complete connection string using the SqlConnectionStringBuilder class. The existing code is: private void CreateConnections(string dataSource, string userName, string userPassword) { ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings ["MyConnectionString"]; if (settings!= null) { string connectString = settings.ConnectionString; SqlConnectionStringBuilder builder = new MISSING CODE(connectString); To complete the task
1. Type SqlConnectionStringBuilder and click the Enter key provided Type SqlConnectionStringBuilder in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A new connection builder string is created. After you create the SqlConnectionStringBuilder string, you provide the additional values data source, user name, and password. The complete connection string is finally constructed and is assigned to lblConString and lblDatasource to be displayed on screen.

Code
private void CreateConnections(string dataSource, string userName, string userPassword) { ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings ["MyConnectionString"]; if (settings != null) { string connectString = settings.ConnectionString; SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); builder.DataSource = dataSource; builder.UserID = userName; builder.Password = userPassword; lblConString.Text = builder.ConnectionString.ToString(); lblDatasource.Text = builder.DataSource.ToString(); } } }

Question
You want to build a connection string based on the information in the configuration file. Enter the code to instantiate the appropriate class. Code
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings ("MyConnectionString"); if (settings != null) { string connectString = settings.ConnectionString; INSERT THE MISSING CODE builder = //..................... }

Answer
The SqlConnectionStringBuilder class enables you to use the details in the web.config file to create a new connection string. Correct answer(s):

1. SqlConnectionStringBuilder

4. Monitoring connection resources


In addition to using connection pooling for efficient use of connection strings, you can use ADO.NET performance counters to monitor the connection resources. These counters help you determine how effectively connection pools are being utilized. You can either set the counters using the Windows Performance Monitor tool or access performance counters programmatically with the Systems.Diagnostics.PerformanceCounter class. You can use any of the ADO.NET counters to measure data access by collecting various metrics, such as the total number of connection pools, the number of connections made to a database server in a second, and the number of active connections accessed from a connection pool. This information helps you take appropriate measures to improve the performance of the application and the connection resources the application uses.

Supplement
Selecting the link title opens the resource in a new browser window. Learning Aid Use the learning aid Performance counters in ADO.NET to know more about the performance counters that help monitor connection resources used by an application. Suppose you notice that occasionally, data connections get timed out. You want to analyze the problems and take corrective action. For this, you need to collect data, such as the number of connections made to the database server per second, number of active and unique connection pools, number of inactive connection pools, and so on. So you decide to open four data connections and collect the metrics for these connections. In the example used here, you place the code to collect performance metrics for data connections, within a class called MyPerformanceCounter.

Code
using using using class System.Data.SqlClient; System.Diagnostics; System.Runtime.InteropServices; MyPerformanceCounter

You then declare an array variable as a PerformanceCounter type and a connection variable as a SqlConnection type.

Code
using System.Data.SqlClient; using System.Diagnostics; using System.Runtime.InteropServices; class MyPerformanceCounter private static PerformanceCounter[] PerformanceCount; private SqlConnection connection = new SqlConnection(); private enum PerformanceCountersEnum { NumberOfActiveConnectionPools, NumberOfReclaimedConnections, HardConnectsPerSecond, HardDisconnectsPerSecond, NumberOfActiveConnectionPoolGroups, NumberOfInactiveConnectionPoolGroups, NumberOfInactiveConnectionPools, NumberOfNonPooledConnections, NumberOfPooledConnections, NumberOfStasisConnections }

When Windows authentication or integrated security is used, connection pools are mapped to connection strings and separate pools are also created for Windows identities. Therefore, when using this type of security you need to monitor both the NumberOfActiveConnectionPoolGroups and NumberOfActiveConnectionPools performance counters.

Code
using System.Data.SqlClient; using System.Diagnostics; using System.Runtime.InteropServices; class MyPerformanceCounter private static PerformanceCounter[] PerformanceCount; private SqlConnection connection = new SqlConnection(); private enum PerformanceCountersEnum { NumberOfActiveConnectionPools, NumberOfReclaimedConnections, HardConnectsPerSecond, HardDisconnectsPerSecond, NumberOfActiveConnectionPoolGroups, NumberOfInactiveConnectionPoolGroups, NumberOfInactiveConnectionPools, NumberOfNonPooledConnections, NumberOfPooledConnections, NumberOfStasisConnections }

Try It

You then define the enumeration containing the performance counters you want to use. You want to complete the list of entries by adding a counter to retrieve the number of pooled connections. The existing code is: private enum PerformanceCountersEnum { NumberOfActiveConnectionPools, NumberOfReclaimedConnections, HardConnectsPerSecond, HardDisconnectsPerSecond, NumberOfActiveConnectionPoolGroups, NumberOfInactiveConnectionPoolGroups, NumberOfInactiveConnectionPools, NumberOfNonPooledConnections, MISSING CODE NumberOfStasisConnections } To complete the task
1. Type NumberOfPooledConnections, and click the Enter key provided TypeNumberOfPooledConnections, in the MISSING CODE field and click the Enter key provided. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The list of members for the performance counters enumeration is now completed. Within the SetUpPerfCounters method, you declare an array of counters with ten elements to store the values of each of the performance metrics. You then call a user-defined GetInstanceName method to uniquely define an instance of the counter. You then use the typeof method that returns the type of the PerformanceCountersEnum as Enum.

Code
private void SetUpPerfCounters() { connection.Close(); this.PerformanceCount = new PerformanceCounter[10]; string instanceName = GetInstanceName(); Type counterEnum = typeof(PerformanceCountersEnum); int x = 0; string y = "";

You then define a foreach loop to set a new performance counter for each of the Enum members you've defined.

Code
private void SetUpPerfCounters() { connection.Close(); this.PerformanceCount = new PerformanceCounter[10]; string instanceName = GetInstanceName(); Type counterEnum = typeof(PerformanceCountersEnum); int x = 0; string y = ""; foreach (y in Enum.GetNames(counterEnum)) { this.PerformanceCount[x] = new PerformanceCounter(); this.PerformanceCount[x].CategoryName = ".Net Data Provider for SqlServer"; this.PerformanceCount[x].CounterName = y; this.PerformanceCount[x].InstanceName = instanceName; x = (x + 1); } }

After defining the array, you declare the GetCurrentProcessId function to call the kernel32.dll file to get a process ID allocated by the CPU. Kernel32.dll a Windows file handles memory, input/output, and interrupts. The process ID returns the data about memory usage and input/output operation.

Code
private void SetUpPerfCounters() { connection.Close(); this.PerformanceCount = new PerformanceCounter[10]; string instanceName = GetInstanceName(); Type counterEnum = typeof(PerformanceCountersEnum); int x = 0; string y = ""; foreach (y in Enum.GetNames(counterEnum)) { this.PerformanceCount[x] = new PerformanceCounter(); this.PerformanceCount[x].CategoryName = ".Net Data Provider for SqlServer"; this.PerformanceCount[x].CounterName = y; this.PerformanceCount[x].InstanceName = instanceName; x = (x + 1); } } [DllImport("kernel32.dll")] private static extern int GetCurrentProcessId();

In the GetInstanceName user-defined function, you store the name of the application.

Code

private string GetInstanceName() { // Used for Windows Applications string instanceName = System.Reflection.Assembly.GetEntryAssembly.GetName.Name; // Used for ASP.NET applications string instanceName = AppDomain.CurrentDomain. FriendlyName.ToString.Replace("(", "[") .Replace(")", "]").Replace("#", "_"). Replace("/", "_").Replace("\\\\", "_");

You call the GetCurrentProcessId function and convert it to ToString data type. Then you append the instance name, which is the application name, along with the process ID to return an instance name value.

Code
private string GetInstanceName() { // Used for Windows Applications string instanceName = System.Reflection.Assembly.GetEntryAssembly.GetName.Name; // Used for ASP.NET applications string instanceName = AppDomain.CurrentDomain. FriendlyName.ToString.Replace("(", "[") .Replace(")", "]").Replace("#", "_"). Replace("/", "_").Replace("\\\\", "_"); string processId = GetCurrentProcessId().ToString(); instanceName = (instanceName + "[" + (processId + "]")); return instanceName; }

You define the DisplayPerfCountData method. Within this method, you use the For Each loop to write the counter name. You also write the next value of the performance counter to the console using the NextValue method.

Code
private void DisplayPerfCountData() { Console.WriteLine("---------------------------"); foreach (PerformanceCounter p in this.PerformanceCount) { Console.WriteLine("{0} = {1}", p.CounterName, p.NextValue().ToString()); } Console.WriteLine("---------------------------"); }

You use two functions to return connection strings that use integrated security or user name/password authentication in the application.

Code
private static string GetISConnectionString() { return ("Data Source=SQL2008;Initial" + "Catalog=AdventureWorks;User Id=user;" + "Password=password;Integrated Security=True"); } private static string GetSqlConnString() { return ("Data Source=SQL2008;Initial" + "Catalog=AdventureWorks;Integrated Security=True"); } }

With the Main method, you set the connection string details, such as the data source and database name by calling the GetISConnectionString method you've defined earlier.

Code
public static void Main() { MyPerformanceCounter counter = new MyPerformanceCounter(); counter.connection.ConnectionString = GetISConnectionString();

Next, to collect the performance metrics, you set the performance counters by calling the SetUpPerfCounters method.

Code
public static void Main() { MyPerformanceCounter counter = new MyPerformanceCounter(); counter.connection.ConnectionString = GetISConnectionString(); counter.SetUpPerfCounters(); Console.WriteLine( "Here are the available Performance Counters:");

After setting the performance counters, you call the CreateConnections method to activate the connections.

Code

public static void Main() { MyPerformanceCounter counter = new MyPerformanceCounter(); counter.connection.ConnectionString = GetISConnectionString(); counter.SetUpPerfCounters(); Console.WriteLine( "Here are the available Performance Counters:"); counter.CreateConnections(); Console.WriteLine("Press Enter to finish."); Console.ReadLine(); }

Supplement
Selecting the link title opens the resource in a new browser window. Learning Aid Use the learning aid Code to use performance counters in ADO.NET to view the entire code to use performance counters to monitor connection resources. If you want to monitor the NumberOfFreeConnections, NumberOfActiveConnections, SoftDisconnectsPerSecond, and SoftConnectsPerSecond counters, you need to first enable them, as they are off by default. You do this by setting the ConnectionPoolPerformanceCounterDetail switch value to 4 in the <system.diagnostics> section of the configuration file.

Code
<system.diagnostics> <switches> <add name="ConnectionPoolPerformanceCounterDetail" value="4"/> </switches> </system.diagnostics>

Question
You want to collect the performance metrics for data connections. Enter the code to declare an appropriate array variable. Code
using System.Data.SqlClient; using System.Diagnostics; using System.Runtime.InteropServices; class MyPerformanceCounter

private static INSERT THE MISSING CODE PerformanceCount; private SqlConnection connection = new SqlConnection(); private enum PerformanceCountersEnum { NumberOfActiveConnectionPools, NumberOfReclaimedConnections, HardConnectsPerSecond, HardDisconnectsPerSecond, NumberOfActiveConnectionPoolGroups, NumberOfInactiveConnectionPoolGroups, NumberOfInactiveConnectionPools, NumberOfNonPooledConnections, NumberOfPooledConnections, NumberOfStasisConnections }

Answer
To declare the array variable, you need to enter PerformanceCounter[]. Correct answer(s):
1. PerformanceCounter[]

Summary
ADO.NET facilitates connection pooling that helps optimize data connections by keeping multiple data connections open for use when required. This improves the performance of your application. The manner in which you handle connection pooling will depend on the .NET Framework data provider you use. Connections are segregated into different pools based on connection strings that contain information about the connection. These connection strings should be stored either in the application code or in the configuration files so they can be used to establish a data connection. You can retrieve a data connection from a configuration file by specifying the provider name. You can access multiple connection strings in the connection pool using the ConnectionStringsSettingsCollection and ConnectionStringSettings classes. You can use the ADO.NET performance counters to monitor data access performance in relation to connections and connection pools usage and take corrective steps to improve performance.

Table of Contents

| Print | Contents | Close |

Using Stored Procedures and Statements with Parameters


Learning Objectives
After completing this topic, you should be able to

recognize the code for using parameters with SQL statements recognize the steps for using the ExecuteNonQuery method identify the steps for creating a stored procedure with parameters

1. Using SQL statements


Any command you execute in SQL is called a SQL statement. However, some statements are SQL queries that return result sets, while other statements are used to modify source data. You use the ExecuteNonQuery method of a command object to execute UPDATE, INSERT or DELETE statements and return the number of rows affected by the SQL statement. You can also use the ExecuteNonQuery method to perform database operations, such as creating or dropping objects like tables and indexes. You can also use stored procedures to group pre-compiled SQL statements so they can be executed as one unit. Stored procedures that do not return result sets can be executed using the ExecuteNonQuery method. You want to create a command-line application to access and update a data source. You define the connection string that will be used. In this example, you are connecting to the RedRock database and are using Windows-based authentication for authenticating users.

Code
using System.Data.SqlClient; class Module1 { static void Main() { string connString = ("Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;" + "Integrated Security=True");

Then you declare the string variables, query1, query2, and so on, to assign the data and data structure manipulation queries, such as INSERT, DELETE, UPDATE, and CREATE TABLE.

Code
string connString = ("Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=Northwind;" + "Integrated Security=True"); string query1 = ("INSERT INTO Employees(LastName, FirstName)VALUES" + "(\'Jones\', \'Bob\')"); string query2 = ("DELETE FROM Employees WHERE LastName=\'Jones\'" + " AND FirstName=\'Bob\'"); string query3 = ("UPDATE Employees SET City=\'Miami\' WHERE " + "LastName=\'Jones\' AND FirstName=\'Bob\'"); string query4 = "CREATE TABLE Vehicles (Make char(50), Model char(50))";

Try It
Next you create the SqlConnection object by passing it the connection string you specified earlier. The existing code is: string query1 = ("INSERT INTO Employees(LastName, FirstName)VALUES" + "(\'Jones\', \'Bob\')"); string query2 = ("DELETE FROM Employees WHERE LastName= \'Jones\'" + " AND FirstName=\'Bob\'"); string query3 = ("UPDATE Employees SET City=\'Miami\' WHERE " + "LastName=\'Jones\' AND FirstName=\'Bob\'"); string query4 = "CREATE TABLE Vehicles (Make char(50), Model char(50))"; using (SqlConnection(connString) conn = new MISSING CODE) To complete the task
1. Type SqlConnection(connString)) and click the Enter key provided Type SqlConnection(connString) in the MISSING CODE field and click the Enter key.

The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The connection string is defined. You then create the SQLCommand objects that you will execute with the ExecuteNonQuery method, passing them the SQL queries for updating the database and the connection object.

Code
string query3 = ("UPDATE Employees SET City=\'Miami\' WHERE " + "LastName=\'Jones\' AND FirstName=\'Bob\'"); string query4 = "CREATE TABLE Vehicles (Make char(50), Model char(50))"; string sqlqry = "select count(*)from Employees"; using (SqlConnection conn { SqlCommand cmd1 = new SqlCommand cmd2 = new SqlCommand cmd3 = new SqlCommand cmd4 = new } = new SqlConnection(connString)) SqlCommand(query1, SqlCommand(query2, SqlCommand(query3, SqlCommand(query4, conn); conn); conn); conn);

Try It
You need to open the connection to the data source and then call the ExecuteNonQuery method to perform the data manipulation. The existing code is using (SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd1 = new SqlCommand(query1, conn); SqlCommand cmd2 = new SqlCommand(query2, conn); SqlCommand cmd3 = new SqlCommand(query3, conn); SqlCommand cmd4 = new SqlCommand(query4, conn); cmd1.Connection.Open(); cmd1.MISSING CODE(); cmd1.Connection.Close(); } To complete the task
1. Type ExecuteNonQuery and click the Enter key provided Type ExecuteNonQuery in the MISSING CODE field and click the Enter key provided.

The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The ExecuteNonQuery method is called. The ExecuteNonQuery method needs to be called for each of the SQLCommand objects. You need to then explicitly close each connection string so that it is released and can be reused later.

Code
cmd1.Connection.Open(); cmd1.ExecuteNonQuery(); cmd1.Connection.Close(); cmd2.Connection.Open(); cmd2.ExecuteNonQuery(); cmd2.Connection.Close(); cmd3.Connection.Open(); cmd3.ExecuteNonQuery(); cmd3.Connection.Close(); cmd4.Connection.Open(); cmd4.ExecuteNonQuery(); cmd4.Connection.Close(); }

Question
As a web developer, you want to execute a set of Insert and Update statements against a SQL data source using the ExecuteNonQuery method. Arrange the steps to create the code that enables the ExecuteNonQuery method to execute the statements in the correct order. Options:
1. Define a connection string and create a connection object 2. Create SQLCommand objects by passing specified SQL statement string objects and the connection object 3. Execute the ExecuteNonQuery method of each command object

Answer
Correct answer(s):
Define a connection string and create a connection object is ranked the first step in creating the code that enables the ExecuteNonQuery method to execute data manipulation statements

In the connection string, you specify the data source and the database name. Then you create an instance of the SQLConnection class to create a connection object that uses the specified connection string. Create SQLCommand objects by passing specified SQL statement string objects and the connection object is ranked the second step in creating the code that enables the ExecuteNonQuery method to execute data manipulation statements You pass SQL statement string objects and connection objects as attributes of the SQLCommand class. Execute the ExecuteNonQuery method of each command object is ranked the third step in creating the code that enables the ExecuteNonQuery method to execute data manipulation statements Finally you call the ExecuteNonQuery method to perform data manipulations.

Question
You've created a command-line application and from within the application, you want to query the data source. So you create a data connection, create cmd1 as a SQLCommand object, and pass query1 as an object. Enter the appropriate method to execute the query1 query. Code
using (SqlConnection conn { SqlCommand cmd1 = new SqlCommand cmd2 = new SqlCommand cmd3 = new SqlCommand cmd4 = new = new SqlConnection(connString)) SqlCommand(query1, SqlCommand(query2, SqlCommand(query3, SqlCommand(query4, conn); conn); conn); conn);

cmd1.Connection.Open(); cmd1.INSERT THE MISSING CODE(); cmd1.Connection.Close();

Answer
You call the ExecuteNonQuery method to perform the data manipulations. Correct answer(s):
1. ExecuteNonQuery

You can execute SQL statements by either passing hard-coded values or by entering the values as command parameters during runtime. A command parameter is a placeholder that is substituted by a user input value at run time. You define a parameter by prefixing it with the @ symbol.

Using commands with parameters helps improve query performance as they enable the command to be mapped with the query plan cached by the database servers. Unlike command text, which is treated as code, it protects the database against SQL injection attacks as parameter values are literal values which are type checked and validated at runtime. To execute a SQL statement by passing parameters, you need to understand certain concepts. They are
The ParameterDirection properties The ParameterDirection property is an enumeration that specifies the type of parameter in a query. Values that the ParameterDirection enumeration supports are Input, Output, InputOuput, and ReturnValue. To execute a SQL statement with parameters, you first configure a parameter for the required ParameterDirection property. The DbParameterCollection class The DbParameterCollection class provides various properties and methods that help you add and retrieve the Parameter objects to and from the collection of DbParameter objects. In addition to using a DbParameter constructor, a DbParameter object can also be created by calling the Add method of a DbParameterCollection class, such as SqlParameterCollection with constructor arguments. The Command objects also contain Parameter properties, which can be used to obtain the relevant ParameterCollection. Placeholders, and Depending on the data source, you can use parameter placeholders. When using placeholders, you specify a question mark (?) symbol instead of the named parameter. However, SQL clients do not support parameters placeholders; they only support named parameters specified as @parametername. Datatypes Each .NET Framework data provider has a specific set of datatypes that it can use. When you specify a type, the parameter value is converted to the .NET Framework data provider type and then passed to the data source. However, you can specify the type of the parameter generically by setting the DbType property of the Parameter object. Supported values of the DbType property include Boolean, Byte, Single, Int32, GUID, String, Time, and Date.

You want to execute an INSERT statement using a SQL statement with the Input parameter that adds the first name and last name of an employee into the Employees table of the RedRock database. When you execute the statement, it prompts the user to enter the first name and the last name of the employee as inputs. To do this, you first specify a connection string.

Code

using System.Data.SqlClient; class Program { static void Main() { string connString = ("Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;" + "Integrated Security=True");

You then write the INSERT statement where you specify the @FirstName and @LastName as input parameters.

Code
using System.Data.SqlClient; class Program { static void Main() { string connString = ("Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;" + "Integrated Security=True"); string query1 = ("INSERT INTO Employees(LastName, FirstName) " + "VALUES(@LastName, @FirstName)");

Then you create an instance of the SQLCommand object and pass the statement and connection as its attributes.

Code
using System.Data.SqlClient; class Program { static void Main() { string connString = ("Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;" + "Integrated Security=True"); string query1 = ("INSERT INTO Employees(LastName, FirstName) " + "VALUES(@LastName, @FirstName)"); using(SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd1 = new SqlCommand(query1, conn);

Try It

You want to perform the INSERT operation using Input parameters so you instantiate the SQLParameter class. You want to set the Input parameter @LastName as the ParameterName. The existing code is: string query1 = ("INSERT INTO Employees(LastName, FirstName) " +"VALUES(@LastName, @FirstName)"); using(SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd1 = new SqlCommand(query1, conn); SqlParameter p1 = new SqlParameter(); p1.ParameterName = "MISSING CODE"; To complete the task
1. Type @LastName and click the Enter key provided Type @LastName in the MISSING CODE field and click the Enter key provided. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The @LastName input parameter is created. You then set the Value property of the SQLParameter class to Johnson because you want to insert this data into the table. You use the Direction property to specify the parameter type. In this example, you set the Direction property value to Input.

Code
using (SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd1 = new SqlCommand(query1, conn); SqlParameter p1 = new SqlParameter(); p1.ParameterName = "@LastName"; p1.Value = "Johnson"; p1.Direction = ParameterDirection.Input;

Note

You can specify an Output, InputOutput, or the ReturnValue parameter by specifying it in the Direction property. Similarly, you specify the other input parameter, @FirstName.

Code
using (SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd1 = new SqlCommand(query1, conn); SqlParameter p1 = new SqlParameter(); p1.ParameterName = "@LastName"; p1.Value = "Johnson"; p1.Direction = ParameterDirection.Input; SqlParameter p2 = new SqlParameter(); p2.ParameterName = "@FirstName"; p2.Value = "Henry"; p2.Direction = ParameterDirection.Input;

You then add the parameters to the parameter collection of the SQLCommand object.

Code
using (SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd1 = new SqlCommand(query1, conn); SqlParameter p1 = new SqlParameter(); p1.ParameterName = "@LastName"; p1.Value = "Johnson"; p1.Direction = ParameterDirection.Input; SqlParameter p2 = new SqlParameter(); p2.ParameterName = "@FirstName"; p2.Value = "Henry"; p2.Direction = ParameterDirection.Input; cmd1.Parameters.Add(p1); cmd1.Parameters.Add(p2); }

After you add the parameters to the parameter collection, you execute the SQL statements using the ExecuteNonQuery method.

Code
try { cmd1.Connection.Open(); cmd1.ExecuteNonQuery(); }

You then handle any exceptions and display it on the console. Finally, you close the data connection.

Code
try { cmd1.Connection.Open(); cmd1.ExecuteNonQuery(); } catch(SqlException ex) { Console.WriteLine(ex.Message); } cmd1.Connection.Close(); Console.WriteLine("Press Any Key to Continue"); Console.ReadLine();

Question
You want to execute a SQL statement that uses input parameters for inserting the last name and first name of an employee. You have already created an input parameter to accept the last name of an employee. Enter the code to specify @FirstName as an input parameter. Code
SqlParameter p2 = new SqlParameter(); p2.ParameterName = "@FirstName"; p2.Value = "Henry"; p2.Direction = INSERT THE MISSING CODE; cmd1.Parameters.Add(p1); cmd1.Parameters.Add(p2); try { cmd1.Connection.Open(); cmd1.ExecuteNonQuery(); } catch (SqlException ex)

{ Console.WriteLine(ex.Message); }

Answer
You use the ParameterDirection enumeration to indicate the parameter direction value as Input, Output, InputOutput, or ReturnValue. Correct answer(s):
1. ParameterDirection.Input

2. Using stored procedures with parameters


You can also use stored procedures to execute groups of SQL statements. Stored procedures help manage and optimize your database by allowing you to execute the most frequently performed tasks together. It helps simplify the execution of complex statements by allowing you to call another stored procedure from within the stored procedure. A stored procedure also executes faster because it is compiled and stored on the database server. You can also create stored procedures that accept input, output, inputoutput, and return parameters that return zero or more result sets. Although you can create stored procedures without parameters, it is recommended to use parameters. This reduces the chances of an SQL injection attack.

Supplement
Selecting the link title opens the resource in a new browser window. Learning Aid Use the learning aid Accessing and configuring stored procedures to understand how to access stored procedures from the Server Explorer. Suppose you want to execute a stored procedure to retrieve the last name of an employee by using the Output parameter. To retrieve the last name using the Output parameter, you need to pass the first name as the input value. To do this, you first create a connection string and open the connection.

Code
using System.Data.SqlClient; using System.Data; class Module1

{ static void Main() { string connString = "Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;" + "Integrated Security=True"; SqlConnection conn = new SqlConnection(connString); conn.Open();

To execute the stored procedure, you create a SQLCommand object, using the CreateCommand method of the connection object.

Code
using System.Data.SqlClient; using System.Data; class Module1 { static void Main() { string connString = "Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;" + "Integrated Security=True"; SqlConnection conn = new SqlConnection(connString); conn.Open(); SqlCommand cmd1 = conn.CreateCommand();

Then you set the properties of the SQLCommand object. The CommandType property indicates whether a stored procedure or a SQL statement is executed. In this example, you specify the CommandType property as StoredProcedure. The CommandText property is used to set the name of the stored procedure, OutputProc, that should be executed at the data source.

Graphic
The code to define the command type and command text is: cmd1.CommandType = CommandType.StoredProcedure; cmd1.CommandText = "OutputProc";

Code
using System.Data.SqlClient; using System.Data; class Module1 { static void Main() { string connString = "Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=RedRock;"

+ "Integrated Security=True"; SqlConnection conn = new SqlConnection(connString); conn.Open(); SqlCommand cmd1 = conn.CreateCommand(); cmd1.CommandType = CommandType.StoredProcedure; cmd1.CommandText = "OutputProc";

Try It
Then you use an instance of the SQLParameter class and declare the first parameter @LastName for accepting the output value for the last name. The existing code is: SqlConnection conn = new SqlConnection(connString); conn.Open(); SqlCommand cmd1 = conn.CreateCommand(); cmd1.CommandType = CommandType.StoredProcedure; cmd1.CommandText = "OutputProc"; SqlParameter p6 = new SqlParameter(); p6.ParameterName = "MISSING CODE"; To complete the task
1. Type @LastName and click the Enter key provided Type @LastName in the MISSING CODE field and click the Enter key. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The @LastName output parameter is defined. After defining the output parameter, you define p6 as an instance of the SQLParameter class. You specify the ParameterDirection as Output, and set the size of the output string as 50.

Code
SqlParameter p6 = new SqlParameter(); p6.ParameterName = "@LastName"; p6.Direction = ParameterDirection.Output; p6.Size = 50;

You then specify the input parameter @FirstName and supply a value to test the code.

Code

SqlParameter p2 = new SqlParameter(); p2.ParameterName = "@FirstName"; p2.Value = "Frank"; p2.Direction = ParameterDirection.Input;

After defining the input parameters, you pass the SQLParameter objects to the command object and execute the ExecuteNonQuery method. When the stored procedure is executed, it returns the last name as the output parameter and displays it on the command prompt using the Value property. You then close the database connection.

Graphic
The code to pass the SQLParameter objects is: cmd1.Parameters.Add(p6); cmd1.Parameters.Add(p2);

Code
cmd1.Parameters.Add(p6); cmd1.Parameters.Add(p2); try { cmd1.ExecuteNonQuery(); } catch (SqlException ex) { Console.WriteLine(ex.Message); } conn.Close(); Console.WriteLine("{0}", cmd1.Parameters["@LastName"].Value.ToString()); Console.WriteLine("Press Any Key to Continue"); Console.ReadLine(); } }

You have specified an output parameter for the stored procedure and now you want to specify the return value parameter @retval. However, you will be able to access this return value parameter only if you set up a parameter explicitly to retrieve it.

Code
using System.Data.SqlClient; class Program { static void Main()

{ String connString = ("Data Source=WIN-UXT\\SQLEXPRESS;" + "Initial Catalog=RedRock; Integrated Security=True"); SqlConnection conn = new SqlConnection(connString); conn.Open(); SqlCommand cmd1 = conn.CreateCommand(); cmd1.CommandType = CommandType.StoredProcedure; cmd1.CommandText = "ReturnValueProc";

Try It
You've defined your connection and command objects. You then define p6 as an instance of the SQLParameter class. Now you want to specify the return value parameter @Retval as the ParameterName property of the SQLParameter class. The existing code is: using System.Data.SqlClient; class Module1 { static void Main() { string connString = ("Data Source=WIN-UXT\\SQLEXPRESS;Initial Catalog=Northwind;" + "Integrated Security=True"); SqlConnection conn = new SqlConnection(connString); conn.Open(); SqlCommand cmd1 = conn.CreateCommand(); cmd1.CommandType = CommandType.StoredProcedure; cmd1.CommandText = "ReturnValueProc"; SqlParameter p6 = new SqlParameter(); p6.ParameterName = "MISSING CODE"; To complete the task
1. Type @Retval and click the Enter key provided Type @Retval in the MISSING CODE field and click the Enter key. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The @Retval parameter is defined.

After specifying the ParameterName property, you specify the DbType to set the return value. In this example, the DbType is set to Int32. Because this example uses the ReturnValue parameter, you set ReturnValue as the Direction property of the SQLParameter class.

Code
SqlConnection conn = new SqlConnection(connString); conn.Open(); SqlCommand cmd1 = conn.CreateCommand(); cmd1.CommandType = CommandType.StoredProcedure; cmd1.CommandText = "ReturnValueProc"; SqlParameter p6 = new SqlParameter(); p6.ParameterName = "@RetVal"; p6.DbType = DbType.Int32; p6.Direction = ParameterDirection.ReturnValue;

The @Retval parameter is then associated with the SQLCommand object, cmd4. You then execute the stored procedure using the ExecuteNonQuery method to retrieve the return value.

Graphic
The code to associate the @Retval parameter with the SQL command object is: cmd4.Parameters.Add(p6);

Code
SqlCommand cmd4 = conn.CreateCommand(); cmd4.CommandType = CommandType.StoredProcedure; cmd4.CommandText = "ReturnValueProc"; SqlParameter p6 = new SqlParameter(); p6.ParameterName = "@RetVal"; p6.DbType = DbType.Int32; p6.Direction = ParameterDirection.ReturnValue; cmd4.Parameters.Add(p6); try { cmd4.Connection.Open(); cmd4.ExecuteNonQuery(); }

The return value specifies the number of rows affected and is displayed on the console window.

Graphic

The code to display the return value on the console window is: Console.WriteLine("{0}", cmd4.Parameters("@RetVal").Value);

Code
SqlParameter p6 = new SqlParameter(); p6.ParameterName = "@RetVal"; p6.DbType = DbType.Int32; p6.Direction = ParameterDirection.ReturnValue; cmd4.Parameters.Add(p6); try { cmd4.Connection.Open(); cmd4.ExecuteNonQuery(); } catch (SqlException ex) { Console.WriteLine(ex.Message); } cmd4.Connection.Close(); Console.WriteLine("{0}", cmd4.Parameters("@RetVal").Value); Console.WriteLine("Press Any Key to Continue"); Console.ReadLine(); } } }

Question
Arrange the steps to execute a stored procedure with an Output parameter in the correct order. Options:
1. Assign the CommandText property with the stored procedure name and specify the CommandType property 2. Define an instance of the SQLParameter class 3. Specify the ParameterDirection as Output and the size of the output string 4. Associate the parameter with the Command object and execute the command

Answer
Correct answer(s):
Assign the CommandText property with the stored procedure name and specify the CommandType property is ranked as the first step in executing the stored procedure with an Output parameter

The CommandText and CommandType properties are set so that the specified stored procedure is executed at the data source. Define an instance of the SQLParameter class is ranked as the second step in executing the stored procedure with an Output parameter You create an instance of the SQLParameter class and specify the ParameterName, ParameterDirection, and the size of the output parameter. Specify the ParameterDirection as Output and the size of the output string is ranked as the third step in executing the stored procedure with an Output parameter Because it is an output parameter, you specify the ParameterDirection as Output and set a size for the output string. Associate the parameter with the Command object and execute the command is ranked as the fourth step in executing the stored procedure with an Output parameter You use the command.Parameters.Add method to add the parameter to the parameter collection of the command object and you then execute the command.

Question
You want to retrieve the number of rows that are affected when executing a stored procedure containing the Create and Update statements. So you decide to use the @RetVal parameter. Enter the code to associate the @Retval parameter with the SQLCommand object cmd4. Code
string query4 = ("SELECT COUNT(LastName) FROM Employees " + "WHERE FirstName=\'Bob\' RETURN"); using(SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd4 = new SqlCommand(query4, conn); SqlParameter p6 = new SqlParameter(); p6.ParameterName = "@RetVal"; p6.DbType = DbType.Int32; p6.Direction = ParameterDirection.ReturnValue; cmd4.INSERT THE MISSING CODE; }

Answer

You use the Parameters.Add method to associate the return value parameter with the SQLCommand object. Correct answer(s):
1. Parameters.Add(p6)

Summary
A command that you execute in SQL is called a SQL statement. Some SQL statements, such as queries, return result sets while others return the number of rows affected. You use the ExecuteNonQuery method of a command object to execute UPDATE, INSERT or DELETE statements and perform database operations, such as creating or dropping objects like tables and indexes. To execute SQL statements, you either pass hard-coded values or enter the values as command parameters during runtime. Command parameters improve query performance and also protect the database against SQL injection attacks. To configure a parameter, you need to specify the ParameterDirection property, provide the parameter placeholders if the data providers support them, use the methods to execute multiple SQL statements and properties of the ParameterCollection class, and specify the parameter data types. You can also use stored procedures to execute a command. A stored procedure is a set of precompiled SQL statements that is processed as a unit. You can create stored procedures with no parameters and also with parameters, such as Input, Output, InputOutput, and ReturnValue.

Table of Contents
| Print | Contents | Close |

Managing Generic Data Access with C# 2010


Learning Objectives
After completing this topic, you should be able to

recognize how to use the DBProviderFactory, DbConnection, DbCommand, and DbException objects identify the steps for using generic data access code to retrieve provider information

1. Writing generic data access code


A data-driven application may be required to retrieve data from various sources, such as SQL Server, Oracle, Web services, and XML files. In such cases, it is more efficient to write generic data access code in the data access application. To enable developers to create provider-independent data access code, ADO.NET includes abstract base classes, such as DbCommand, DbConnection, and DbAdapter. These classes are stored in the System.Data.Common namespace. Because these are abstract classes, they cannot be instantiated directly.

Note
These classes are also used as base classes for provider-specific classes, such as SqlConnection, SqlCommand, and SqlAdapter. Some of the important classes that help write generic data access code are
DbCommand

The DbCommand class provides a base class for the implementation of the concrete classes of the data provider. You use the methods and properties of the DbCommand class to represent a SQL statement or stored procedure that can be executed against a data source.
DbConnection, and

The DbConnection class indicates a connection to a database. Using the DbConnection methods, such as Close and CreateCommand, enables you to explicitly close a database connection; and create and return a DbCommand object associated with the current connection.
DbDataAdapter

You use the methods and properties of the DbDataAdapter class to represent a set of SQL commands and a database connection to fill the DataSet and update the data source.

Supplement
Selecting the link title opens the resource in a new browser window. Learning Aid Use the learning aid Classes in the System.Data.Common namespace for a complete list and description of the classes in this namespace. To write generic data access code using the abstract classes in the System.Data.Common namespace, you use the "factory" design pattern.

The "factory" design pattern provides a single interface for accessing different data sources with different providers. It involves creating a single factory object that creates a strongly typed required connection and data access objects based on the information about a provider at runtime. When implementing the "factory" design model, you use the DbProviderFactories class introduced in ADO.NET 2.0 to write generic data access code. You use the DbProviderFactories.GetFactory method to create an instance of the DbProviderFactory class. This instance is a strongly typed object based on the provider information and the connection string provided at runtime. For example, the GetFactory method will create a SqlClientFactory if it is passed the System.Data.SqlClient as the provider name. Regardless of its type, the DbProviderFactory object includes a set of methods, such as CreateConnection and CreateCommand, for accessing and retrieving data by creating instances of a provider's implementation of the data source classes. Suppose you've created a web application to update the salaries of employees. Recently, the employee data has been moved to a different database. To update the salaries, regardless of the change in database, you write generic data access code. You first import the System.Configuration and the System.Data.Common namespaces and create required variables.

Code
using System.Configuration; using System.Data.Common; class GenericDataAccessSP { static void Main() { string providerName; string connectionString = ""; bool validSelection;

In this example, you write code to enable users to select the database they want to query. And then based on the user's selection of the database, you get the connection string by creating an object of the ConnectionStringSettingsCollection class.

Code
static void Main() { bool validSelection; string providerName = ""; string connectionString = ""; do { validSelection = true;

Console.Write(("Please select a database to" + " query (Odbc, Ole, or Sql): ")); providerName = Console.ReadLine(); switch (providerName) { case "OleDB": providerName = "System.Data. OleDb"; break; case "Sql": providerName = "System.Data.SqlClient"; break; default: Console.WriteLine(("Not a valid entry." + " Please try again.")); validSelection = false; break; } } while (validSelection != true); ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings;

You then write the code to traverse the collection string and return the first string that matches the provider name.

Code
ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; foreach (ConnectionStringSettings cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString; } }

Try It
Next you declare a variable of type DbConnection. The existing code is: ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; foreach (ConnectionStringSettings cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString;

} } MISSING CODE connection; To complete the task


1. Type DbConnection and click the Enter key provided Type DbConnection in the MISSING CODE field and click the Enter key. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A variable of the type DbConnection class is declared. You create an instance of the DbProviderFactory class factory by passing the provider name to the DbProivderFactories.GetFactory method.

Code
ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; foreach (ConnectionStringSettings cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString; } } DbConnection connection = null; DbProviderFactory factory = null; try { factory = DbProviderFactories. GetFactory(providerName);

Then you create the connection instance by using the CreateConnection method of the factory object.

Code
connection = factory.CreateConnection(); connection.ConnectionString = connectionString;

Try It

After creating the connection, you execute the update_salary stored procedure. For this, you create an object of the DbCommand class. The existing code is: catch(DbException ex) { Console.WriteLine(ex.Message); } using(connection) { try { DbCommand command = connection.MISSING CODE; } To complete the task
1. Type CreateCommand()and click the Enter key provided Type CreateCommand() in the MISSING CODE field and click the Enter key. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

An object of the DbCommand class is created. The update_salary stored procedure takes the employee ID and the increment value of the employee salary as inputs.

Code
using(connection) { try { // Create command DbCommand command = connection.CreateCommand(); command.CommandText = "update_salary"; command.CommandType = CommandType.StoredProcedure; }

So you declare generic parameters to accept these input values by using the ParameterName property.

Code

DbParameter param1 = DbParameter param2 = param1.ParameterName param1.Value = 1; param2.ParameterName param2.Value = 1005;

factory.CreateParameter(); factory.CreateParameter(); = "@empID"; = "@increment";

Then you add the parameters and call the ExecuteNonQuery method to execute the stored procedure. When the stored procedure is executed, the salary of the employee with ID 1 is incremented by 1005.

Code
command.Parameters.Add(param1); command.Parameters.Add(param2); connection.Open(); command.ExecuteNonQuery(); Console.WriteLine("Completed.");

Try It
You then create an object of the DbException class to handle any errors and display the message to the console using the Message property of the DbException class. The existing code is: command.Parameters.Add(param1); command.Parameters.Add(param2); connection.Open(); command.ExecuteNonQuery(); Console.WriteLine("Completed."); } catch(MISSING CODE ex) { Console.WriteLine(ex.Message); } To complete the task
1. Type DbException and click the Enter key provided Type DbException in the MISSING CODE field and click the Enter key. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to handle errors and display them on the console is added.

Finally, you check the database state. If the data connection is open, you explicitly close it by calling the Close method so that the connection is returned to the pool for reuse.

Code
command.Parameters.Add(param1); command.Parameters.Add(param2); connection.Open(); command.ExecuteNonQuery(); Console.WriteLine("Completed."); } catch(DbException ex) { Console.WriteLine(ex.Message); } finally { if (connection.State == ConnectionState.Open) { connection.Close(); } } } Console.ReadLine(); }

Question
You want to retrieve a connection string based on an option that the user selects. You have already declared connection as a variable of the DbConnection type and instantiated the DbProviderFactory object. Enter the code to generate the connection. Code
DbConnection connection = null; DbProviderFactory factory = null; try { factory = DbProviderFactories. GetFactory(providerName); connection = INSERT THE MISSING CODE; connection.ConnectionString = connectionString; }

Answer
You use the CreateConnection method of the factory object to create the connection. Correct answer(s):

1. factory.CreateConnection()

Question
You've created the update_salary stored procedure to recalculate and update the salaries of employees based on their employee ID. Enter the code to execute it from within a web application. Code
try { factory = DbProviderFactories. GetFactory(providerName); connection = factory.CreateConnection(); connection.ConnectionString = connectionString; } catch(DbException ex) { Console.WriteLine(ex.Message); } using (connection) { try { INSERT THE MISSING CODE command = connection.CreateCommand(); command.CommandText = "update_salary"; command.CommandType = CommandType. StoredProcedure;

Answer
You use the methods and properties of the DbCommand class to represent a stored procedure to be executed against a data source. Correct answer(s):
1. DbCommand

Question
You have written provider-independent code and now want to handle any errors that are raised. Specify ex as an instance of the appropriate class to handle any data source errors. Code
DbConnection connection = null; DbProviderFactory factory = null;

try { factory = DbProviderFactories. GetFactory(providerName); connection = factory.CreateConnection(); connection.ConnectionString = connectionString; } catch(INSERT THE MISSING CODE) { Console.WriteLine(ex.Message); }

Answer
You use the DbException base class to handle all exceptions thrown by the data source. Correct answer(s):
1. DbException ex

Question
As a web developer, you've created provider-independent code to access data from any data source. You need to create instances of the DbProviderFactory, DbConnection, and DbCommand classes. What do these classes help you do? Options:
1. 2. 3. 4. Help retrieve a connection string from a collection based on the data source the user selects Handle any data connection errors that may arise when connecting to the database Help execute a stored procedure from within a web application Help create instances of a provider's implementation and connect to the database

Answer
Option 1: Incorrect. Based on the user's selection, a connection string that matches the requested data source can be retrieved from the collection by using the ConnectionStringSettingsCollection class. Option 2: Incorrect. The DbException class helps handle any database exceptions that are thrown. Option 3: Correct. You call the CreateCommand method of the DbCommand class to execute SQL statements as well as stored procedures.

Option 4: Correct. The DbProviderFactory and DbConnection classes are instantiated to help create instances of a provider's implementation and also to establish a connection to the database. Correct answer(s): 3. Help execute a stored procedure from within a web application 4. Help create instances of a provider's implementation and connect to the database

2. Retrieving provider information


Each of the .NET data providers supports a factory-based class and each of the data provider registers configuration information in the DbProviderFactory section of the machine.config file on the local computer. Some of the data providers, such as System.Data.SqlClient and System.Data.OleDb are registered factory classes. The entries include a name attribute for the relevant provider. The invariant attribute in the configuration entry identifies the data provider.

Code
<system.data> <DbProviderFactories> <add name="Odbc Data Provider" invariant="System.Data.Odbc" descriptin=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <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" /> <add name="SQL Server CE Data Provider" invariant="Microsoft.SqlServerCe.Client" description=".NET Framework Data Provider for Microsoft SQL Server 2005 Mobile Edition" type="Microsoft.SqlServerCe.Client.SqlCeClientFactory, Microsoft.SqlServerCe.Client, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" /> </DbProviderFactories> </system.data>

You can retrieve information about available data providers from the machine.config file by using the GetFactoryClasses method of the DbProviderFactories class. Calling this method returns a DataTable named DbProviderFactories containing a DataRow for each provider entry in the machine.config file.

Code
using System.Data.Common; class Program { static void Main() { DataTable dTable = DbProviderFactories.GetFactoryClasses(); DbProviderFactory provider;

A DataRow for a specific provider can be selected and the details can be used to create a strongly typed provider factory. The methods of the provider object, such as CreateConnection and CreateCommand, can then be used to establish data access.

Code
using System.Data.Common; class Program { static void Main() { DataTable dTable = DbProviderFactories.GetFactoryClasses(); DbProviderFactory provider; DataRow row = null; DataColumn column = null;

The DataTable columns are Name, Description, InvariantName, and AssemblyQualifiedName. These columns use the values set for these attributes in machine.config:
name

The name attribute displays the name of the data provider. For example, SQLClient Data Provider is the data provider name that is returned by the GetFactory method.
description

A short description of the data provider is presented in the Description column. An example of the description attribute value would be .Net Framework data provider for OracleServer.
invariant, and

The InvariantName column which displays the invariant attribut value helps you refer to the data provider programmatically. For example, the code would return System.Data.SqlClient if the data source is SQL Client. type

The AssemblyQualifiedName column displays the type attribute value which denotes the fully qualified name of the factory class that contains the required information to instantiate the object of the class. This is an example of the AssemblyQualifiedName. System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.

Try It
You want to create a provider factory based on information retrieved from the machine.config file so you pass the required DataRow object to the GetFactory method of the DbProviderFactories class. The existing code is: DataRow row; DataColumn column; foreach (row in table.Rows) { Console.WriteLine("- NEW ROW - "); provider = DbProviderFactories.MISSING CODE } To complete the task
1. Type GetFactory(row) and click the Enter key provided Type GetFactory(row) in the MISSING CODE field and click Enter key. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The DataRow object is passed to the GetFactory method. You display the column names from the DataTable and use the GetFactory method to retrieve the provider from each row in the data table.

Code
Console.WriteLine("Provider: {0}", provider.ToString()); foreach (DataColumn column in table.Columns) { Console.WriteLine("Column Name: {0}, {1}", column.ColumnName, row[column]); } } Console.ReadLine(); }

Question
You have created a web application that allows users to select a database provider from a table of available providers. Arrange the steps for using the generic data access code to retrieve the provider information and establish data access. Options:
1. Ensure a reference to the System.Data.Common namespace is provided 2. Use the GetFactoryClasses method to retrieve information from the machine.config file 3. Display values obtained from the machine.config file 4. Use the GetFactory method to create a DbProviderFactory object provider factory based on information in a selected DataRow 5. Use the provider object methods to establish data access

Answer
Correct answer(s):
Ensure a reference to the System.Data.Common namespace is provided is ranked the first step in using generic data access code to retrieve provider information and create data access You obtain the required factory classes in the System.Data.Common namespace. Use the GetFactoryClasses method to retrieve information from the machine.config file is ranked the second step in using generic data access code to retrieve provider information and create data access The GetFactoryClasses method returns the information from the DbProviderFactories section of the machine.config file in a DataTable. Display values obtained from the machine.config file is ranked the third step in using generic data access code to retrieve provider information and create data access To display values obtained from the machine.config file in their corresponding rows and columns, you loop through the DataRows of the DataTable. Use the GetFactory method to create a DbProviderFactory object provider factory based on information in a selected DataRow is ranked the fourth step in using generic data access code to retrieve provider information and create data access Use the GetFactory method to create a factory object based on the given provider attributes.

Use the provider object methods to establish data access is ranked the fifth step in using generic data access code to retrieve provider information and create data access You use the methods of the factory object such as CreateConnection and CreateCommand to establish data access.

Question
You want to create a provider factory based on the information about available providers retrieved from the machine.config file. Specify the appropriate class to create the factory object. Code
void Main() { DataTable dTable = DbProviderFactories.GetFactoryClasses(); DbProviderFactory provider; foreach (DataRow row in dTable.Rows) { Console.WriteLine("- NEW ROW - "); provider = INSERT THE MISSING CODE.GetFactory(row); Console.WriteLine("Provider: {0}", provider.ToString());

Answer
You pass the DataRow object to the GetFactory method of the DbProviderFactories class to create the required factory object. Correct answer(s):
1. DbProviderFactories

Summary
ADO.NET 4 includes abstract base classes, such as DbCommand, DbConnection, and DbAdapter, to write provider-independent code. These classes are stored in the System.Data.Common namespace. Because these classes are abstract, they cannot be directly instantiated. To write generic data access code, you use the DbProviderFactories class. You use the DbProviderFactories.GetFactory method to create an instance of the DBProviderFactory class. This instance is a strongly typed object based on the provider information and the connection string provided at runtime. The DbProviderFactory object includes a set of methods, such as CreateConnection and

CreateCommand,

that you can use to access and retrieve data by creating instances of a provider's implementation of the data source classes. Each of the data providers registers configuration information in the DbProviderFactory section of the machine.config file on the local computer. You can retrieve information about data providers from the machine.config file by using the GetFactoryClasses method of the DbProviderFactories class.

Table of Contents
| Print | Contents | Close |

Coding Generic Data Access with C# 2010


Learning Objectives
After completing this topic, you should be able to

retrieve a connection string from a configuration file, based on a provider name create a DBProviderFactory and DBConnection create a DBCommand object handle data source errors

Exercise Overview
In this exercise, you're required to retrieve a connection string from a configuration file, manage generic data access code, and handle exceptions. This involves the following tasks:

retrieving a connection string creating factory and connection objects creating a DbCommand object handling data source errors

Retrieving a connection string


You are creating a data-driven application that retrieves data from any data source, such as Microsoft SQL Server, Oracle, and Web services. You want to, therefore, write generic, provider-independent code, to retrieve data from any data source. First you need to write code to retrieve a connection string from the configuration file. You begin by prompting the users to select the database they want to query.

Question
You retrieve the connection strings collection. Now complete the code to access a connection string in the collection that maps to the selected provider. Code
ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; foreach (INSERT THE MISSING CODE cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString; } } do { validSelection = true; Console.Write(("Please select a database to" + " query (Odbc, Ole, or Sql): ")); providerName = Console.ReadLine(); switch (providerName) { case "Odbc": providerName = "System.Data.Odbc"; break;

Answer
You use the ConnectionStringSettings class to access the string mapping to the selected provider. Correct answer(s):
1. ConnectionStringSettings

Question
Now declare the connection using the appropriate type. Code
case "Ole": providerName = "System.Data.OleDb"; break; case "Sql": providerName = "System.Data.SqlClient"; break; default: Console.WriteLine(("Not a valid entry." +

" Please try again.")); validSelection = false; break; } }while (validSelection != true); ConnectionStringSettingsCollection settings = configurationManager.ConnectionStrings; foreach (ConnectionStringSettings cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString; } } INSERT THE MISSING CODE connection = null;

Answer
You declare a connection as a DbConnection type. Correct answer(s):
1. DbConnection

You have successfully completed the code to retrieve a connection string mapping to a selected provider, and you have initialized a DbConnection.

Creating factory and connection objects


You now need to create connection and provider factory objects for generic data access.

Question
You also want to declare and initialize a variable that will be used to instantiate a factory provider object. Complete the code to specify the correct type for the variable. Code
ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; foreach (ConnectionStringSettings cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString;

} } DbConnection connection = null; INSERT THE MISSING CODE factory = null;

Answer
You declare a variable of the type DbProviderFactory. Correct answer(s):
1. DbProviderFactory

Question
You create the factory object by passing the provider name to the GetFactory method. Complete the code to provide the class for the GetFactory method. Code
ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; foreach (ConnectionStringSettings cs in settings) { if (cs.ProviderName.Equals(providerName)) { connectionString = cs.ConnectionString; } } DbConnection connection = null; DbProviderFactory factory = null; try { factory = INSERT THE MISSING CODE.GetFactory(providerName); connection = factory.CreateConnection(); connection.ConnectionString = connectionString; }

Answer
The DbProviderFactories class contains the GetFactory method, which is used to return an instance of the DbProviderFactory class. Correct answer(s):
1. DbProviderFactories

You have successfully created the DbProviderFactory object for the specified provider.

Creating a DbCommand object


You have created the update_salary stored procedure that updates the salaries of selected employees based on the employee ID. You want to call this stored procedure and execute it from within the application.

Question
Complete the code to instantiate the command object by specifying the correct type for the object created by the CreateCommand method of the connection. Code
using(connection) try { INSERT THE MISSING CODE command = connection.CreateCommand(); command.CommandText = "update_salary"; }

Answer
You need to instantiate an object of the DbCommand class. Correct answer(s):
1. DbCommand

Question
Now specify that a stored procedure needs to be executed. Code
using(connection) try { DbCommand command = connection. CreateCommand(); command.CommandText = "update_salary"; command.CommandType = CommandType. INSERT THE MISSING CODE; DbParameter param1 = factory. CreateParameter(); DbParameter param2 = factory. CreateParameter(); param1.ParameterName = "@empID";

param1.Value = 1; param2.ParameterName = "@increment"; param2.Value = 1005; }

Answer
You use the CommandType property of the Command object to specify that a stored procedure is to be executed. The Text property specifies the stored procedure name. Correct answer(s):
1. StoredProcedure

You have successfully created a DbCommand object to call the update_salary stored procedure.

Handling data-source errors


In the process of connecting to a database, the data connection timed out. To handle such exceptions, you want to use the DbException class.

Question
Declare an object ex of the appropriate type to catch this and other data source exceptions. Code
catch(INSERT THE MISSING CODE ex) { Console.WriteLine(ex.Message); } finally { if (connection.State == ConnectionState.Open) { connection.Close(); } }

Answer
To call the methods and properties of the DbException class, you need to first declare an object of the class. Correct answer(s):
1. DbException

You have successfully used the DbException class to handle errors thrown by the data source.

| Print | Contents | Close |

Managing ADO.NET Transactions with C# 2010


Learning Objectives
After completing this topic, you should be able to

recognize how the .NET Framework APIs can be used to configure transactions identify the code for implementing implicit and explicit transactions and for managing concurrency recognize the ways for managing and implementing transactions using the System.Transactions namespace

1. Managing transactions
To secure data and ensure that multiple, related operations are performed concurrently, you use transactions. Transactions consist of similar operations that succeed or fail as a unit. For example, in an online shopping application, you may have to enter the order details and the customer details simultaneously. As the Orders and Customer tables in the data source are interdependent, they need to be updated concurrently. If one action fails, then the data is not in a consistent state. To avoid this, you can use a transaction. To ensure that all the tasks in a transaction succeed or fail as a unit they need to adhere to ACID properties. These include
atomic A transaction is considered atomic when all the tasks in it execute as a single action. When a single task in a transaction fails, all the other tasks are rolled back and are not committed to the data source. In this way, an atomic transaction prevents partial data updates to the data source. consistent A transaction must maintain the data in a consistent state regardless of the success or failure of the operation. When a transaction is successful, the data is modified from one consistent state to another. However, a failed transaction rolls back to its previous consistent state. isolated, and

Each transaction is isolated from similar transactions that are running concurrently in the system. Therefore, any data modification from one transaction does not affect the state of other transactions. durable A transaction is durable when it is able to retrieve the data updates committed to the data source even when the system crashes. The updated data in every transaction is saved in a log file and can be recovered later.

You can perform transactions on single resources such as a database, or on multiple resources such as databases or servers. When a transaction is restricted to a single resource, it is called a local transaction. On the other hand, a distributed transaction involves more than one resource. The resources are managed by resource managers and all the transactions from the resources should commit or rollback as a single unit.

Question
Identify the features of transactions. Options:
1. 2. 3. 4. Interferes with the state of other transactions Executes all the tasks in a single action Commits the data during system failure Maintains data consistency after transaction

Answer
Option 1: Incorrect. According to the ACID properties of a transaction, a transaction is isolated from other transactions running in the system. Option 2: Correct. A transaction is said to be atomic when it is executed or rolled back without partial updates. Option 3: Incorrect. A transaction either commits or rolls back data as a single unit. During system failure, the transaction rolls back to its previous consistent state. Option 4: Correct. The transaction performs all its tasks as a single unit so the consistency of data is preserved. Correct answer(s):

2. Executes all the tasks in a single action 4. Maintains data consistency after transaction To create transactions, you can use either Transact-SQL statements or database Application Programming Interface or API functions. For example, you can use Transact-SQL statements such as BEGIN TRANSACTION, ROLLBACK TRANSACTION or COMMIT TRANSACTION to delete the employee details in two tables. Transact-SQL statements enable you to create transactions directly within stored procedures which can offer performance advantages.

Code
BEGIN TRANSACTION DELETE FROM EmpInfo WHERE DeptID = @DepartmentID IF @@ERROR <> 0 BEGIN ROLLBACK RAISERROR('Error in deleting employees in spRemoveDeptTransaction.', 16, 1) RETURN END DELETE FROM JobType WHERE DepartmentID = @DepartmentID IF @@ERROR <> 0 BEGIN ROLLBACK RAISERROR ('Error in deleting department in spRemoveDeptTransaction.', 16, 1) RETURN END COMMIT TRANSACTION

Note
You should not use both the Transact-SQL and database API methods to manage transactions.

Question

You want to use Transact-SQL statements to manage transactions. What are the features of Transact-SQL? Options:
1. 2. 3. 4. Decreases performance of the application Uses BEGIN TRANSACTION and COMMIT TRANSACTION statements Allows the use of an END TRANSACTION statement Enables you to create transactions in stored procedures

Answer
Option 1: Incorrect. Since the Transact-SQL statements help you manage transactions in stored procedures, they can help to increase application performance. Option 2: Correct. The Transact-SQL statements help you create transactions directly within stored procedures, so they can help to increase performance. Option 3: Incorrect. The Transact-SQL transaction method uses statements, such as BEGIN TRANSACTION, ROLLBACK TRANSACTION or COMMIT TRANSACTION to manage transactions. However, statements can be grouped within BEGIN .. END blocks. Option 4: Correct. The Transact-SQL transaction method uses statements, such as BEGIN TRANSACTION, ROLLBACK TRANSACTION or COMMIT TRANSACTION to manage transactions. Correct answer(s): 2. Uses BEGIN TRANSACTION and COMMIT TRANSACTION statements 4. Enables you to create transactions in stored procedures

2. Using .NET Framework APIs


You can use .NET Framework APIs to create transactions. The framework provides different models to configure transactions. Each model has its own advantages and limitations. There are a few transaction models available.
ADO.NET When you want to use a single database or a resource, you can use the ADO.NET transaction model. Although this model is simple, it is limited to local transactions with a single database or a resource.

Enterprise Services The Enterprise Services transaction model deals with the limitations of the ADO.NET transaction model and uses the System.EnterpriseServices namespace. The transactions in this model function independently of the database and you can use it to access more than one database or resource. You do not explicitly create a transaction object in this model but you implement transactions declaratively. However, this model works only on the principle of distributed transactions even if one database is used. System.Transactions Using the System.Transactions namespace of the .NET Framework, you can implement transactions in a simple way. In this model, you use the TransactionScope object to group statements in a transaction. The System.Transactions model also differentiates between single and multiple resources and functions accordingly. Therefore, it is the most efficient model.

In the ADO.NET transaction model, after a connection to the database is established, you create a transaction object of the SqlTransaction class. As transactions are controlled by the Connection object in ADO.NET, you instantiate a local connection using the BeginTransaction method. For example, you have created an application for Investment Works company. The application manages details of employees and their jobs. When an employee quits the company, you want the details of the employee to be simultaneously deleted from the EmpInfo and JobType tables. You can create a local transaction that deals with both the EmpInfo and JobType tables using a single database connection.

Code
class Module1 { public class AdoNetTransactionClass { public static void Main() { SqlConnection myConn; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); SqlCommand myCommand = new SqlCommand(); myConn.Open(); myCommand.Connection = myConn; SqlTransaction myTrans;

myTrans = myConn.BeginTransaction("DemoTransaction"); myCommand.Transaction = myTrans;

You use the try block with two commands to delete the employee and their job type information. When both the records are deleted from the database, the changes are committed.

Code
try { myCommand.CommandText = "Insert into EmpInfo" + "(EmpID, EmpName, DeptID) VALUES" + " \'456794\', \'Jennifer Westlein\', \'98\')"; myCommand.ExecuteNonQuery(); myCommand.CommandText = "Insert into JobType" + "(EmployeeID, JobType, DepartmentID) VALUES" + " (\'456794\', \'Stock Broker\', \'98\')"; myCommand.ExecuteNonQuery(); myCommand.CommandText = "DELETE FROM EmpInfo" + " WHERE DeptID = \'98\'"; myCommand.ExecuteNonQuery(); myCommand.CommandText = "DELETE FROM JobType" + " WHERE DepartmentID = \'98\'"; myCommand.ExecuteNonQuery(); myTrans.Commit(); Console.Write("Both Records have been" + " deleted from the database!"); Console.ReadLine();

If there is an error in the transaction or if the connection is closed before the transaction completes, the Catch block rolls back the changes to its previous state.

Code
catch(Exception e) { myTrans.Rollback(); Console.WriteLine(e.ToString()); Console.Write("Neither record has been deleted from the database!"); Console.ReadLine(); } finally { myConn.Close(); } }

} }

You can use the Enterprise Services transaction model to perform transactions in multiple databases. The Enterprise Services model uses the two-phase commit protocol and manages distributed transactions from multiple databases using the transaction manager. In this example, you use the Enterprise Services to transfer money from one account to another declaratively. You use the AutoComplete attribute to commit the transaction with no errors. And the TransactionOption.Required attribute of the transaction specifies that the objects that are created in the MyAccountManager class works as a transaction.

Code
[Transaction(TransactionOption.Required)] class MyAccountManager : ServicedComponent { [AutoComplete()] private void TransferMoney(Account fromAcct, Account toAcct, double amtTransfered) { fromAcct.Withdraw(amtTransfered); toAcct.Deposit(amtTransfered); } }

To overcome the shortcomings of the ADO.NET and the Enterprise Services model, the .NET Framework includes the System.Transactions namespace model. The model uses the TransactionScope object to group a set of statements in a scope. The Complete method is called when the transaction is ready to commit. When an exception occurs, the data is rolled back. Depending on the number of resources or domains used, the System.Transactions namespace implements a single or a distributed transaction. You can also use nested transaction scopes and specify how each interacts with the other. In addition, you can specify the timeout and isolation levels of the transaction.

Question
You want to create a transaction with multiple resources. Which feature of the .NET Framework APIs helps you configure distributed transactions? Options:
1. Implements classes from the System.EnterpriseServices namespace

2. Commits a transaction in a single phase 3. Implements classes from the System.Transactions namespace 4. Depends on the database to implement transactions

Answer
Option 1: Correct. The System.EnterpriseServices namespace provides a two-commit protocol and a distributed transaction manager to configure transactions. Option 2: Incorrect. You can use only a single resource or a database to manage transactions in a single phase. To enable distributed transactions, you implement from the System.EnterpriseServices and System.Transactions namespaces. Option 3: Correct. The System.Transactions namespace automatically detects a distributed transaction and functions accordingly. It uses the TransactionScope class to create a transaction. Option 4: Incorrect. The System.EnterpriseServices and System.Transactions namespace enable you to create distributed transactions. They are independent of the database. Correct answer(s): 1. Implements classes from the System.EnterpriseServices namespace 3. Implements classes from the System.Transactions namespace

Question
You want to create a transactional application from the System.EnterpriseServices namespace. Which features of the model help you configure a transaction? Options:
1. 2. 3. 4. Uses the BeginTransaction method to instantiate transaction Uses two-phase commit protocol Depends on the database Provides transactions declaratively

Answer
Option 1: Incorrect. The BeginTransaction method is used to start a transaction in the ADO.NET transaction model. It uses only one resource or database to complete a transaction.

Option 2: Correct. The Enterprise Services transaction model uses the System.EnterpriseServices namespace to provide a two-commit protocol and a distributed transaction manager to configure transactions. Option 3: Incorrect. The System.EnterpriseServices and System.Transactions namespace enable you to create distributed transactions. They are independent of the database. Option 4: Correct. The Enterprise Services transaction models implements transactions declaratively, but it uses the distributed transaction by default. Correct answer(s): 2. Uses two-phase commit protocol 4. Provides transactions declaratively

3. Using the System.Transactions namespace


To create transactions, you can also use the new transactional programming models of the System.Transactions namespace. This namespace simplifies transactional programming by providing an implicit programming model with the TransactionScope class where a Lightweight Transaction Manager, also known as LTM provided by System.Transactions automatically manages the transaction. When multiple functions or threads are involved, the System.Transactions also provides an explicit programming model using the CommittableTransaction class derived from the Transaction class. This requires the transaction to be explicitly managed within the application. The CommittableTransaction class includes all the functionality provided by the Transaction class including a Rollback method. However, it additionally includes a Commit method which is absent in the Transaction class. This allows a transaction object to be passed to other methods while still controlling when the transaction is committed. The implicit programming model of System.Transactions namespace automatically manages both local and distributed transactions when working with the .NET Provider for SQL Server. The model uses a lightweight local transactional model if the application is accessing a single resource. However, if the application needs to access more than one resource or database, then the transaction is automatically promoted to use the distributed transactional model. While promoting from a local transaction to a distributed transaction, the LTM provided by the System.Transactions namespace coordinates with the Microsoft Distributed Transaction Coordinator or MSDTC.

This type of escalation performed by the System.Transactions infrastructure when multiple distributed resources are involved is called Dynamic Escalation. Additionally, when only a single remote resource, such as a database, is involved, the System.Transactions infrastructure allows the database to control the transaction. This type of escalation is called Promotable Single Phase Enlistment, also known as PSPE and it only enlists the help of the MSDTC if it is required.

Note
In SQL Server 2008, a local transaction is not promoted to a distributed transaction when another connection is added. This is because the connection reset mode in the SQL Server 2008 does not rollback local transactions, so the connection can be reused. To control access to the data from the resources during a transaction, the System.Transactions namespace defines three types of trust level permissions for participating code. These include the following attributes:

the AllowPartiallyTrustedCallers or APTCA attribute for transactions within an application domain the DistributedTransactionPermission or DTP attribute for distributed transactions, and full trust permission for applications that use durable resources

There are a number of ways you can configure transactions. For instance, you can nest a transaction scope directly within another. Or you can nest scopes indirectly by calling a method with a TransactionScope constructor from within another method using a TransactionScope constructor. You can use the TransactionScopeOption enumerator with the TransactionScope constructor to specify whether a transaction is required, whether a new transaction should be created, or whether the ambient transaction context should be suppressed. You can use the TransactionOptions structure to configure behavior for an explicit or implicit transaction. For instance, you can use the Timeout property to automatically abort the transaction if it does not execute in the defined time. You can also define the isolation level of a transaction by using the TransactionOptionsIsolationLevel property. The isolation level is defined as Serializable by default. Suppose you have created an application that manages employees and their job details. When you want to delete the details of an employee, you want to ensure that the data is simultaneously deleted from both the EmpInfo and JobType tables of the data source. To perform this operation, you want to implement a transaction using the explicit programming model. This model uses the CommittableTransaction class within the System.Transactions namespace.

Code
using System.Data; using System.Data.SqlClient; using System.Transactions;

To begin, you create SqlConnection and then associate the connection object with a command object.

Code
using System.Data; using System.Data.SqlClient; using System.Transactions; static class Module1 { public class AsyncCommittableTransClass { SqlConnection myConn; SqlCommand myCommand = new SqlCommand(); public static void main() { AsyncCommittableTransClass actc = null; actc = new AsyncCommittableTransClass(); actc.myConn = new SqlConnection("Data Source=" + "(local);Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); actc.myCommand.Connection = actc.myConn; actc.myConn.Open(); actc.MyTransactionalWork(); }

Creating an instance of the CommittableTransaction class does not automatically set an ambient transaction, so you need to explicitly set this as the ambient transaction. And because it contains the code that executes the transaction, it is required by the resource manager to manage a transaction. To set an ambient transaction, you first create a transaction object, in this case oldAmbient, and then use its Current property.

Code
using System.Data; using System.Data.SqlClient; using System.Transactions; static class Module1 { public class AsyncCommittableTransClass { SqlConnection myConn; SqlCommand myCommand = new SqlCommand(); public static void main() { AsyncCommittableTransClass actc = null;

actc = new AsyncCommittableTransClass(); actc.myConn = new SqlConnection("Data Source=" + "(local);Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); actc.myCommand.Connection = actc.myConn; actc.myConn.Open(); actc.MyTransactionalWork(); } public void MyTransactionalWork() { Transaction oldAmbient = Transaction.Current;

Try It
You then create a committable transaction and set it to the ambient transaction. The existing code is:

public void MyTransactionalWork() { Transaction oldAmbient = Transaction.Current; CommittableTransaction committableTransaction = new MISSING CODE(); Transaction.Current = committableTransaction; To complete the task
1. Type CommittableTransaction and click the Enter key provided Type CommittableTransaction in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to create a committable transaction is typed. You can then explicitly call the Commit method to commit the transaction. The CommittableTransaction class allows a transaction to be committed asynchronously. In this case, you process the asynchronous commit using a callback method that is called when the transaction commit is complete.

Code
AsyncCallback callback; callback = new System.EventHandler(this.OnCommitted);

Then you use the try block to add the commands that perform the transaction. In this example, you want to delete records from the EmpInfo and JobType tables that has the DepartmentID value as 98.

Code
try { myCommand.CommandText = ("Insert into EmpInfo" + ("(EmpID, EmpName, DeptID) VALUES" + " (\'456794\', \'Jennifer Westlein\', \'98\')")); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("Insert into JobType" + ("(EmployeeID, JobType, DepartmentID) VALUES" + " (\'456794\', \'Stock Broker\', \'98\')")); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("DELETE FROM EmpInfo" + " WHERE DeptID = \'98\'"); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("DELETE FROM JobType" + " WHERE DepartmentID = \'98\'"); myCommand.ExecuteNonQuery();

Try It
To commit the transaction asynchronously, you can use the BeginCommit and EndCommit methods of the CommittableTransaction class. You want to use the BeginCommit method to commit the transaction with no errors. The existing code is:

committableTransaction.MISSING CODE(callback, null); To complete the task


1. Type BeginCommit and click the Enter key provided Type BeginCommit in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to commit the transaction asynchronously is entered. Now the callback method is called when the data commits the transaction asynchronously.

Code
committableTransaction.BeginCommit(callback, null);

You then restore the ambient transaction.

Code
finally { Transaction.Current = oldAmbient; } }

You can use the EndCommit method to check if the transaction had successfully committed. This method raises a TransactionException when there is an error. The changes are then rolled back and removed from the data source.

Graphic
The code to check if the transaction is successfully committed is: committableTransaction.EndCommit(asyncResult)

Code
public void OnCommitted(IAsyncResult asyncResult) { CommittableTransaction committableTransaction = default(CommittableTransaction); committableTransaction = asyncResult as CommittableTransaction; Debug.Assert((committableTransaction) != null); try { using (committableTransaction) { committableTransaction.EndCommit(asyncResult); } } catch (TransactionException e) { committableTransaction.Rollback(); Console.WriteLine(e.ToString()); Console.Write("Neither record has been deleted from the database!"); Console.ReadLine(); } finally { myConn.Close(); } } } }

Now you want to manage the transaction more efficiently. To delete the employee details from the EmpInfo table, you decide to use the TransactionScope class to implement the transaction implicitly.

Code
using System.Transactions; using System.EnterpriseServices; using System.Data.SqlClient; class Module1 { static void Main() { string connString = "Data Source=" + ".\\SQLEXPRESS;AttachDbFilename=\"C:\\" + ("Transactions2\\NORTHWND.MDF\";" + "Integrated Security=True;User Instance=True"); myCommand.CommandText = ("DELETE FROM EmpInfo" + " WHERE DeptID = \'98\'"); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("DELETE FROM JobType" + " WHERE DepartmentID = \'98\'"); myCommand.ExecuteNonQuery();

To begin an implicit transaction, you first create an instance of the TransactionScope class with a Using block. After the instance is created, the transaction manager decides which transaction to perform. It selects a transaction based on the availability of an ambient transaction and the value of the TransactionScopeOption parameter of the constructor.

Code
using(TransactionScope scope = new TransactionScope())

After creating the SqlConnection object and associating it with the commands, you provide the code to execute the query in a Try block.

Graphic
The code to execute the query in a Try block is: cmd.ExecuteNonQuery()

Code

using (SqlConnection connection = new SqlConnection(connString)) { SqlCommand cmd = new SqlCommand(query, connection); connection.Open(); try { cmd.ExecuteNonQuery(); } catch (SqlException ex) { Console.WriteLine(ex.Message); }

When the data is deleted successfully from the table, you can inform the transaction manager to commit the transaction by calling the Complete method. If you don't call this method, then the transaction manager considers this as an error or an exception and aborts the transaction. The Complete method, however, does not commit the transaction. It is performed by the transaction manager.

Graphic
To inform the transaction manager to commit the transaction is: scope.Complete();

Code
scope.Complete(); } } Console.WriteLine("Press any key to continue..."); Console.ReadLine(); } }

Question
While creating an implicit transaction, you want to ensure that the transaction does not form a deadlock. Which code helps you avoid this? Options:
1. 2. 3. 4.
BeginCommit Isolation Timeout EndCommit

Answer
Option 1: Incorrect. You use the BeginCommit method to attempt the committing process of explicit transactions asynchronously. Option 2: Incorrect. The TransactionOptions.IsolationLevel property is used to set the isolation level of a transaction. Option 3: Correct. To avoid a deadlock in the transaction process, you can use the timeout value of the TimeSpan property. Option 4: Incorrect. To check if the transaction has successfully completed in an explicit transaction, you use the EndCommit method. Correct answer(s): 3. Timeout

Question
You want to create a transaction for an online shopping application that saves the customers and their orders data to the database. You are using an explicit model. Which class or method enables you to commit the transaction? Options:
1. 2. 3. 4.
Transaction TransactionScope CommittableTransaction TransactionOptions

Answer
Option 1: Incorrect. The Transaction object does not have the commit method, so it is not used to commit the transaction. Option 2: Incorrect. You use the TransactionScope object to create an implicit transaction. Option 3: Correct. You use the Commit method of the CommittableTransaction object to commit the data. Option 4: Incorrect. TransactionOptions is used to set behaviors such as the timeout and isolation features of the transaction. Correct answer(s):

3. CommittableTransaction

Question
You are using the explicit programming model of the Systems.Transaction namespace to create a transaction. Which code helps you explicitly set an ambient transaction? Code
public void MyTransactionalWork() { Transaction oldAmbient = Transaction.Current; CommittableTransaction committableTransaction = new CommittableTransaction();

Answer
The ambient transaction contains the code that executes in a transaction. You can create and set it using the Current property of the Transaction object. Correct answer(s):
1. Current

Question
You want to finalize an implicit transaction. Which code helps you inform the transaction manager that it is ready to commit the transaction? Code
class MyAccountManager { private void TransferMoney(Account fromAcct, Account toAcct, double amtTransfered) { TransactionScope MyScope = new TransactionScope(); fromAcct.Withdraw(amtTransfered); toAcct.Deposit(amtTransfered); MyScope.INSERT THE MISSING CODE; } }

Options:
1. Complete() 2. EndCommit 3. Commit

Answer
Option 1: Correct. You use the Complete method of the TransactionScope class to inform the transaction manager to commit the transaction. Option 2: Incorrect. The EndCommit method of the CommittableTransaction class is called to explicitly check if the transaction has committed successfully. Option 3: Incorrect. You call the Commit method to commit the transaction in an explicit programming model. Correct answer(s):
1. Complete()

4. Managing concurrency
When asynchronous tasks are running together in a transaction, you can maintain data consistency by creating dependent transactions using the DependentClone method of the Transaction object. The method creates dependent clones of the parent transaction. Each dependent clone belongs to the DependentTransaction class. An instance of the DependentTransaction class ensures that the parent transaction can continue to execute without being blocked by the cloned transactions. Additionally, you can use the DependentTransaction class to ensure that a transaction containing concurrent tasks does not commit while work is still being carried out, by a worker thread for instance. Suppose you want to manage two concurrent transactional tasks of inserting and deleting the records in the EmpInfo and JobType tables of the data source. You decide to create a cloned transaction to manage the record deletions. You begin by importing the System.Transactions and the System.Threading namespaces.

Code
using using using using System.Data; System.Data.SqlClient; System.Transactions; System.Threading;

Try It
You then declare a variable of DependentTransaction type in the Main procedure.

The existing code is: class Module1 { public static void Main(string[] args) { DependentTransaction dtx;

To complete the task


1. Type DependentTransaction and click the Enter key provided Type DependentTransaction in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to create a dependent transaction is entered. You then create a new worker thread to execute the code associated with the dependent transaction. The task to be performed by the worked thread is defined in a separate method, ThreadProc.

Code
class Program { public static void Main(string[] args) { DependentTransaction dtx; Thread newThread = new Thread(WorkerThreadClass.ThreadProc);

To set the ambient transaction, you create a transaction scope.

Code
class Program { public static void Main(string[] args) { DependentTransaction dtx; Thread newThread = new Thread(WorkerThreadClass.ThreadProc); using(TransactionScope s = new TransactionScope()) {

To create a clone of the ambient transaction, you call the DependentClone method on the current transaction and pass the DependentCloneOption as its parameter.

Code
class Program { public static void Main(string[] args) { DependentTransaction dtx; Thread newThread = new Thread(WorkerThreadClass.ThreadProc); using(TransactionScope s = new TransactionScope()) { dtx = Transaction.Current.DependentClone(DependentCloneOption);

The values of the DependentCloneOption parameter help you define the behavior of the dependent transaction. In this example, you want to ensure that the dependent transaction blocks the commit process of the parent transaction until it is complete. To do this, you can use the BlockCommitUntilComplete value of the parameter. Alternatively, you can use the RollbackIfNotComplete value to abort the dependent transaction if the parent has committed the tasks before Complete()is called.

Code
class Program { public static void Main(string[] args) { DependentTransaction dtx; Thread newThread = new Thread(WorkerThreadClass.ThreadProc); using(TransactionScope s = new TransactionScope()) { dtx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilCompl ete);

Try It
You want to block the parent until the work of the dependent transaction is complete. The existing code is:

public static void Main(string[] args)

{ DependentTransaction dtx; Thread newThread = new Thread(WorkerThreadClass.ThreadProc); using (TransactionScope s = new TransactionScope()) { dtx = Transaction.Current.DependentClone(DependentCloneOption.MISSING CODE); To complete the task
1. Type BlockCommitUntilComplete and press the Enter key provide Type BlockCommitUntilComplete in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to block the parent is entered. You start the thread, which takes the object of the dependent transaction as its parameter.

Code
public static void Main(string[] args) { DependentTransaction dtx; Thread newThread = new Thread(WorkerThreadClass.ThreadProc); using (TransactionScope s = new TransactionScope()) { dtx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilCompl ete); newThread.Start(dtx); Console.WriteLine(("About to complete" + " the main thread"));

You provide the code for the parent transaction to insert employee details in the EmpInfo table.

Code
SqlConnection myConn; SqlCommand myCommand = new SqlCommand(); myConn = new SqlConnection("Data Source=" + "(local);Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); myCommand.Connection = myConn; myConn.Open(); myCommand.CommandText = ("Insert into EmpInfo" + ("(EmpID, EmpName, DeptID) VALUES" + " (\'456794\',

\'Jennifer Westlein\', \'98\')")); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("Insert into JobType" + ("(EmployeeID, JobType, DepartmentID) VALUES" + " (\'456794\', \'Stock Broker\', \'98\')")); myCommand.ExecuteNonQuery();

Finally, you call the Complete method when the tasks are ready to commit. This method is called only once.

Graphic
The code to indicate that the tasks are ready to commit is: s.Complete();

Code
s.Complete(); } Console.WriteLine("Transaction Completed"); Console.ReadLine(); } } }

You start the worker thread by calling the ThreadProc method. In the worker thread, you want to delete the employee details from the data source based on their department ID. To perform these dependent tasks, you create a dependent transaction from the object passed to the worker thread.

Code
class WorkerThreadClass { public static void ThreadProc(object o) { DependentTransaction dtx = (DependentTransaction)o;

Note
You need to create a separate dependent clone for each worker thread. You then create a new TransactionScope object and pass the DependentTransaction object, dtx, to it. This ensures that the work performed within the scope becomes part of the worker thread's transaction.

Code
class Module1 { class WorkerThreadClass { public static void ThreadProc(object o) { DependentTransaction dtx = (DependentTransaction)o; using(TransactionScope s = new TransactionScope(dtx))

You want this thread to complete after the parent transaction, so you let it sleep for five seconds.

Code
Thread.Sleep(5000); Console.WriteLine("About to complete the worker thread\'s transaction scope");

Then you provide the code that enables the worker thread to delete the records from the EmpInfo and JobType tables based on the DeptID value.

Code
SqlConnection myConn; SqlCommand myCommand = new SqlCommand(); myConn = new SqlConnection(("Data Source=" + ("(local);Initial Catalog=InvestmentWorks;" + "Integrated Security=true"))); myCommand.Connection = myConn; myConn.Open(); myCommand.CommandText = ("DELETE FROM EmpInfo" + " WHERE DeptID = \'98\'"); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("DELETE FROM JobType" + " WHERE DepartmentID = \'98\'"); myCommand.ExecuteNonQuery();

Try It
When the tasks end in the scope, you call the Complete method. The existing code is:

SqlConnection myConn;

SqlCommand myCommand = new SqlCommand(); myConn = new SqlConnection("Data Source=" + "(local);Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); myCommand.Connection = myConn; myConn.Open(); myCommand.CommandText = ("DELETE FROM EmpInfo" + " WHERE DeptID = \'98\'"); myCommand.ExecuteNonQuery(); myCommand.CommandText = ("DELETE FROM JobType" + " WHERE DepartmentID = \'98\'"); myCommand.ExecuteNonQuery(); s.MISSING CODE(); } To complete the task
1. Type Complete and click the Enter key provided Type Complete in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to inform the parent transaction is entered. When the work done by the cloned transaction is complete, it informs the parent transaction using the Complete method.

Graphic
The code to inform that the work done by the cloned transaction is complete is: dtx.Complete();

Code
SqlConnection myConn; SqlCommand myCommand = new SqlCommand(); myConn = new SqlConnection("Data Source=" + "(local);Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); myCommand.Connection = myConn; myConn.Open(); myCommand.CommandText = ("DELETE FROM EmpInfo" + " WHERE DeptID = \'98\'"); myCommand.ExecuteNonQuery();

myCommand.CommandText = ("DELETE FROM JobType" + " WHERE DepartmentID = \'98\'"); myCommand.ExecuteNonQuery(); s.Complete(); } Console.WriteLine("Completing the dependent clone"); dtx.Complete(); }

Question
You have created multiple dependent tasks. You want to ensure that the dependent transactions abort if the parent transaction commits before the Complete method is called. Which code helps you to do this? Options:
1. 2. 3. 4.
DependentTransaction DependentCloneOption BlockCommitUntilComplete RollbackIfNotComplete

Answer
Option 1: Incorrect. A dependent transaction itself is defined by the DependentTransaction class. Option 2: Incorrect. You create an instance of the dependent transaction by calling the DependentClone method and pass the DependentCloneOption enumeration as its parameter. Option 3: Incorrect. You use the DependentCloneOption BlockCommitUntilComplete value to block the parent transaction from completing until all the dependent tasks are complete. Option 4: Correct. The RollbackIfNotComplete value creates a dependent transaction that aborts when the parent transaction is committed. Correct answer(s): 4. RollbackIfNotComplete

Summary
Transactions help you secure data by grouping related operations that succeed or fail as a unit. A successful transaction adheres to the ACID properties, such as atomic, consistent, isolated, and

durable. You can use the Transact-SQL statements or database APIs to manage transactions. The .NET Framework API provides three models to configure transactions. They are ADO.NET, Enterprise Services, and System.Transactions namespace. The transactions created from the System.Transactions namespace automatically configure both local and distributed transactions. It uses the TransactionScope class to create a transaction. You can manage transactions concurrently by creating clones of the dependent transactions using the DependentClone method.

Table of Contents
| Print | Contents | Close |

Implementing ADO.NET Transactions with C# 2010


Learning Objectives
After completing this topic, you should be able to

implement an asynchronous explicit transaction implement an implicit transaction implement concurrency

Exercise Overview
In this exercise, you're required to implement an asynchronous explicit transaction, implement an implicit transaction, and manage concurrency. This involves the following tasks:

implementing an explicit transaction implementing an implicit transaction managing concurrency

You have created an online application that manages the employee details of your company. You want to insert the details of an employee, Jeremiah Hunt. To manage this, you decide to create transactions that possess the ACID properties and are confined to a single data source.

Implementing an explicit transaction

You want to use multiple thread calls in a transaction, so you decide to manage the tasks explicitly. To implement the transaction explicitly, you first import the classes from the System.Transactions namespace. You then create a connection with the data source.

Code
using System.Transactions; using System; using System.Data.SqlClient; class Program { static void Main() { MyTransaction(); Console.WriteLine("Press any key to continue..."); Console.ReadLine(); } public static void MyTransaction() { string connString = ("Data Source=" + ".\\SQLEXPRESS;AttachDbFilename=\"C:\\" + "Transactions3\\NORTHWND.MDF\";" + "Integrated Security=True;User Instance=True"); string query = "INSERT INTO Employees(LastName, FirstName) " + "VALUES(\'Hunt\', \'Jeremiah\')";

Question
To ensure that resource managers use the appropriate transactional context, you want to specifically save the ambient transaction before setting the CommittableTransaction object as the ambient transaction. Which code helps you to do this? Code
Transaction old = INSERT THE MISSING CODE; CommittableTransaction committable = new CommittableTransaction(); Transaction.Current = committable; AsyncCallback callback; callback = new AsyncCallback(TransactionCommitted);

Options:
1. Transaction.Current 2. Current 3. Commit

Answer

Option 1: Correct. To specifically set and retrieve the ambient transaction, you need to set the static Current property on the Transaction object. Option 2: Incorrect. Although the static Current property is used to set the ambient transaction, you need to set the property on the global Transaction object. Option 3: Incorrect. To commit the transaction, you explicitly call the Commit method. Correct answer(s):
1. Transaction.Current

After creating a new SqlConnection object and associating it with the commands, you execute the query. You now want to commit the transaction. You also want to ensure that an exception is thrown if there is an error in transaction. However, you decide to commit the explicit transaction asynchronously as a transactional commit may require a long time.

Code
try { using (SqlConnection connection = new SqlConnection(connString)) { SqlCommand cmd = new SqlCommand(query, connection); connection.Open(); try { cmd.ExecuteNonQuery(); } catch (SqlException ex) { Console.WriteLine(ex.Message); } }

Question
Which code enables you to asynchronously commit the transaction object, commitable? Code
INSERT THE MISSING CODE(callback, null); } catch (TransactionException e) { Console.WriteLine(e.Message); } finally {

Transaction.Current = old; }

Options:
1. committable.BeginCommit 2. committable.Commit 3. committable.Complete

Answer
Option 1: Correct. You can use the BeginCommit method of the CommittableTransaction class to begin an attempt to commit the transaction asynchronously. Option 2: Incorrect. You can call the Commit method of the CommittableTransaction class to explicitly commit the transaction. Option 3: Incorrect. You use the Complete method of a TransactionScope object to inform the transaction manager to commit the transaction. Correct answer(s):
1. committable.BeginCommit

Question
You also want to verify that the transaction has completed and ensure that if there is a transactional error, the transaction rollsback its changes asynchronously. Complete the code that ends the process of committing the committable transaction object asynchronously and raises the TransactionException in case of an error. Code
public static void TransactionCommitted(IAsyncResult asyncResult) { CommittableTransaction committable; committable = ((CommittableTransaction)(asyncResult)); try { using (committable) { INSERT THE MISSING CODE(asyncResult); } } catch (TransactionException e) { Console.WriteLine(e.Message); } } }

Answer
The EndCommit method of the CommittableTransaction class determines if the transaction has successfully committed. Correct answer(s):
1. committable.EndCommit

Implementing an implicit transaction


You want to use a simple and more efficient way to manage your transaction. Therefore, you want to set and manage the ambient transaction automatically. You have already defined the connection string and inserted the query statement.

Code
using System.Transactions; using System.EnterpriseServices; using System.Data.SqlClient; class Module1 { static void Main() { string connString = "Data Source=" + ".\\SQLEXPRESS;AttachDbFilename=\"C:\\" + "Transactions2\\NORTHWND.MDF\";" + "Integrated Security=True;User Instance=True"; string query = "INSERT INTO Employees(LastName, FirstName) " + "VALUES(\'Hunt\', \'Jeremiah\')";

Question
Which class helps you select and manage the ambient transaction automatically? Options:
1. 2. 3. 4.
Transaction TransactionScope CommittableTransaction DependentTransaction

Answer

Option 1: Incorrect. The Transaction class represents a transaction and it can be used control the transactional context but doesn't provide automatic transaction management. Option 2: Correct. You can create implicit transactions using the TransactionScope class. The transactions in this class are automatically managed in the .NET infrastructure. Option 3: Incorrect. You can manage a transaction explicitly using the CommittableTransaction class. However, you need to first explicitly set the ambient transaction in this class. Option 4: Incorrect. To manage concurrency between asynchronous tasks, you use the DependentTransaction class. Correct answer(s): 2. TransactionScope After creating an instance of the TransactionScope class, you define the connection object and associate with a command and then provide the code to execute the required task.

Code
string query = "INSERT INTO Employees(LastName, FirstName) " + "VALUES(\'Hunt\', \'Jeremiah\')"; using( TransactionScope scope = new TransactionScope()) { using(SqlConnection connection = new SqlConnection(connString)) { SqlCommand cmd = new SqlCommand(query, connection); connection.Open(); try { cmd.ExecuteNonQuery(); } catch (SqlException ex) { Console.WriteLine(ex.Message); }

Question
Which code helps you inform the transaction manager to commit the transaction? Code
using( TransactionScope scope = new TransactionScope()) { using(SqlConnection connection = new SqlConnection(connString)) { SqlCommand cmd = new SqlCommand(query, connection);

connection.Open(); try { cmd.ExecuteNonQuery(); } catch (SqlException ex) { Console.WriteLine(ex.Message); } INSERT THE MISSING CODE(); } } Console.WriteLine("Press any key to continue..."); Console.ReadLine(); } }

Options:
1. scope.Complete 2. scope.Commit 3. scope.BeginCommit

Answer
Option 1: Correct. The Complete method indicates that the tasks within the transaction have been completed successfully. Option 2: Incorrect. The Commit method is called to commit the transaction implemented using the CommittableTransaction class. Option 3: Incorrect. You can call the BeginCommit method to attempt to begin the commit process of an asynchronous explicit transaction. Correct answer(s):
1. scope.Complete

Implementing concurrency
You need to manage many tasks concurrently in your application. To ensure that data consistency is maintained, you decide to create clones of the parent transaction so that the dependent clones in the thread can work simultaneously with the parent transaction.

Code
using System.Transactions; using System.Data.SqlClient;

using System.Threading; class Module1 { static void Main() { Thread thread = new Thread(TransactionWork); Transaction current;

Question
Which class helps you manage concurrency in transactional applications? Code
using System.Transactions; using System.Data.SqlClient; using System.Threading; class Module1 { static void Main() { Thread thread = new Thread(TransactionWork); Transaction current; INSERT THE MISSING CODE dependent;

Answer
The DependentTransaction class enables you to manage concurrency between asynchronous tasks by defining a clone of the parent transaction. It ensures that the transaction cannot commit until all dependent tasks are complete. Correct answer(s):
1. DependentTransaction

To perform the actual transaction on the cloned dependent transaction in another method, you set the DependentTransaction object as a clone to the current Transaction object. Now you want to define the behavior of the dependent transaction.

Code
class Module1 { static void Main() { Thread thread = new Thread(TransactionWork); Transaction current; DependentTransaction dependent;

using (TransactionScope scope = new TransactionScope()) { current = Transaction.Current; dependent = current.DependentClone();

Question
You want to ensure that the parent transaction does not commit until all of the dependent transactions are complete. Which code helps you hold the commit process? Code
class Module1 { static void Main() { Thread thread = new Thread(TransactionWork); Transaction current; DependentTransaction dependent; using (TransactionScope scope = new TransactionScope()) { current = Transaction.Current; dependent = current.DependentClone (INSERT THE MISSING CODE.BlockCommitUntilComplete); thread.Start(dependent); scope.Complete(); } Console.WriteLine("Press any key to continue..."); Console.ReadLine(); }

Options:
1. DependentCloneOption 2. dependent 3. RollbackIfNotComplete

Answer
Option 1: Correct. The DependentCloneOption parameter of the DependentClone method enables you to block the commit process of the parent transaction. Option 2: Incorrect. You need to provide the DependentCloneOption parameter of the DependentClone method to set the behavior of the dependent transaction. Option 3: Incorrect. If you want the dependent transaction to abort when the parent transaction is ready to commit, then you use the RollbackIfNotComplete value for the DependentCloneOption parameter.

Correct answer(s):
1. DependentCloneOption

Table of Contents
| Top of page |
| Print | Contents | Close |

Working with Multiple Active Result Sets (MARS)


Learning Objectives
After completing this topic, you should be able to

recognize the steps and considerations for enabling and using MARS identify the code for using MARS to manipulate data

1. Manipulating data using MARS


Fast data transfer is crucial for applications that interact with databases. For databases based on Microsoft SQL Server 2005 and later versions, you can ensure fast exchange of data by using multiple active result sets (MARS). You can activate MARS for any application that uses Microsoft .NET Framework 2.0 or later versions. MARS allows you to send multiple batches of SQL queries to a database over a single connection. With MARS, you don't need to create a unique SqlConnection object for each SqlCommand object. You can also send multiple SqlCommand objects simultaneously over the same SqlConnection. MARS minimizes overhead and, at the same time, ensures faster processing of data. Two important features of MARS are
interleaved and atomic operations and MARS allows an application to carry out both interleaved and atomic operations over a database connection. SQL commands such as SELECT and RECEIVE run in an interleaved manner over a MARS-enabled connection, whereas data manipulation language and data definition language statements such as INSERT and UPDATE run atomically.

For example, if your application sends a RECEIVE command to a connection that is already running a SELECT command, both the commands alternatively process rows of data. Neither command needs to wait for the other to stop. However, if your application sends an INSERT statement to the same connection, MARS stalls the execution of the SELECT and RECEIVE commands and allows the INSERT statement to run to completion. Only then does MARS allow the SELECT and RECEIVE commands to resume. This selective processing of data helps increase data transfer rates. batch-scoped transactions Using MARS, you can perform batch-scoped transactions on databases. MARS ensures that when a batch initiates a transaction, the changes made by the transaction take effect only if the transaction ends before the batch is completed or canceled. This helps minimize database errors.

MARS uses an innovative caching strategy to optimally use each database connection. A MARS-enabled SqlConnection object creates a logical session when you set it up for the first time. The SqlClient object associated with the SqlConnection object stores this logical session in a cache, which can accommodate a maximum of 10 logical sessions. When you send a SqlCommand object to the SqlConnection object, the SqlCommand object acquires a logical session from the cache. If the cache is empty, SqlConnection creates a logical session for the SqlCommand object. After using the logical session, the SqlCommand object returns the session to the cache. However, this session is lost if the cache is full. This caching strategy reduces overhead and optimizes the performance of each SqlConnection. If the logical aspect of MARS-enabled connections is optimized, so is their physical aspect. You can store MARS-enabled physical connections in a dedicated connection pool. This pool of physical connections removes the overhead required to open a new physical connection. Although MARS can help minimize overhead, it might not be suitable for all types of applications. You shouldn't rely on MARS if you want your applications to
ensure thread safety or MARS isn't configured to ensure thread safety. During the execution of multiple batches, one batch can intercept data from and adversely affect the execution of another batch. process data in parallel

You can't process data in parallel over a MARS connection. For example, you can't use a single MARS connection to simultaneously perform two read operations. The second operation can start only after the first operation completes.

Question
You want to create an application that updates various types of tables and lists. What information about MARS should you keep in mind before you configure the application to use a MARS connection? Options:
1. 2. 3. 4. Allows all database operations to run in parallel Uses batch-scoped transactions to minimize database errors Prevents one batch of queries from affecting another batch Allows each SqlCommand object to use a logical session

Answer
Option 1: Incorrect. MARS does not allow parallel processing of data. It supports only interleaved and atomic database operations. Option 2: Correct. MARS supports batch-scoped transactions. Changes made by such transactions take effect only if the transactions end before the batch that initiated them is completed or canceled. This helps minimize database errors. Option 3: Incorrect. MARS doesn't ensure thread safety. One batch of queries can affect the execution of another batch. Option 4: Correct. A MARS-enabled SqlConnection object creates a logical session when you set it up for the first time. This logical session is stored in a cache. Every SqlCommand you send to the SqlConnection object acquires a logical session from the cache. If the cache is empty, SqlConnection creates a logical session for the SqlCommand object. Correct answer(s): 2. Uses batch-scoped transactions to minimize database errors 4. Allows each SqlCommand object to use a logical session Suppose you're creating an ASP.NET console application, EasyNomadHR, for the HR department at Easy Nomad Travel. The application will retrieve employee details from a SQLEXPRESS table named HrTable. The application will also update the department ID of any employee who's transferred to a new department.

Graphic

A console application named EasyNomadHR is open in a Visual Studio 2010 window. The right side of the window displays the Solution Explorer pane. The tree view in this pane has a root node named EasyNomadHR. The root node contains three child nodes named Properties, References and Program.cs. The left side of the Visual Studio 2010 window displays the Program.cs page. The Program.cs page is open and displays this code using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace EasyNomadHR { class Program { static void Main(string[] args) { } } } Because the application will perform multiple database operations simultaneously, it may run slowly. MARS can enhance the performance of the application. However, MARS is disabled by default for each newly created application. Before you can use the features of MARS, you need to enable it using the connection string for the application. So you create a connection string named ConnString.

Graphic
The code to create the connection string is: Dim ConnString as String ConnString = "Data Source = .\SQLEXPRESS; Initial Catalog = Samples;" _ + "Integrated Security = True"

Code
using System.Data; using System.Data.SqlClient; class Module1 { public class SQLProviderClass { public static void Main() { string ConnString; ConnString = ("Data Source = .\\SQLEXPRESS; Initial Catalog = Samples;" + "Integrated Security = True"); }

} }

Try It
You then set the property of the connection string that enables MARS. The existing code is: using System.Data; using System.Data.SqlClient; class Module1 { public class SQLProviderClass { public static void Main() { string ConnString; ConnString = ("Data Source = .\\SQLEXPRESS; Initial Catalog = Samples; Integrated Security = True; MISSING CODE = True"); ... To complete the task
1. Type MultipleActiveResultSets and click the Enter key provided Type MultipleActiveResultSets in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

MARS is enabled for the EasyNomadHR application. After enabling MARS, you configure the behavior of the application. You first create three String variables named selectString1, selectString2, and updateString. The selectString1 variable stores all the data retrieved from HrTable, selectString2 stores only the name and the department ID of a selected employee, and updateString stores the new department ID of the selected employee.

Code
... string ConnString; ConnString = "Data Source = .\\SQLEXPRESS; Initial Catalog = Samples; Integrated" + "Security = True; MultipleActiveResultSets = True"; string selectString1;

selectString1 = "Select * from HrTable"; string selectString2; selectString2 = ("Select EmpName, DeptID from" + " HrTable WHERE EmpName = @EmpName"); string updateString; updateString = ("UPDATE HrTable SET DeptID =" + " @NewDeptID WHERE EmpName = @EmpName"); ...

You need to ensure that the application retrieves and updates data within the scope of a transaction. So you declare a SqlTransaction object named updateTx.

Code
... string updateString; updateString = ("UPDATE HrTable SET DeptID =" + " @NewDeptID WHERE EmpName = @EmpName"); SqlTransaction updateTx;

You also create three SqlCommand objects named myCommand1, myCommand2, and myUpdateCommmand and two SqlDataReader objects named dataReader1 and dataReader2. The myCommand1 and dataReader1 objects retrieve data from HrTable, and the myCommand2 object loads dataReader2 with a subset employee name and department ID of contents from dataReader1. Finally, myUpdateCommmand updates the contents of dataReader2.

Code
... string updateString; updateString = ("UPDATE HrTable SET DeptID =" + " @NewDeptID WHERE EmpName = @EmpName"); SqlTransaction updateTx; SqlCommand myCommand1; SqlCommand myCommand2; SqlCommand myUpdateCommmand; SqlDataReader dataReader1; SqlDataReader dataReader2; ...

Try It
Before you can exchange data with HrTable, you need to establish a connection to the SQLEXPRESS database.

The existing code is: ... SqlTransaction updateTx; SqlCommand myCommand1; SqlCommand myCommand2; SqlCommand myUpdateCommmand; SqlDataReader dataReader1; SqlDataReader dataReader2; string employeeName; string employeeDept; int recordsAffected; int total; using(SqlConnection myConn = new SqlConnection(ConnString)) { MISSING CODE ... To complete the task
1. Type myConn.Open() and click the Enter key provided Type myConn.Open() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A connection to the SQLEXPRESS database is established. You configure updateTx to begin transactions with HrTable. You also configure myCommand1 and myCommand2 to retrieve data from HrTable within the scope of the updateTx transaction. Because you want myCommand2 to retrieve values related to a particular employee, you pass the variable @EmpName to myCommand2.

Graphic
The code to configure updateTx to begin transactions is: updateTx = myConn.BeginTransaction() The code that configures myCommand1 to retrieve data is: myCommand1 = new SqlCommand(selectString1, myConn); myCommand1.Transaction = updateTx; And the code to pass the variable @EmpName to myCommand2 is: myCommand2.Parameters.Add("@EmpName", SqlDbType.NVarChar);

Code
... using (SqlConnection myConn = new SqlConnection(ConnString)) { myConn.Open(); updateTx = myConn.BeginTransaction(); myCommand1 = new SqlCommand(selectString1, myConn); myCommand1.Transaction = updateTx; myCommand2 = new SqlCommand(selectString2, myConn); myCommand2.Transaction = updateTx; myCommand2.Parameters.Add("@EmpName", SqlDbType.NVarChar); } ...

Similarly, you configure myUpdateCommmand to update data within the scope of updateTx. You also pass the variables @EmpName and @NewDeptID to myUpdateCommmand. You next plan to associate the three SqlCommand objects with the two SqlDataReader objects.

Code
... using (SqlConnection myConn = new SqlConnection(ConnString)) { myConn.Open(); updateTx = myConn.BeginTransaction(); myCommand1 = New SqlCommand(selectString1, myConn); myCommand1.Transaction = updateTx; myCommand2 = new SqlCommand(selectString2, myConn); myCommand2.Transaction = updateTx; myCommand2.Parameters.Add("@EmpName", SqlDbType.NVarChar); myUpdateCommmand = New SqlCommand (updateString, myConn); myUpdateCommmand.Transaction = updateTx; myUpdateCommmand.Parameters.Add("@EmpName", SqlDbType.NVarChar); myUpdateCommmand.Parameters.Add("@NewDeptID", SqlDbType.NVarChar); } ...

Try It
You use the ExecuteReader() method to associate myCommand1 with dataReader1. The existing code is: ... SqlCommand myUpdateCommmand = new SqlCommand(updateString, myConn); myUpdateCommmand.Transaction = updateTx;

myUpdateCommmand.Parameters.Add("@EmpName", SqlDbType.NVarChar); myUpdateCommmand.Parameters.Add("@NewDeptID", SqlDbType.NVarChar); using (SqlDataReader dataReader1 = MISSING CODE) { while (dataReader1.Read()) { Console.WriteLine(dataReader1[0] + " "+ dataReader1[1] + " " + dataReader1[2]); employeeDept = dataReader1["DeptID"].ToString(); employeeName = dataReader1["EmpName"].ToString(); ... To complete the task
1. Type myCommand1.ExecuteReader() and click the Enter key provided Type myCommand1.ExecuteReader() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.
The myCommand1

object is associated with dataReader1.

Similarly, you associate myCommand2 with dataReader2.

Graphic
The code to associate myCommand2 with dataReader2 is: dataReader2 = myCommand2.ExecuteReader();

Code
... myUpdateCommmand = new SqlCommand(updateString, myConn); myUpdateCommmand.Transaction = updateTx; myUpdateCommmand.Parameters.Add("@EmpName", SqlDbType.NVarChar); myUpdateCommmand.Parameters.Add("@NewDeptID", SqlDbType.NVarChar); using (SqlDataReader dataReader1 = myCommand1.ExecuteReader()) { while (dataReader1.Read()) { Console.WriteLine(dataReader1[0] + " "+ dataReader1[1] + " " + dataReader1[2]); employeeDept = dataReader1["DeptID"].ToString(); employeeName = dataReader1["EmpName"].ToString(); myCommand2.Parameters["@EmpName"].Value = employeeName;

dataReader2 = myCommand2.ExecuteReader(); ...

Try It
You want to ensure that myUpdateCommmand functions as intended and updates the contents of dataReader2. The existing code is: ... employeeDept = dataReader1["DeptID"].ToString(); employeeName = dataReader1["EmpName"].ToString(); myCommand2.Parameters["@EmpName"].Value = employeeName; dataReader2 = myCommand2.ExecuteReader(); using(dataReader2) { while (MISSING CODE) { Console.WriteLine(dataReader2["EmpName"] + ": Old Department" + " Number: " + dataReader2["DeptID"]); myUpdateCommmand.Parameters["@EmpName"].Value = employeeName; myUpdateCommmand.Parameters["@NewDeptID"].Value = ("Downtown building, floor " + employeeDept); recordsAffected = myUpdateCommmand.ExecuteNonQuery(); total = (total + recordsAffected); } } ... To complete the task
1. Type dataReader2.Read() and click the Enter key provided Type dataReader2.Read() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The dataReader2 object is now configured to read data from myUpdateCommmand. The EasyNomadHR application can now effectively exchange data with HrTable.

Question
You want to enable MARS for an ASP.NET application. Complete the code to do this.

Code
... using System.Data; using System.Data.SqlClient; class Module1 { public class SQLProviderClass { public static void main() { string RedrockConn; RedrockConn = ("Data Source = .\\SQLEXPRESS; Initial Catalog=Samples; Integrated" + "Security=True; INSERT THE MISSING CODE = True"); ...

Answer
MARS is disabled by default for each newly created application. You use the MultipleActiveResultSets property of the connection string of the application to enable MARS. Correct answer(s):
1. MultipleActiveResultSets

Question
You are creating an ASP.NET application that contains various drop-down lists and tables. You want the application to update the contents of these lists and tables using a single SQL Server 2005 database connection. You have already enabled MARS for the connection. Complete the code to establish a connection with the database. Code
... using(SqlConnection newConn = new SqlConnection(PortageString)) { INSERT THE MISSING CODE; ...

Answer
You assign the Open() method to the SqlConnection instance to establish a connection with the database in the standard way. Correct answer(s):

1. newConn.Open()

Question
Identify the situations in which MARS must be enabled for an application. Options:
1. 2. 3. 4. Run multiple SQL commands in parallel Create a separate SqlConnection for each SqlCommand Configure a SqlDataReader to accept data from another SqlDataReader Run batches of SQL commands over a single SqlConnection

Answer
Option 1: Incorrect. MARS allows only interleaved and atomic execution of commands and statements. It doesn't allow commands to run in parallel. Option 2: Incorrect. You do not need MARS if you want to create a separate SqlConnection for each SqlCommand. Option 3: Incorrect. MARS doesn't affect the functions of the SqlDataReader instances you associate with SQL commands. You can configure a SqlDataReader to accept data from another SqlDataReader even when MARS is disabled. Option 4: Correct. MARS ensures you can execute batches of commands and statements over a single SqlConnection. With MARS, you don't need to create a unique SqlConnection object for each SqlCommand object. Correct answer(s): 4. Run batches of SQL commands over a single SqlConnection

Summary
MARS was introduced with ADO.NET 2.0 and SQL Server 2005 to enable you to run multiple batches of queries over a single SqlConnection. Using MARS, an application can interact with a database at a fast rate. But MARS also has some constraints. It doesn't ensure thread safety and it can't execute commands in parallel. To use the MARS features in an application, you need to first enable MARS in the connection string for the application. After this, you can use the connection string to establish a connection

between the application and the desired database and send multiple commands over the connection.

Table of Contents
| Top of page |
| Print | Contents | Close |

Executing Commands Asynchronously with C# 2010


Learning Objectives
After completing this topic, you should be able to

identify the uses of asynchronous design patterns in a given scenario recognize how to poll an asynchronous operation, implement a callback, and monitor asynchronous operations

1. Identifying asynchronous design patterns


Database operations that run for long periods can slow the performance of your applications. You can avoid performance degradation by using asynchronous processing. This feature was first introduced in Microsoft ADO.NET 2.0. The asynchronous-processing feature allows an application to assign a long-running database operation, such as a command to retrieve a large file, to a background thread. Simultaneously, the application can run user-interface commands and other high-priority operations over a foreground thread. Both these threads can use a single database connection.

Note
You can use asynchronous processing to exchange data with all types of databases and data sources. Suppose you're developing a console application for RedRock Mountain Tours, a company that organizes mountain-biking holidays worldwide. The application will allow employees at the company to book tours and retrieve booking details. The application will use the RedRock database to store and retrieve data. To help the employees process customer requests quickly, you want the user interface of the

application to remain responsive even when the application is processing database commands. So you decide to use asynchronous processing in the application.

Graphic
A console application named RedRock is open in a Visual Studio 2010 window.

Try It
You need to first enable asynchronous processing in the connection string of the RedRock application. You've already declared a connection string named ConnString. The existing code is: using System.Data; using System.Data.SqlClient; class Module1 { public class SQLProviderClass { public static void main() { string ConnString; connString = ("Data Source = .\\SQLEXPRESS; Initial Catalog = RedRock; Integrated" + "Security = True; MISSING CODE=True"); ... To complete the task
1. Type Asynchronous Processing and press the Enter key provided Type Asynchronous Processing in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

Asynchronous processing is enabled for the RedRock console application. Apart from enabling asynchronous processing, you also need to identify and use an appropriate method to initiate and end asynchronous processing. The SqlCommand class contains a number of methods that support asynchronous processing.
BeginExecuteNonQuery()

You use the BeginExecuteNonQuery() method to initiate the asynchronous execution of a query that doesn't return results. This method returns the IAsyncResult object that waits for or seeks results from the query.
EndExecuteNonQuery()

The EndExecuteNonQuery() method helps end the asynchronous operation initiated by a BeginExecuteNonQuery() method. If you pass the IAsyncResult object to the EndExecuteNonQuery() method, you can determine how many rows were modified by the asynchronous operation.
BeginExecuteReader()

The BeginExecuteReader() method performs the same tasks as the BeginExecuteNonQuery() method. The only difference between the methods is that the BeginExecuteReader() method initiates the asynchronous execution of a query that returns a result set.
EndExecuteReader()

Using the EndExecuteReader() method, you can end the operation started by a BeginExecuteReader() method. The EndExecuteReader() method returns a SqlDataReader object. This object stores the result set generated during the asynchronous operation.
BeginExecuteXmlReader()

The BeginExecuteXmlReader() method operates in a similar manner to the BeginExecuteNonQuery() method. However, the BeginExecuteXmlReader() method uses a query that returns an XML-based result set.
EndExecuteXmlReader()

To complete the asynchronous operation initiated by the BeginExecuteXmlReader() method, you use the EndExecuteXmlReader() method. The EndExecuteXmlReader() method returns an XmlReader object, which provides read-only, forward-only access to XML data streams.

When an asynchronous database operation ends, your application should process the results of the operation and immediately allocate the thread used by the operation to other tasks. So alerting the application as soon as the operation ends is important. You can use an asynchronous design pattern to monitor each asynchronous database operation until it ends. ADO.NET supports three asynchronous design patterns callback, poll, and wait.

Note

Employ the callback or poll design pattern only if your application processes one asynchronous database operation at a time. You use the poll design pattern if you anticipate that your application will, at some point, have to stop all other activities and wait for a result from the asynchronous database operation. In the poll design pattern, you continuously poll the IAsyncResult object until the database operation stops. Specifically, you use a while loop and the IsCompleted property of the IAsyncResult object to determine when the operation stops.

Graphic
In the poll design pattern, you use this type of code: ... IAsyncResult Result; ... while(!(Result.IsCompleted)) ... Using the callback design pattern, an application can perform other tasks while it waits for the result of the asynchronous operation. This design pattern is the best option if you want to use minimal processing to determine when the asynchronous operation ends. In the callback design pattern, you pass an instance of the AsyncCallback class to the method that initiates the asynchronous operation. As soon as the operation ends, the application calls the instance to retrieve and display the result of the asynchronous operation, using the appropriate End method. You don't have to manually type the code for the End method in this case.

Graphic
In the callback design pattern, you use this type of code: ... AsyncCallback callback = new AsyncCallback(...); ...

Question
You create a console application using Visual Studio 2010. You want the application to asynchronously interact with a database, retrieve a result set, and display it to users. You also want the application to remain responsive to users at all times. Identify the tasks that will help you successfully perform the asynchronous operation.

Options:
1. 2. 3. 4. 5. 6. Implement the poll design pattern Implement the callback design pattern Use the BeginExecuteReader method Use the BeginExecuteNonQuery method Add the IsCompleted property to the connection string Add the Asynchronous Processing attribute to the connection string

Answer
Option 1: Incorrect. You use the poll design pattern only if your application needs to perform other operations while it waits for the result of an asynchronous operation. Option 2: Correct. You use the callback design pattern if you want your application to perform other tasks while it waits for the result of an asynchronous operation. Option 3: Correct. The BeginExecuteReader method initiates the asynchronous execution of a query that returns a result set. Option 4: Incorrect. The BeginExecuteNonQuery method initiates the asynchronous execution of a query that doesn't return results. Option 5: Incorrect. You use the IsCompleted property of the IAsyncResult object with the polling design pattern. The property helps you determine when an asynchronous operation stops. Option 6: Correct. To enable asynchronous processing, you set the Asynchronous Processing attribute to true for the connection string of your application. Correct answer(s): 2. Implement the callback design pattern 3. Use the BeginExecuteReader method 6. Add the Asynchronous Processing attribute to the connection string The wait design pattern is the third and most efficient design pattern. Unlike the poll and callback design patterns, this pattern allows you to run multiple asynchronous processes simultaneously. These processes can run on multiple servers. When using the wait design pattern, you associate a wait handle with the processes that feed results to other processes. However, you allow standalone processes to run uninterrupted. You can associate two types of wait handles with the processes that feed results to other processes Wait (Any) and Wait (All). You implement the Wait (Any) handle using the WaitAny method and the Wait (All) handle using the WaitAll method.

Both the WaitAny and WaitAll methods belong to the WaitHandle class and use the AsyncWaitHandle property of the IAsyncResult object. The two methods differ in the way they pass results for processing. The WaitAny method passes results as soon as an asynchronous process stops. In contrast, the WaitAll method waits for all the asynchronous processes to stop before passing the consolidated results.

Question
You are creating an ASP.NET application. Your team member has suggested that you configure the wait design pattern for the application. Identify the characteristic features of the wait design pattern. Options:
1. 2. 3. 4. Allows standalone processes to run uninterrupted Allows you to run only one asynchronous process at a time Requires that you associate a wait handle with dependent processes Uses the IsCompleted property to determine when a command stops

Answer
Option 1: Correct. While using the wait design pattern for asynchronous processing, you can allow standalone processes to run uninterrupted. However, you need to associate a wait handle with processes that feed results to other processes. Option 2: Incorrect. The wait design pattern allows you to run multiple asynchronous processes simultaneously. Option 3: Incorrect. You need to associate a wait handle only with the processes on which other processes depend. You can associate two types of wait handles with the processes Wait (Any) and Wait (All). Option 4: Incorrect. You use the IsCompleted property when you poll for results from an asynchronous command. This property helps determine when the command stops. Correct answer(s): 1. Allows standalone processes to run uninterrupted

2. Implementing the design patterns


Identifying and implementing an appropriate asynchronous design pattern is important if you want to maximize the benefits of asynchronous processing.

Suppose you're creating a console application for the Accounts department at Portage Airlines. Among other functions, the application will retrieve the first and last names and salaries of employees from a table named Employees. After retrieving the information, the application will use it to generate other data. This requirement may cause the application to stop all other activities until it receives the information from the database.

Graphic
The code for the SqlConnection is: connection = New SqlConnection _ (ConfigurationManager.ConnectionStrings(0).ToString()) The code for the SqlCommand is: command = new SqlCommand(commandText, connection);

Code
.... SqlConnection connection = null; SqlCommand command = null; Console.WriteLine("Press <ENTER> to begin querying the Employees table."); try { // Create connection and command connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString()); string commandText = string.Format("WAITFOR DELAY \'0:0:05\';" + " SELECT LastName, FirstName, Salary " + "FROM Employees"); command = new SqlCommand(commandText, connection); connection.Open();

You decide to implement the poll design pattern for asynchronous processing. To do this, you first create a SqlConnection instance named connection and a SqlCommand instance named command. Next you create an instance, result, of the IAsyncResult object. Because you want the SqlCommand to return a result set, you configure the result instance to use the BeginExecuteReader method.

Graphic
The code that creates the result instance is: IAsyncResult result = command.BeginExecuteReader();

Code
.... try { // Create connection and command connection = new SqlConnection (ConfigurationManager.ConnectionStrings[0].ToString()); string commandText = string.Format("WAITFOR DELAY \'0:0:05\';" + " SELECT LastName, FirstName, Salary FROM Employees"); command = new SqlCommand(commandText, connection); connection.Open(); //Execute command asynchronously IAsyncResult result = command.BeginExecuteReader(); Int16 time = 0;

Try It
You configure the result instance to poll the SqlCommand and determine when it stops. The existing code is: .... //Execute command asynchronously IAsyncResult result = command.BeginExecuteReader(); Int16 time = 0; // Poll to see if execution has completed while (MISSING CODE) { Console.WriteLine(string.Format("Waiting" + " for result . . . total wait time:" + " {0} second(s)", time)); System.Threading.Thread.Sleep(1000); time++; } To complete the task
1. Type !result.IsCompleted and press the Enter key provided Type !result.IsCompleted in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The result instance is configured to poll the SqlCommand until it stops. Finally, you create an EndExecuteReader method to return an instance of the SqlDataReader object. This instance stores the result set generated during the asynchronous operation.

Graphic
The code for the reader instance is: SqlDataReader reader = command.EndExecuteReader(result);

Code
.... // Output results Console.WriteLine(); Console.WriteLine("EMPLOYEES"); SqlDataReader reader = command.EndExecuteReader(result); while (reader.Read()) { Console.WriteLine("{0}, {1} : Salary = {2}", reader[0].ToString().TrimEnd(), reader[1].ToString().TrimEnd(), reader[2].ToString().TrimEnd()); } ...

The console application can now efficiently handle asynchronous processing.

Question
You're creating a console application to update data in a database. The application needs to wait for the update process to end before it executes other operations. So you enable asynchronous processing and implement the poll design pattern for the application. Complete the code to end the asynchronous process and return the number of rows updated. Code
... Console.WriteLine("Command complete. Affected {0} rows.", command.INSERT THE MISSING CODE(result));

Answer
You use the BeginExecuteNonQuery and EndExecuteNonQuery methods to begin and end an asynchronous process that does not return a result set. After ending the process, the EndExecuteNonQuery method returns the number of updated rows. Correct answer(s):
1. EndExecuteNonQuery

Having successfully created the console application, you are given another assignment by Portage Airlines. You need to create an application that allows the HR department to update

employee details in the Employees table. So you decide to create a Window Forms Application project.

Graphic
The form in the example contains two sections Select Employee ID and Employee Details. The first section contains a drop-down list box and the second section contains three text boxes labeled Last Name, First Name, and Salary. The form also contains the Update button. Because the Employees table contains a number of records and can take a long time to update, you decide to delegate the update process to a background thread. So you first enable asynchronous processing for the form using its app.config file.

Graphic
The Asynchronous Processing=True attribute enables asynchronous processing.

Code
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> </configSections> <connectionStrings> <clear /> <add name="EmployeeSQL" connectionString="Data Source=MENTOR2631\SQLExpress; Initial Catalog=Employee;Integrated Security=True; Asynchronous Processing=True" providerName="System.Data.SqlClient" /> <add name="AsyncCommands_Callback_.My.MySettings. EmployeeConnectionString" connectionString="Data Source=MENTOR2631\SQLEXPRESS; Initial Catalog=Employee;Integrated Security=True; Pooling=False" providerName="System.Data.SqlClient" /> </connectionStrings>

You also want the form to remain responsive to users while the Employees table is updated. To do this, you want to enable the callback design pattern in the Form1.vb file. You start by configuring a SqlConnection instance and the isProcessing Boolean variable. You use the isProcessing property to signal the status of the SqlConnection instance to the asynchronous processes.

Graphic
The code for the SqlConnection object is:

SqlConnection connection; And the code to configure the isProcessing property is: private bool isProcessing = false;

Code
... using System.Data.SqlClient; using System.Configuration; public class Form1 { private void Form1_Load(System.Object sender, System.EventArgs e) { this.EmployeesTableAdapter.Fill(this.EmployeeDataSet.Empl oyees); } SqlConnection connection; private bool isProcessing = false; ...

You add a drop-down list, three text boxes, and a button to the form. You want to use the dropdown list to select employee IDs, the text boxes to enter employee details, and the button to transfer the details to the Employees table. Next you create an event handler for the button. If the user clicks the button while the form is processing another operation, the user is prompted to wait until the operation is complete. Otherwise, a SqlConnection to the Employees table is established.

Graphic
The code that checks if the form is performing an operation and displays a message is: if (isProcessing) MessageBox.Show("An operation is currently being processed. Please wait until" + " this operation has ended before beginning another one.", "Warning!");

The code to establish the SqlConnection is: connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString());

Code
SqlConnection connection; bool isProcessing = false; public delegate void UpdateLabelDelegate(string text);

private void Button1_Click(object sender, System.EventArgs e) { if (isProcessing) { MessageBox.Show(("An operation is currently being processed. Please wait until" + " this operation has ended before beginning another one.","Warning!"); } else { try { // Create connection object connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString()); ...

Try It
You also create a SqlCommand instance named command. You want to initiate the asynchronous execution of command. Additionally, you want to use the callback design pattern to monitor the progress of command. The existing code is: ... textBox1.Text.TrimEnd(), textBox2.Text.TrimEnd(), textBox3.Text.TrimEnd(), comboBox1.Text); SqlCommand command = new SqlCommand(commandText, connection); connection.Open(); lblStatus.Text = "Updating . . ."; isProcessing = true; MISSING CODE callback = MISSING CODE(HandleCallback); MISSING CODE (callback, command); } catch (Exception ex) { isProcessing = false; MessageBox.Show(("The following error while interacting with the database: " + ex.Message), "Warning!"); } ... To complete the task

1. Type New AsyncCallback and press the Enter key provided Type callback As New AsyncCallback in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. 2. Type command.BeginExecuteNonQuery and press the Enter key provided Type command.BeginExecuteNonQuery in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The asynchronous execution of command can now be initiated, and the callback design pattern can now be used to monitor the progress of command. When the asynchronous execution of command ends, you want to determine the number of rows that were updated. Next you want to close the SqlConnection. To accomplish these tasks, you add appropriate code to the form.

Graphic
The code to end the asynchronous update operation is:

int numRowsAffected = command.EndExecuteNonQuery(result); The code to close the SqlConnection is:

if (connection != null) { connection.Close(); }

Code
... private void HandleCallback(IAsyncResult result) { try { SqlCommand command = ((SqlCommand)(result.AsyncState)); int numRowsAffected = command.EndExecuteNonQuery(result); UpdateLabelDelegate del = new UpdateLabelDelegate(this.UpdateStatusLabel); this.Invoke(del, "Completed"); } catch (Exception ex) { MessageBox.Show(("The following error occurred while retrieving

the results: " + ex.Message), } finally { if (connection != null) { connection.Close(); } isProcessing = false; } } ...

"Warning!");

The application can now successfully use the callback design pattern for asynchronous processing.

Question
You're creating an application for Easy Nomad Travel that interacts with and retrieves data from the EasyNomad database. To ensure the application interface remains responsive at all times, you've enabled asynchronous processing. To monitor the processing, you are implementing the callback design pattern in the application. Identify the class that you use to implement the callback design pattern. Options:
1. 2. 3. 4.
AsyncCallback BeginExecuteReader IAsyncResult AsyncWaitHandle

Answer
Option 1: This is correct. In the callback design pattern, you pass an instance of AsyncCallback to the method that initiates the asynchronous operation. Option 2: This is incorrect. You use BeginExecuteReader to initiate the asynchronous execution of a query that returns a result set. Option 3: This is incorrect. You use IAsyncResult only in the poll design pattern to identify the termination of a back-end asynchronous process. Option 4: This is incorrect. You use AsyncWaitHandle in the wait design pattern for handling the wait states of back-end asynchronous processes. Correct answer(s):

1. AsyncCallback The console application you created for Portage Airlines has slowed because of an increase in the size of the Employees table and the inclusion of additional features in the application. So you decide to split the command to retrieve the first and last names and salaries of employees over three SqlCommands and three SqlConnections. To ensure the application quickly processes the retrieved information, you decide to implement the WaitAny method. You start the implementation process by creating arrays of SqlConnection and SqlCommand instances. Each array contains three instances. For example, the SqlConnection array contains connections(0), connections(1), and connections(2).

Graphic
The code to declare the SqlConnection array is: SqlConnection[] connections = new SqlConnection[3];

The code to declare the SqlCommand array is: SqlCommand[] commands = new SqlCommand[3]; And the code to create three instances in the SqlConnection array is:

connections[0] = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString()); connections[1] = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString()); connections[2] = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString());

Code
... class Module1 { static void Main() { SqlConnection[] connections = new SqlConnection[3]; SqlCommand[] commands = new SqlCommand[3]; Console.WriteLine("Press <ENTER> to begin querying the Employees table using WaitAny."); Console.ReadLine(); try { connections[0] = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString()); connections[1] = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString());

connections[2] = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ToString()); ...

You create instances of the WaitHandle class and the IAsyncResult object.

Code
... WaitHandle[] waitHandles = new WaitHandle[3]; IAsyncResult[] results = new IAsyncResult[3]; ...

You also create the three SqlCommand instances.

Code
string commandText = String.Format("WAITFOR DELAY '0:0:03'; SELECT LastName FROM Employees"); commands[0] = new SqlCommand(commandText, connections[0]); commandText = String.Format("WAITFOR DELAY '0:0:07'; SELECT FirstName FROM Employees"); commands[1] = new SqlCommand(commandText, connections[1]); commandText = String.Format("WAITFOR DELAY '0:0:11'; SELECT Salary FROM Employees"); commands[2] = new SqlCommand(commandText, connections[2]); ...

Try It
You want to ensure the SqlCommand instances begin executing. You also want the instances to use appropriate WaitHandle instances while they execute. The existing code is: ... for (int index = 0; (index <= 2); index++) { connections[index].Open(); results[index] =commands[index].MISSING CODE; waitHandles[index] = results[index].MISSING CODE; } ... To complete the task
1. Type BeginExecuteReader() and press the Enter key provided Type BeginExecuteReader() in the MISSING CODE field.

The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. 2. Type AsyncWaitHandle and press the Enter key provided Type AsyncWaitHandle in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The SqlCommand can now use appropriate WaitHandle instances while they execute. Next you create a for loop. When a SqlCommand instance ends, this loop ends the corresponding SqlConnection and displays the result returned by the SqlCommand instance. To enable the for loop to identify the correct SqlCommand instance, you create a variable, waitIndex, which uses the WaitAny method.

Graphic
The code for the waitIndex variable is:

waitIndex = WaitHandle.WaitAny(waitHandles, new TimeSpan(0, 1, 0), false);

Code
... int waitIndex; for (int waitCounter = 1; (waitCounter <= 3); waitCounter++) { waitIndex = WaitHandle.WaitAny(waitHandles, new TimeSpan(0, 1, 0), false); SqlDataReader reader; switch (waitIndex) { case 0: reader = commands[0].EndExecuteReader(results[0]); Console.WriteLine(); Console.WriteLine("Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while (reader.Read()) { Console.WriteLine("Value: {0}", reader[0].ToString().TrimEnd()); } ...

The waitIndex variable can have values from 0 through 2. For example, if the value of the waitIndex variable is 0, the for loop ends commands[0] and displays the value returned by results[0].

Code
case 1: reader = commands[1].EndExecuteReader(results[1]); Console.WriteLine(); Console.WriteLine("Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while(reader.Read()) { Console.WriteLine("Value: {0}", reader[0].ToString().TrimEnd()); } ...

When you run the application, a SqlCommand instance may time out instead of running to completion. To handle these time-outs, you configure the for loop to throw an exception.

Graphic
The code that throws the exception is:

Default: throw new Exception("A timeout has occurred!");

Code
... Case 2: ... Default: throw new Exception("A timeout has occurred!"); } ...

Question
You have enabled asynchronous processing for your console application. You now want to implement the WaitAny method for asynchronous processing. Ensure the method can pass results to the console.

Code
... int waitIndex; for (int waitCounter = 1; (waitCounter <= 3); waitCounter++) { waitArray = WaitHandle.WaitAny(waitHandles, new TimeSpan(0, 1, 0), false); INSERT THE MISSING CODE reader; switch (waitIndex) { case 0: reader = commands[0].EndExecuteReader(results[0]); Console.WriteLine(); Console.WriteLine("Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while (reader.Read()) { Console.WriteLine("Value: {0}", reader[0].ToString().TrimEnd()); } ...

Answer
You create an instance of a SqlDataReader object to store and display the value returned by each SqlCommand. Correct answer(s):
1. SqlDataReader

Suppose you're creating a console application for Award Sportswear, a worldwide chain of clothing stores. The application will retrieve information such as stock inventory and maintenance costs from two different databases and calculate the tax liability of the company. You want the calculation to take place only after all the details have been retrieved. You also want the application to remain responsive at all times. So you enable asynchronous processing. You also create SqlConnection and SqlCommand arrays, each containing four instances. You now want to implement the wait design pattern using the WaitAll method.

Code
using System.Data; using System.Data.SqlClient; public class SQLProviderClass { public static void main()

{ string ConnString; connString = ( "Data Source = .\\SQLEXPRESS;" + "Initial Catalog = AwardSports;" + "Integrated Security = True;" + "Asynchronous Processing = True"); SqlConnection[] connections; SqlCommand[] commands; Console.WriteLine("Press <ENTER> to begin querying" + "the Employees table using WaitAny."); Console.ReadLine(); ...

You create instances of the WaitHandle class. You also create a Boolean variable, waitResult. You configure this variable to use the WaitHandle instances and the WaitAll method. If all the SqlCommands execute successfully, the value of the variable is True. Even if one SqlCommand times out, the value is False.

Graphic
The code to create the WaitHandle instances is: WaitHandle[] waitHandles = new WaitHandle[5];

And the code for the waitResult variable is:

bool waitResult = WaitHandle.WaitAll(waitHandles, new TimeSpan(0, 1, 0), false);

Code
... WaitHandle[] waitHandles = new WaitHandle[5]; .... bool waitResult = WaitHandle.WaitAll(waitHandles, new TimeSpan(0, 1, 0), false);

Finally, you create an If loop that reads values from the SqlCommands. This loop functions only if the value of the waitResult variable is True.

Graphic

The code that initiates the If loop is: if (waitResult)

Code
if (waitResult) { SqlDataReader reader; // Query 1 reader = commands[0].EndExecuteReader(results[0]); Console.WriteLine(); Console.WriteLine( "Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while (reader.Read()) { Console.WriteLine("Value: {0}", reader[0].ToString().TrimEnd()); } //Query 2 reader = commands(1).EndExecuteReader(results(1)); Console.WriteLine(); Console.WriteLine( "Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while (reader.Read()) { Console.WriteLine( "Value: {0}", reader(0).ToString().TrimEnd()); }

You configure the If loop to throw an exception if the value of the variable is False.

Graphic
The code that causes the exception to be thrown is: else { throw new Exception("A timeout has occurred!"); }

Code
.... //Query 2 reader = commands(1).EndExecuteReader(results(1)); Console.WriteLine(); Console.WriteLine( "Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while (reader.Read()) { Console.WriteLine(

"Value: {0}", reader(0).ToString().TrimEnd()); } //Query 3 reader = commands(1).EndExecuteReader(results(2)); Console.WriteLine(); Console.WriteLine("Time: {0}", System.DateTime.Now.TimeOfDay.ToString()); while (reader.Read()) { Console.WriteLine("Value: {0}", reader(0).ToString().TrimEnd()); } else { throw new Exception("A timeout has occurred!"); }

Question
You've created an ASP.NET application that runs an array of SqlCommands, named commands, asynchronously. You want the application to use the WaitAll method to display the results. Ensure the array can process the results. Code
... commands[2] = new SqlCommand(commandText, connections[2]); for (int index = 0; (index <= 2); index++) { connections[index].Open(); results[index] = INSERT THE MISSING CODE; } ...

Answer
To initiate the array of commands, you type commands[index].BeginExecuteReader() Correct answer(s):
1. commands[index].BeginExecuteReader()

Summary
Asynchronous processing was first introduced in ADO.NET 2.0. You can use asynchronous processing to run multiple commands in parallel, delegate lengthy commands to background threads, and delegate user-interface commands to foreground threads. This selective distribution of commands prevents lengthy commands from adversely affecting the responsiveness of your application.

You enable asynchronous processing by setting the Asynchronous Processing property of the connection string for an application to True. To successfully initiate and end asynchronous processes, you use appropriate pairs of methods, such as BeginExecuteNonQuery and EndExecuteNonQuery. And to monitor each asynchronous process until it ends, you employ an asynchronous design pattern, such as poll, callback, or wait.

Table of Contents
| Print | Contents | Close |

Handling Special Data Types with C# 2010


Learning Objectives
After completing this topic, you should be able to

recognize the code for manipulating large objects identify the code for moving large amounts of data identify the benefits and uses of the SqlBulkCopy class identify the code for populating a table-valued parameter

1. Manipulating large objects


When creating ASP.NET applications, you might need to create or handle data that has a row size that exceeds 8 kilobytes or KB. This type of data is known as large object, also known as LOB, data, and it mainly consists of unstructured text documents, photographs, and video files.

Note
A LOB that consists of large chunks of binary data is known as a binary LOB or BLOB. This binary data could be a string value or a byte array. Prior to the launch of Microsoft SQL Server 2005, you had to store LOBs outside the database and use complex code to manage the LOBs. SQL SQL Server 2005 and later offer built-in functionality to handle LOBs and makes the handling of LOBs easier, through the max modifier. In SQL Server 2005 and later databases, you define the LOBs as varchar, nvarchar, or varbinary data types. You can increase the storage capacity of these data types by using the max modifier. This modifier increases storage capacity from the default 8 KB to 2^31-1 bytes. The modified versions of these data types are called varchar(max), nvarchar(max), and varbinary(max).

If you are creating a web site that allows users to upload and download videos, you can assign the varbinary(max) data type to the video files. Similarly, you can assign varchar(max) and nvarchar(max) data types to large text files. You can then store these files as LOBs in a single row of your SQL Server database. SQL Server 2008 brought further improvements to LOB storage by introducing the FILESTREAM attribute of the varbinary data type. You can use this attribute to store LOBs in a local file system. These LOBs are easily accessible, and their retrieval is quicker and more efficient. Suppose you're creating an ASP.NET application for Zoflina, a global fashion brand. The application will upload images of new garments and other data to a database. Because each image is a large chunk of binary data, you want to save the images as BLOBs.

Code
using System.IO; using System.Data.SqlClient; class Module1 { static void Main() { DateTime picDate = System.DateTime.Today.Date; string connection = ("Data Source=.\\SQLEXPRESS;" + "Initial Catalog=Zoflina;" + "Integrated Security=True"); AddPicture("notepad.bmp", picDate, "notepad.bmp", connection); }

So you create a function, AddPicture, the application. The function uses three parameters picName, picDate, and picPath. The picName parameter specifies the name of an image, picDate stores the date the image was created, and picPath specifies the location where the image is stored.

Code
public void AddPicture(string picName, DateTime picDate, string picPath, string connectionString) {

Try It
You need to configure the application to read each image from the picPath location. The existing code is: public static void AddPicture(string picName, DateTime picDate, string picPath, string

connectionString) { //read image from file MISSING CODE (picPath, FileMode.Open, FileAccess.Read); To complete the task
1. Type FileStream stream = new FileStream and press the Enter key provided Type FileStream stream = new FileStream in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The application is configured to read each image from the picPath location. You want to save each read image as a byte-array BLOB. So you first create an instance of the BinaryReader object, reader. Then you configure the reader to use the ReadBytes method to save each image as a byte array.

Graphic
The code that saves the image as a byte array is:

byte[] picture = reader.ReadBytes((int)stream.Length);

Code
FileStream stream = new FileStream( picPath, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(stream); byte[] picture = reader.ReadBytes((int)stream.Length); reader.Close(); stream.Close();

Try It
You want to add each image BLOB to the ProductPhoto table of a database named Production. The existing code is: BinaryReader reader = new BinaryReader(stream); byte[] picture = reader.ReadBytes((int)stream.Length); reader.Close(); stream.Close();

using (SqlConnection connection = new SqlConnection(connectionString)) { MISSING CODE ("INSERT INTO Production.ProductPhoto " + "(LargePhoto, LargePhotoFileName, " + "ModifiedDate) Values(@LargePhoto, " + "@LargePhotoFileName, @ModifiedDate)", connection); To complete the task
1. Type SqlCommand command = new SqlCommand and press the Enter key provided Type SqlCommand command = new SqlCommand in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A SqlCommand instance that adds each image BLOB to the ProductPhoto table is created. Using the SqlCommand instance, you set the three input parameters, @LargePhoto, @LargePhotoFileName, and @ModifiedDate with the values of the variable that were passed to the function.

Code
// add the BLOB command.Parameters.Add("@LargePhoto", SqlDbType.Image, picture.Length).Value = picture; command.Parameters.Add("@LargePhotoFileName", SqlDbType.NVarChar, 50).Value = picName; command.Parameters.Add("@ModifiedDate", SqlDbType.DateTime).Value = picDate;

Finally, you open the SqlConnection object so that it can transfer the data to the database. To execute the query associated with the SqlCommand object, you configure the object to use the ExecuteNonQuery() method.

Graphic
The code to open the SqlConnection object is connection.Open(); The code that helps execute the associated query is command.ExecuteNonQuery();

Code

'add the BLOB command.Parameters.Add("@LargePhoto", SqlDbType.Image, picture.Length).Value = picture; command.Parameters.Add("@LargePhotoFileName", SqlDbType.NVarChar, 50).Value = picName; command.Parameters.Add("@ModifiedDate", SqlDbType.DateTime).Value = picDate; connection.Open(); command.ExecuteNonQuery(); } } }

Question
You are creating a console application for Quick 24x7, a leading provider of online career and recruitment resources. The application will store video-based profiles of career seekers as BLOBs in a database. Ensure the application can store each image as a byte array. Code
BinaryReader reader = new BinaryReader(stream); byte[] video = INSERT THE MISSING CODE((int)stream.Length);

Answer
You use the ReadBytes method of the BinaryReader object to save video files as byte-array BLOBs. Correct answer(s):
1. reader.ReadBytes

Having configured the application to save images as BLOBs, you now want to ensure that it can retrieve the saved images. To configure the code to do this, you add another .cs file, Module2.cs, to the application. Next you add a connection string, conString, to the file.

Graphic
The code to create the connection string is:

string conString = "Data Source=.\\SQLEXPRESS;" + "Initial Catalog=Zoflina;" + "Integrated Security=True";

Code

using System.IO; using System.Data.SqlClient; class Module2 { static void Main() { DateTime picDate = System.DateTime.Today.Date; string conString = "Data Source=.\\SQLEXPRESS;" + "Initial Catalog=Zoflina;" + "Integrated Security=True";

Then you create a SqlConnection instance named connection and a SqlCommand instance named command. You configure the connection instance to use conString, and you configure the command instance to retrieve each image BLOB from the ProductPhoto table. Because each BLOB contains a large amount of data, you want to retrieve each field ProductPhotoID and LargePhoto of a BLOB sequentially, in streams.

Graphic
The code to create the SqlConnection instance is: SqlConnection connection = new SqlConnection(conString); And the code to create the SqlCommand instance is:

SqlCommand command = new SqlCommand(("SELECT ProductPhotoID, " + "LargePhoto FROM Production.ProductPhoto"), connection);

Code
using System.IO; using System.Data.SqlClient; class Module2 { static void Main() { DateTime picDate = System.DateTime.Today.Date; string constring = "Data Source=.\\SQLEXPRESS;" + "Initial Catalog=Zolfina;" + "Integrated Security=True; SqlConnection connection = new SqlConnection(conString); SqlCommand command = new SqlCommand(("SELECT ProductPhotoID, " + "LargePhoto FROM Production.ProductPhoto"), connection);

To retrieve the ProductPhotoID and LargePhoto fields as streams, you declare a FileStream instance, stream, and a BinaryWriter instance, writer. You plan to use the stream instance to read data from the database and the writer instance to write the data to the console. You also declare variables bufferSize, buffer, numBytes, and photoID, and you set 100 as the value of the bufferSize variable. You plan to use these variables to track bytes and values retrieved during the read operation.

Code
FileStream stream; BinaryWriter writer; int bufferSize = 100; byte[] buffer; long numBytes; int photoID;

Try It
To perform the read operation, you declare a SqlDataReader instance, reader. You want to ensure the reader instance can read the data generated by the command instance as a sequential stream. The existing code is: byte[] buffer; long numBytes, start; int photoID; connection.Open(); SqlDataReader reader = command.ExecuteReader(MISSING CODE); To complete the task
1. Type CommandBehavior.SequentialAccess and press the Enter key provided Type CommandBehavior.SequentialAccess in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The SequentialAccess method of the CommandBehavior property is used to ensure the reader instance can read data as a sequential stream. You initiate a while loop to retrieve each row in the database. You then assign the value of the first column in the row that has been read to the photoID variable.

Graphic

The code that assigns the first value read by reader to the photoID variable is:

photoID = (int)reader[0];

Code
connection.Open(); SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess); //loop for each image record while(reader.Read()) { photoID = reader[0];

Next you use the value in the photoID variable to create a FileStream object, stream, that builds a file. You also set the OpenOrCreate method of the FileMode class to either open the file if it exists or create the file.

Graphic
The code to generate the stream instance is: stream = new FileStream ("pic" + photoID + ".bmp", FileMode.OpenOrCreate);

Code
connection.Open(); SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess); //loop for each image record while(reader.Read()) { photoID = reader[0]; stream = new FileStream("pic" + photoID + ".bmp", FileMode.OpenOrCreate);

Try It
You want the data read by the reader instance to be written to the FileStream object. The existing code is: while (reader.Read()) { photoID = (int)reader[0]; stream = new FileStream ("pic" + photoID + ".bmp", FileMode.OpenOrCreate);

writer = MISSING CODE; start = 0; To complete the task


1. Type new BinaryWriter(stream) and press the Enter key provided Type new BinaryWriter(stream) in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The BinaryWriter object, writer, ensures the data read by the reader instance can be written to the FileStream object. You use the GetBytes method to read portions of the byte array from the second column and store each portion in the variable, buffer. Then you create another while loop to write the current value of the buffer variable to the BinaryWriter object. The loop continues while the value of the numBytes variable is equal to the buffer size.

Graphic
The code to assign the byte count to the numBytes variable is:

numBytes = reader.GetBytes(1, start, buffer, 0, bufferSize);

Code
// Read bytes, get num of bytes numBytes = reader.GetBytes(1, start, buffer, 0, bufferSize); // while bytes are not less than buffer size while (numBytes == bufferSize) { writer.Write(buffer); writer.Flush(); // move index, fill buffer with next piece start = (start + bufferSize); numBytes = reader.GetBytes (1, start, buffer, 0, bufferSize); }

You complete the code for the Module2.cs file by ensuring it can write the remaining values to the BinaryWriter object.

Graphic
The code to write the remaining values to the console is:

if (numBytes > 0) { writer.Write(buffer, 0, (int)(numBytes - 1)); }

Code
// Write remainder if (numBytes > 0) { writer.Write(buffer, 0, (int)(numBytes - 1)); } writer.Flush(); writer.Close(); stream.Close(); } reader.Close(); connection.Close(); } }

Question
You are configuring the Quick 24x7 console application to retrieve video-file BLOBs from the database. Ensure the SqlDataReader instance, retriever, can read the data generated by the SqlCommand instance, command, as a sequential stream. Code
connection.Open(); SqlDataReader retriever = command.ExecuteReader(INSERT THE MISSING CODE);

Answer
You use the SequentialAccess method of the CommandBehavior property to ensure that the retriever instance can read BLOB data as a sequential stream. Correct answer(s):
1. CommandBehavior.SequentialAccess

2. Using SqlBulkcopy to transfer data


While working with databases, you often transfer data between tables in bulk. You can use the SqlBulkCopy class first introduced in Microsoft .NET Framework 2.0 to perform these transfers efficiently and quickly. Unlike Microsoft SQL Server Transact-SQL statements, the SqlBulkCopy class doesn't transfer data row by row. This makes the class particularly useful for transferring large amounts of data between tables that are part of different databases. The SqlBulkCopy class is also a better alternative to Data Transformation Services, also known as DTS packages, which some database administrators use to perform bulk transfers. Unlike DTS, the SqlBulkCopy class ensures a successful data transfer even over a busy network. Also, whereas you can use only Microsoft Visual Basic to create a DTS package, you can implement the SqlBulkCopy class using either Visual Basic or Microsoft C# code. The SqlBulkCopy class facilitates the transfer of multiple rows of data at once. The class also offers other advantages:

prompt data transfer and enhanced application efficiency security and integrity of transferred data, and option to roll back changes from the destination table in the event of errors

Using the SqlBulkCopy class, you can transfer data from any data source that supports DataTable and IDataReader instances to load and read data. But you need to ensure the destination table is a SQL Server table and the source and destination tables have the same data structure. Even though SqlBulkCopy attempts to convert the source data to the data type of the target column, the conversion is not always successful. Whenever possible, make sure that corresponding source and destination columns use identical data types. To facilitate a single SqlBulkCopy operation between two tables, you need to perform the following:
1. ensure the source and destination tables have identical data structures 2. create a single connection string for data transfer if the tables belong to the same database and two connection strings if the tables belong to different databases 3. create two SqlConnection instances, one SqlDataReader instance, and one SqlBulkCopy instance

4. configure the SqlDataReader instance to use one of the SqlConnection instances to read data from the source table, and 5. configure the SqlBulkCopy instance to use the other SqlConnection instance and the WriteToServer method to transfer the read data to the destination table

If you think SqlBulkCopy operations may result in errors, you can set up each operation to roll back changes. To do so, you ensure that each operation begins and ends within the scope of a transaction.
SqlBulkCopy

transactions can be of two types:

dedicated transaction or To enable a dedicated transaction, you configure the SqlBulkCopy instance to use the UseInternalTransaction attribute. Each SqlBulkCopy operation can manage its own dedicated transaction. multistep transaction A multistep transaction consists of multiple SQL operations. To ensure a SqlBulkCopy instance can run within a multistep transaction, you pass the KeepIdentity attribute and an instance of the SqlTransaction object to the SqlBulkCopy instance.

You can configure any SqlBulkCopy instance to perform multiple bulk copy operations. For example, you can use a single SqlBulkCopy instance to update two different destination tables. This ability to perform multiple bulk copy operations using a single SqlBulkCopy instance further enhances the efficiency of your applications and saves you from writing extra code.

Question
You work for BlazerFire, a company that offers archiving systems for newspapers. These systems often need to transfer large amounts of data between databases. So you're planning to use the SqlBulkCopy class to facilitate the transfers. What actions will the SqlBulkCopy class help the systems perform? Options:
1. 2. 3. 4. Transfer data row by row Write data to all types of data sources Roll back changes from destination tables Run within and manage a dedicated transaction

Answer

Option 1: Incorrect. The Transact-SQL statements transfer data row by row. The SqlBulkCopy class facilitates the transfer of multiple rows of data at once. Option 2: Incorrect. Instances of the SqlBulkCopy class can write data only to SQL Server tables. Option 3: Correct. You can configure SqlBulkCopy operations to roll back changes from destination tables in case of errors. For this, you need to ensure each operation begins and ends within the scope of a transaction. Option 4: Correct. To run a SqlBulkCopy operation within a dedicated transaction, you configure the SqlBulkCopy instance to use the UseInternalTransaction attribute. Correct answer(s): 3. Roll back changes from destination tables 4. Run within and manage a dedicated transaction Suppose you work as a database administrator at Award Sportswear, a sportswear manufacturing firm. You store details about sportswear in a table named Production.Product. The table has become too large to manage efficiently. So you decide to remove data related to outdated sportswear from the Production.Product table and archive the data in another table. You plan to use the SqlBulkCopy class to quickly and efficiently transfer the data. To implement the SqlBulkCopy class, you create a console application named AwardArchival and open the Program.cs file for editing. In the Module1.cs file, you first create a connection string for the AwardSportswear database, which hosts the Production.Product table.

Graphic
The code to create the connection string is:

string con = "Data Source=.\\SQLEXPRESS;" + "Initial Catalog=AwardSportswear;" + "Integrated Security=True";

Code
using System.Data.SqlClient; class Module1 {

static void Main() { string con = "Data Source=.\\SQLEXPRESS;" + "Initial Catalog=AwardSportswear;" + "Integrated Security=True";

...

To connect to the AwardSportswear database, you create a SqlConnection instance, source, that uses the connection string. You also open the SqlConnection.

Graphic
The code to create the SqlConnection instance is: SqlConnection source = new SqlConnection(con); And the code to open the SqlConnection is: source.Open();

Code
using System.Data.SqlClient; class Module1 { static void Main() { string con = "Data Source=.\\SQLEXPRESS;" + "Initial Catalog=AwardSportswear;" + "Integrated Security=True"; SqlConnection source = new SqlConnection(con); source.Open(); ...

Now you need to create an empty table in which you can archive outdated data. So you create a function named CreateTable. The function first checks whether an empty table, Product2, exists. If it doesn't exist, the function creates a blank table named Product2. This table has the same format and constraints as the Production.Product table.

Graphic

The code that initiates the CreateTable function is CreateTable(source);

Code
... ... CreateTable(source); public static void CreateTable(SqlConnection source) { SqlCommand create = new SqlCommand( "USE AwardSportswear " + "IF NOT EXISTS (SELECT * FROM sys.objects " + "WHERE object_id = " + "OBJECT_ID(N\'[dbo].[Product2]\')) " + "CREATE TABLE [dbo].[Product2](" + "[ProductID] [int] IDENTITY(1,1) NOT NULL," + "[Name] [nvarchar](50) NOT NULL, " + "[ProductNumber] [nvarchar](25) NOT NULL, " + "CONSTRAINT [PK_Product2ID] " + "PRIMARY KEY CLUSTERED " + "([ProductID] ASC) ON [PRIMARY] " + ") ON [PRIMARY] ", source); ...

Try It
You want to ensure the application can read data from the source table. The existing code is: ... ... CreateTable(source); SqlCommand sourceData = new SqlCommand( "SELECT ProductID, Name, ProductNumber " + "FROM Production.Product;", source); SqlDataReader reader = MISSING CODE; public void CreateTable(SqlConnection source) ... To complete the task

1. Type sourceData.ExecuteReader() and press the Enter key provided Type sourceData.ExecuteReader() in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A SqlDataReader instance named reader is created. To write data to the destination table, you create a SqlConnection instance, dest, and open the SqlConnection.

Graphic
The code to create dest is:

using(SqlConnection dest = new SqlConnection(con))

Code
... CreateTable(source); SqlCommand sourceData = new SqlCommand( "SELECT ProductID, Name, ProductNumber " + "FROM Production.Product;", source); SqlDataReader reader = sourceData.ExecuteReader(); using(SqlConnection dest = new SqlConnection(con)) { dest.Open(); public void CreateTable(SqlConnection source) ...

Try It
You now need to transfer data to the destination table. The existing code is: ... ... SqlBulkCopy bulkCopy = new SqlBulkCopy(dest); bulkCopy.DestinationTableName = "dbo.Product2"; try { bulkCopy.MISSING CODE;

} catch (Exception ex) { Console.WriteLine(ex.Message); } finally { reader.Close(); } ... To complete the task
1. Type WriteToServer(reader) and press the Enter key provided Type WriteToServer(reader) in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The SqlBulkCopy instance, bulkCopy, is configured to use the WriteToServer method to transfer the data to the destination table.

Question
You are creating an ASP.NET application. The application will use a SqlBulkCopy instance, bulkCopy, to transfer data from a Microsoft Office Excel database to a SQL Server table named Table1. Ensure bulkCopy stores the data in the correct location. Code
... SqlBulkCopy copyBulk = new SqlBulkCopy(dest); copyBulk.INSERT THE MISSING CODE = "datatables.Table1"; try { copyBulk.WriteToServer(reader); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { reader.Close(); } ...

Answer

You use the DestinationTableName property of the SqlBulkCopy class to specify the name of the destination table to the SqlBulkCopy instance. Correct answer(s):
1. DestinationTableName

3. Using table-valued parameters


Although the SqlBulkCopy class provides an efficient way of transferring data in bulk, it doesn't allow data to be transferred directly to a stored procedure. To enable such a transfer using SqlBulkCopy, you first need to load the data in temporary tables or in table variables. Microsoft SQL Server 2008 eliminates this constraint by offering table-valued parameters (TVPs). Using a TVP, you can transfer multiple rows of data to a SQL Server database using a single stored procedure or a Transact-SQL statement. When you use this parameter, you don't need to create a temporary table, establish multiple transactions between the source and the destination, or use server-side logic. TVPs enhance the performance of your applications and offer a simple programming model. They also

populate data from a client without acquiring locks support strongly typed and set-oriented queries allow the client to specify sort order and unique keys, and reduce the number of steps required to transfer bulk data

However, TVPs also have certain limitations. They don't support UPDATE, DELETE, or INSERT operations within the body of a Transact-SQL routine. Additionally, you can use them only as input parameters. Another drawback of TVPs is they don't allow SQL Server to maintain statistics on columns. Also, you can't use TVPs as targets of a SELECT INTO or INSERT EXEC statement. You can use them only in the FROM clause of such statements. To transfer tabular data using the TVP feature, you first need to declare a user-defined table type. This declaration, which you make using the CREATE TYPE statement, prepares a user-defined type or UDT table structure. A UDT

Code
CREATE TYPE SalesTable AS TABLE ( SaleID int, ProductName nvarchar(50) )

can be created using any coding language recognized by the .NET Framework common language runtime, also known as CLR helps define objects, data structures, table columns, or arguments for stored procedures contains multiple elements, and can be accessed as an object, as raw bytes through a SqlDataReader object, or as a parameter through a SqlParameter object

After declaring the table type, you create a stored procedure to receive data for the table using the CREATE PROCEDURE declaration.

Graphic
The code to create the stored procedure is CREATE PROCEDURE usp_UpdateSalesTable

Code
CREATE PROCEDURE usp_UpdateSalesTable (@tvpSales SalesTable READONLY) AS INSERT INTO Sales (SaleID, ProductName) SELECT ns.SaleID, ns.ProductName FROM @tvpSales AS ns; ...

The stored procedure has one parameter, which accepts a SalesTable data type. To prevent a table pointer from modifying the data in a table based on this data type, you declare the data type as READONLY. You can now create an instance of the table and pass the instance to the stored procedure. Using the stored procedure, you can also pass multiple rows of data into a target table in the database.

Graphic
The code to set the parameter for the stored procedure is (@tvpSales SalesTable READONLY)

Code
CREATE PROCEDURE usp_UpdateSalesTable (@tvpSales SalesTable READONLY)

AS INSERT INTO Sales (SaleID, ProductName) SELECT ns.SaleID, ns.ProductName FROM @tvpSales AS ns; ...

Question
Identify the benefits and uses of TVPs. Options:
1. 2. 3. 4. Support strongly typed queries Function as output parameters Allow the client to specify unique keys Allow SQL Server to maintain statistics in columns

Answer
Option 1: Correct. TVPs support strongly typed and set-oriented queries. Using a TVP, you can transfer multiple rows of data to a SQL Server database using a single stored procedure or a Transact-SQL statement. Option 2: Incorrect. TVPs can only be used as input parameters. That's why you use the READONLY keyword when you declare a TVP within a stored procedure. Option 3: Correct. TVPs allow the client to specify both sort order and unique keys. They also help reduce the number of steps required to transfer bulk data. Option 4: Incorrect. TVPs don't allow SQL Server to maintain statistics on columns, and you can't use TVPs as targets of a SELECT INTO or INSERT EXEC statement. Correct answer(s): 1. Support strongly typed queries 3. Allow the client to specify unique keys You work as a developer at Mathemetric Ltd., a company that sells precision instrumentation. You have recently installed SQL Server 2008 and have created a database named MathemetricLtd on the new server. You now plan to store product-related data in a table named ProductData. To transfer the data to the ProductData table without multiple round trips to the server, you decide to use a stored procedure that uses a TVP. To configure the stored procedure, you create a console application named Mathemetric.

In the Module1.cs file of the application, you import the System.Data.SqlClient provider. This provider allows you to populate the database using DataTable, DbDataReader, or IEnumerable objects. You also establish a connection to the MathemetricLtd database.

Code
using System.Data.SqlClient; class Module1 { static void Main() { string con = ( "Data Source=.\\SQL2008;" + "Initial Catalog=MathemetricLtd;" + "Integrated Security=True"); ...

Try It
Next you create a structure for the table using the user-define table type. The existing code is: ... MISSING CODE TABLE dbo.ProduceTableType (ProduceID int, ProductName nvarchar(50)) ... To complete the task
1. Type CREATE TYPE and press the Enter key provided Type CREATE TYPE in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The structure of the table is created. You now declare a stored procedure that can pass data to the user-defined table.

Code
... string createProc = "CREATE PROCEDURE InsertTVP

(ProductTableType @tvp READONLY)" + "AS SET NOCOUNT ON; " + "INSERT INTO ProductData" + "SELECT ProductID, ProductName FROM @tvp;"; ...

You also create a data connection for retrieving data and specifying the SQL code to run the stored procedure.

Code
SqlConnection connection = new SqlConnection(con); connection.Open(); SqlCommand proc = new SqlCommand(createProc, connection); proc.ExecuteNonQuery(); ...

Another useful step in the process is to create a second data connection for inserting data.

Code
... SqlConnection connect2 = new SqlConnection(con); connect2.Open(); ...

You also create a SqlCommand that calls the InsertTVP procedure to add data to the TVP table.

Code
SqlCommand run = new SqlCommand("InsertTVP", connect2); run.CommandType = CommandType.StoredProcedure; ...

Try It
To complete the steps for configuring and populating the table, you create a SqlDataReader instance, reader. You configure reader to read data which is then passed as a TVP to the InsertTVP stored procedure. The existing code is: ... SqlDataReader reader = query.ExecuteReader(CommandBehavior.CloseConnection); MISSING CODE param = run.Parameters.AddWithValue("@tvp", reader);

param.SqlDbType = SqlDbType.Structured; run.ExecuteNonQuery(); ... To complete the task


1. Type SqlParameter and press the Enter key provided Type SqlParameter in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

A SqlParameter instance, param, is created. The param instance assigns a value to the @tvp parameter in the InsertTvp stored procedure.

Question
You're creating a console application that will copy a large amount of data from one table to another using a stored procedure and a TVP. Ensure the SqlDataReader instance, reader, can pass data as a TVP to the stored procedure. Code
... CREATE PROCEDURE usp_UpdateEmployeeData (@tvpEmp EmpTable READONLY) ... SqlCommand modify = new SqlCommand("InsertTVP", connect2); modify.CommandType = CommandType.StoredProcedure; SqlParameter tvparam = modify.INSERT THE MISSING CODE("@tvpEmp", reader); tvparam.SqlDbType = SqlDbType.Structured; modify.ExecuteNonQuery(); ...

Answer
You use the AddWithValue method of the Parameters property to enable the SqlDataReader instance to pass data as a TVP to the stored procedure. Correct answer(s):
1. Parameters.AddWithValue

Summary
The max modifier in SQL Server 2005 and SQL Server 2008 makes it easy for you to manipulate LOBs and transfer large amounts of data between tables.

Similarly, the SqlBulkCopy class makes it possible for you to efficiently transfer tabular data in bulk. If you use SQL Server 2008, you can use TVPs, which make bulk transfer of data even more efficient. With TVPs, you can transfer bulk data to a database using a single stored procedure or Transact-SQL statement. The TVPs help avoid multiple round trips to the server and don't require you to use server-side logic.

Table of Contents

Working with MARS and Asynchronous Commands


Learning Objective
After completing this topic, you should be able to

work with MARS, asynchronous commands, and special data types

1. Optimizing database transactions

ADO.NET DataSets
Abstract
This article discusses DataSets and how they are used by ADO.NET for disconnected data access. It also describes scenarios where using the disconnected data access model is suitable.

Disconnected data access in ADO.NET


ADO.NET supports both connected and disconnected data access. In disconnected data access, an application connects to the database only when it is retrieving or storing data. In contrast, the connected data access maintains the connection with the database as long as the application is open. Disconnected data access offers advantages such as reduced network traffic, improved database security, application scalability, and system performance. These make it a preferred mode of data access. To implement disconnected data access, ADO.NET uses DataSets. A DataSet is an inmemory copy of data from the data source that does not require the connection to be maintained with the data source.

DataSet and its components


A DataSet is a storage element that stores data retrieved from a data source. Internally DataSets use DataAdapters to connect to the data source. DataSets store data in the same hierarchy of data as in the relational database using these objects or components

Contain DataRow and DataColumn objects to represent the rows and columns of a table in a database. DataConstraints and DataRelations Various constraints are applied to the data in the columns of a table and these tables could be related to one another. The constraints and relations in a database are represented using the DataConstraints and DataRelations objects in a DataSet.
DataTables

DataSet and related classes are specified in the System.Data namespace. These components of DataSets need to be defined explicitly and exposed only as collections. Every DataSet contains

two collection objects: 1. The DataTableCollection object contains one or more DataTable objects. The component objects of a DataTable object are also grouped using the collection objects such as DataRowCollection, DataColumnCollection, and ConstraintCollection. 2. The DataRelationCollection object contains one or more DataRelation objects, which represent the relation between tables. The DataRelation objects store relation information such as the relationship name, the tables being related, the related columns, and unique and foreign key constraints.

DataSet types
DataSets

are of two types and you can use either of them in your applications.

Untyped DataSet An untyped DataSet is not derived from the DataSet class directly but is an instance of a DataSet class created in a form or component. Additionally, untyped DataSets do not have an inherent structure because their base classes are not generated using XML schema files. Because of this, you need to create the DataTable, DataColumn, DataConstraint, and DataRelation objects at design time and make them available as collections. Typically, untyped DataSets are used when you need to get data from an application but don't know the schema of the returned DataSet. You also use untyped DataSets to work with data that doesn't have a constant or a predefined structure.

Disconnected data access scenarios


Using DataSets for disconnected access is beneficial when you want to:

Manipulate data locally Suppose you are creating a mobile application that allows users to manipulate data multiple times. If manipulation is done directly on the database, the overhead of validating new data, updating the data source, and opening the database connection and keeping it open are high. Instead you can use DataSets because they are local copies of data and do not need an active connection with the data source. Additionally, DataSets enable you to reuse the record set multiple times. After all the changes are done, the data source can be updated with the changes made to the DataSets. Exchange data between applications You are creating a web service that accepts data from the calling application and sends requested data back. To create such applications that exchange data, you use DataSets because data is generally transported in the form of XML on the wire and DataSets support serialization of data to XML. Additionally, ADO.NET allows DataSets to be transferred across applications and firewalls along with metadata. Move data in a distributed application You are creating an n-tier application with various tiers exchanging data among them. Because ADO.NET allows DataSets to be transferred between the tiers of a distributed application, changes made to the tiers can be communicated to the other layers without requiring extensive code. Interact dynamically with data In your application, you want to bind data to a form using DataGrid a data binding control. When you need to bind data, you might need to access large amounts of data and then process it before displaying it using controls. Therefore, you can use DataSets to store data in the memory temporarily before processing it. This saves a lot of programming effort. Integrate data from different sources When your application requires data from different data sources, DataSets are the only available option. DataSets are the only data storage elements that enable you to integrate and work with data from sources that contain data in different formats such as XML, relational, spreadsheet, and text file.

Summary
ADO.NET uses DataSets to implement disconnected data access. DataSets are offline copies of data retrieved from a database in a structure similar to the data in the database. DataSets use collections of objects such as DataTables, DataRows, DataColumns, DataConstraints, and DataRelations to represent data with the appropriate structure. DataSets are of two types typed and untyped. DataSets are specifically used in situations where you need to manipulate data locally, exchange data between applications, or move data in a distributed application. They are also useful when you need to interact dynamically with data or process data extensively.

Table of Contents

Creating Untyped DataSets with Visual C# 2010

Learning Objectives
After completing this topic, you should be able to

recognize the code for creating a DataSet, adding a DataTable to it, and setting column properties enforce and remove unique constraints for a column or an array of columns

1. Creating untyped DataSets


An untyped DataSet is an instance of the DataSet class and is not type safe. An untyped DataSet has no built-in schema defining the structure of the DataSet. It exposes its tables and columns as collections. In an untyped DataSet, you need to explicitly convert the columns to the required data type. Because untyped DataSets are not type safe and lack a schema, they are considered difficult to use. However, they are also flexible because they enable working with data whose structure is not fixed or known at design time.

Note
Because untyped DataSet are not type safe, if you fill a column in the DataSet with data of a particular type while it expects data of a different data type, the compiler does not throw errors. However at runtime, data type mismatch errors are thrown, which can impact the application. You can create an untyped DataSet in four steps: 1. create a DataSet instance 2. add a DataTable object to the DataSet instance 3. add columns to the table 4. set column properties You create an untyped DataSet by creating an instance of the DataSet class using the constructor given in the syntax, where the name argument specifies the name of the Dataset.

Syntax
DataSet Instance_Name = new DataSet("Name"); You can also exclude the name argument in the constructor. This will create a DataSet with the default name NewDataSet.

Syntax
DataSet Instance_Name = new DataSet( );

Try It
Suppose you want to create a DataSet named "Products" to store product details. So you create an instance of the DataSet class using the constructor. The existing code is: public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products= new DataSet("MISSING CODE"); To complete the task 1. Type Products and click the Enter key provided Type Products in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to create a new DataSet object is added. After creating an instance of the DataSet class, you add a table to it using the DataTable object. This ensures that the structure you create in the DataSet is similar to the source relational database.

Note
In addition to tables, a DataSet can contain relationships and constraints. To add a table called NewProducts to the products DataSet you created, you create an instance of the DataTable using this code.

Graphic
The code that adds a new table to the dataset is: DataTable newProducts = products.Tables.Add ("NewProducts");

Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts ("NewProducts"); DataTable = products.Tables.Add

object names are conditionally case-sensitive so two tables with the same name can exist in the DataSet as long as their casing is different. When calling these tables, you must keep in mind that the names are case-sensitive. For example, if the Products DataSet contains two tables NewProducts with an uppercase N and an uppercase P, and newproducts all in lowercase, you need to match the case when you call them. However, if only one table exists with the name, you can use any casing to call the table and no error is thrown. For example, if the DataSet contains a table called NewProducts uppercase N uppercase P, you can refer to it as either NewProducts with an uppercase N and uppercase P or newProducts with only an uppercase P. Every table contains columns and constraints represented by the DataColumn, ForeignKeyConstraint, and UniqueKeyConstraint objects that help define the schema of a table. Because a DataTable contains a collection of DataColumn objects, you need to create a DataColumn object as the third step in creating an untyped DataSet. In this example, you want to create a column called productid.

Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add("NewProducts"); DataColumn productID;

Note
You can also use the constructor of the DataColumn object to add columns to a table.

Try It

To create a new column, you use the Add method of the Columns property of the table. This method accepts optional arguments, such as the column name and the data type. You want the ProductID column of the System.Int32 type added to the NewProducts table. The existing code is: public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add ("NewProducts"); DataColumn productID = MISSING CODE ("ProductID", Type.GetType("System.Int32")); To complete the task 1. Type newProducts.Columns.Add and click the Enter key provided Type newProducts.Columns.Add in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code to add a DataColumn object is added. Once a column is added, you can specify its properties. You need to set the primary key, which enables you to uniquely identify the rows. To specify a particular column or group of columns as the primary key, you use the PrimaryKey property of the DataColumn object.

Try It
You decide to specify the productID column as the primary key for the newProducts table. So you set the DataColumn object as the value for the PrimaryKey property. The existing code is: public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add("NewProducts");

DataColumn productID = newProducts.Columns.Add ("ProductID", Type.GetType("System.Int32")); MISSING CODE = new DataColumn[] { newProducts.Columns["ProductID"] }; To complete the task 1. Type newProducts.PrimaryKey and click the Enter key provided Type newProducts.PrimaryKey in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The code specifies the productID column as the primary key. When data is entered in a column, you need to ensure that it is correct and consistent. The properties of the DataColumn object you can use to control data entered in columns are
AllowDBNull

You use the AllowDBNull property to set the column to accept or reject null values. If you set the AllowDBNull property to False, the column does not accept a null value. The default value of the property is True. For example, if you have a ProductID column in your NewProducts table, you cannot accept a null value as a ProductID value. So you set the AllowDBNull property of this column to False.
ReadOnly

The ReadOnly property is used to specify if a column can allow data to be edited when a row of data is added to the table. If the value of this property is set to true, you cannot edit the data in this column. The default value of the property is False. In a ProductID column, if you want to ensure that product identification values are not edited, you can set the ReadOnly property on the column to True.
DataType

If you want to specify the type of data to be stored in a column, you use the DataType property. This property can accept any Type object Boolean, Char, Double, String, and Int32, for example. If you want the ProductID column to be a String, you set the value of its DataType property to String. If a column is specified as the primary key for a table, the AllowDBNull property of the column is automatically set to False and the Unique property set to True.

When multiple columns are specified as the primary key, only the AllowDBNull property of the column is automatically set to False. The Unique property is not set to True. To ensure column values are unique, you can create a column that is autoincrementing by setting the DataColumn object properties. These properties are

Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add("NewProducts"); DataColumn productID = newProducts.Columns.Add ("productID", Type.GetType("System.Int32")); newProducts.PrimaryKey = new DataColumn[] { newProducts.Columns["productID"] }; productID.AutoIncrement = true; productID.AutoIncrementSeed = 5; productID.AutoIncrementStep = 2; AutoIncrement

Setting the AutoIncrement property of a column to true specifies that the value of the data stored in the column in each row is incremented by a specific value. In the example provided, to set the ProductID column as an autoincrementing column, you set its AutoIncrement property to True.
AutoIncrementSeed The AutoIncrementSeed

property is used to specify the starting value of the first row of the autoincrementing column in the table. For example, to set the start value of the autoincrementing column, ProductID, as 5, you specify the AutoIncrementSeed property as given in the code. This ensures that when the first row of the table is filled with data, the autoincrementing column is automatically filled with the value 5.

AutoIncrementStep Using the AutoIncrementStep

property, you can set the value by which the data in the autoincrementing column is incremented for each row. In the example provided, to set the increment value as 2, you set the AutoIncrementStep property as given in the code. For example, assume the AutoIncrementSeed is set to 5 and the AutoIncrementStep is set to 2 for a column in the table. When data is filled in the table, the value automatically filled in the first row is 5, the value filled in the next row is 7, and so on, for the autoincrementing column.

In addition to creating an untyped DataSet programmatically, you can also create an untyped DataSet using the Dataset Designer in Visual Studio 2010.

Supplement
Selecting the link title opens the resource in a new browser window. Learning aid Use the learning aid Using Component Designer to learn how to create an untyped DataSet using Component Designer.

Question Set
Learning how to create untyped DataSet objects takes time. Answer these practice questions to review how you're progressing.

Question 1 of 4
Question You are developing an application that retrieves customer data from many databases. However, you are unsure about the structure of the data being received. Enter the code to create an instance, called customers, of an untyped DataSet with the name Customers. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { INSERT THE MISSING CODE;

Answer You create an instance, customers, of the untyped DataSet Customers class by entering this line of code DataSet customers = new DataSet("Customers"). Correct answer(s): 1. DataSet customers = new DataSet("Customers")

Question 2 of 4
Question

You want to add a table called NewCustomers to the untyped DataSet customers. Complete the code to create a table that helps store details of new customers. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet customers = new DataSet("Customers"); DataTable newCustomers = INSERT THE MISSING CODE("NewCustomers");

Answer You use the Add method of the DataSet's Table property to add a table to its DataTableCollection. Correct answer(s): 1. customers.Tables.Add

Question 3 of 4
Question In the NewCustomers table, you want to add a column that stores the identification number of the customers and is called custID. Complete the code to add this. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet customers = new DataSet("Customers"); DataTable newCustomers = customers.Tables.Add("NewCustomers"); DataColumn custID = INSERT THE MISSING CODE ("CustID", Type.GetType("System.Int32"));

Answer You use the Add method of the DataSet's Column property to add a column to the DataSet's DataColumnCollection. Correct answer(s): 1. newCustomers.Columns.Add

Question 4 of 4
Question In the application, you added code specifying that the CustID column is autoincrementing and that its starting value is 5. Complete the code to set 1 as the value to specify how column data should be incremented for every row. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet customers = new DataSet("Customers"); DataTable newCustomers = customers.Tables.Add("NewCustomers"); DataColumn custID = newCustomers.Columns.Add ("CustID", Type.GetType("System.Int32")); newCustomers.PrimaryKey = new DataColumn[] { newCustomers.Columns["custID"] }; custID.AutoIncrement = true; custID.AutoIncrementSeed = 5; custID.INSERT THE MISSING CODE = 1;

Answer You use the AutoIncrementStep property of the DataColumn object to specify the increment value. Correct answer(s): 1. AutoIncrementStep

2. Enforcing unique constraints


In the DataTable object, you might want a particular column or an array of columns to contain unique values in each row. For example, in a table containing employee details, you want the column called EmployeeID to include unique numbers in every row because each employee has a different identification number. You can enforce unique constraints to ensure that no two rows within the column contain the same data.

Note

Setting a column as an autoincrementing column also ensures that the value in each row is unique. You can enforce a unique constraint on a column or array of columns using

Syntax
public partial class Form1 : Form { ... UniqueConstraint UniqueConstraintObj = new UniqueConstraint(new DataColumn[] {table. Columns["Column1Name"], table.Columns["Column2Name"]}); dataset.Tables["TableName"].Constraints.Add(UniqueConstraintObj); Column3Name.Unique=true; the UniqueConstraint object You can use the UniqueConstraint object to enforce unique constraint on a column or array of columns. To do this, you first need to create a DataColumn array containing the columns on which you want to enforce unique constraint. You then create a UniqueConstraint object using the DataColumn array object. Then you pass the object to the Add method of the Constraints property of the table. If a column is specified as the primary key for the table, the unique constraint is automatically enforced on the column. You use this syntax to create a unique constraint. the Unique property To enforce a unique constraint on a single column, you set the Unique property of the column to true as given in the syntax. If the column is set as the primary key, the Unique property of the column is automatically set to True. If you want to remove any existing unique constraints in the table, you can set the Unique property of a column to false. Suppose you want to enforce unique constraints on the columns EmployeeID and EmployeeName in the employee DataSet, you use this code to do so.

Code
public partial class Form1 : Form { ... DataColumn employeeName = newEmployees.Columns.Add("EmployeeName");

employeeName.ReadOnly = true; UniqueConstraint empUnique = new UniqueConstraint(new DataColumn[] {newEmployees. Columns["EmployeeID"], newEmployees.Columns["EmployeeName"]}); employees.Tables["NewEmployee"].Constraints.Add(empUnique); (new DataColumn[] {newEmployees. Columns["EmployeeID"], newEmployees.Columns["EmployeeName"]}); This creates a DataColumn array containing the EmployeeID and EmployeeName

columns the two columns for which you want the unique constraint enforced.
UniqueConstraint empUnique = new UniqueConstraint You create a UniqueConstraint object, empUnique, using this constructor and the DataColumn array. employees.Tables["NewEmployee"].Constraints.Add(empUnique); The Add method adds the empUnique object to the Constraints property of the NewEmployees table in the DataSet object. The Constraints property represents a ConstraintCollection.

To enforce a unique constraint on just the employeeID column, you use the Unique property of the column. You do this by setting the value of the property to true as given in the code.

Graphic
The line of code that enforces the unique constraint on the EmployeeID column using the Unique property is: employeeID.Unique = true;

Code
public partial class Form1 : Form { ... DataColumn employeeName = newEmployees.Columns.Add("EmployeeName"); employeeName.ReadOnly = true; employeeID.Unique = true;

Try It
You want to use the UniqueConstraint object to enforce a unique constraint in the NewProducts table for a column array containing the columns ProductID and ProductName.

The existing code is: public partial class Form1 : Form { ... DataColumn description = newProducts.Columns.Add ("Description", Type.GetType("System.String")); description.AllowDBNull = true; UniqueConstraint productUnique = MISSING CODE(new DataColumn[] { newProducts. Columns["ProductID"], newProducts. Columns["ProductName"]}); products.Tables["NewProducts"]. Constraints.Add(productUnique); To complete the task 1. Type New UniqueConstraint and click the Enter key provided Type New UniqueConstraint in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The unique constraint is enforced on the column array. You can also use the Unique property of a DataColumn object to remove unique constraints. In the NewProducts table, you have an autoincrementing column ProductID and have enforced unique constraints on this column and a ProductName column.

Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add ("NewProducts"); DataColumn productID = newProducts.Columns.Add ("ProductID", Type.GetType("System.Int32")); newProducts.PrimaryKey = new DataColumn[] { newProducts.Columns["productID"] }; productID.AutoIncrement = true;

productID.AutoIncrementSeed = 1; productID.AutoIncrementStep = 1; DataColumn productName = newProducts.Columns.Add("ProductName"); ... UniqueConstraint productUnique = new UniqueConstraint(new DataColumn[] {newProducts. Columns["ProductID"], newProducts. Columns["ProductName"]}); products.Tables["NewProducts"]. Constraints.Add(productUnique);

However, you feel it will be difficult to keep the product names unique when there are too many products. Therefore, you want to check the AutoIncrementStep value of the ProductID column. And when its value crosses 100, you want to remove the unique constraint on the ProductName column as given in the code.

Graphic
The code that checks the AutoIncrementStep value is: if(productID.AutoIncrementStep > 100) { productName.Unique = false; }

Code
public partial class Form1 : Form { ... DataColumn description = newProducts.Columns.Add ("Description", Type.GetType("System.String")); description.AllowDBNull = true; UniqueConstraint productUnique = new UniqueConstraint(new DataColumn[] {newProducts. Columns["ProductID"], newProducts. Columns["ProductName"]}); products.Tables["NewProducts"].Constraints.Add(productUnique); if (productID.AutoIncrementStep > 100) { productName.Unique = false; }

Question

You want to enforce unique constraints for an array of columns to ensure that data for each row in these columns is unique. Which options help you to do this? Options: 1. Pass the UniqueConstraint object to the Add method of the Constraints property of the table 2. Set the Unique property of all columns to false 3. Remove the primary key property of the columns 4. Set the Unique property of all columns to true

Answer
Option 1: Correct. The best option to enforce unique constraint on an array of columns is to use the UniqueConstaint object. Option 2: Incorrect. When you set the Unique property of all columns to false the unique constraints on the columns are removed. Option 3: Incorrect. When you remove the primary key property of the columns, it automatically removes the unique constraint enforced on the columns. Option 4: Correct. Although the Unique property is primarily used for enforcing unique constraint on individual columns, you can also use it to enforce unique constraints on multiple columns. Correct answer(s): 1. Pass the UniqueConstraint object to the Add method of the Constraints property of the table 4. Set the Unique property of all columns to true When you create an untyped DataSet, a schema does not get created in the process. If you want to create a schema file from an untyped DataSet, you can do it by exporting the structure of the DataSet. To do this, you can use the WriteXmlSchema method. The WriteXmlSchema method is an overloaded method. You can use this method to write the XML schema to a file using a Stream object, to a TextWriter object, or to an XmlWriter object.

Try It
You want to write the structure of the products DataSet as an XML schema to a file named schemaFile1. To do this, you pass the name of the file as a string to the WriteXmlSchema method used for creating the schema.

The existing code is: public partial class Form1 : Form { ... products.Tables["NewProducts"]. Constraints.Add(productUnique); if (productID.AutoIncrementStep > 100) { productName.Unique = false; } MISSING CODE(schemaFile1); To complete the task 1. Type products.WriteXmlSchema and click the Enter key provided Type products.WriteXmlSchema in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. The XML schema is created using the WriteXmlSchema method. The WriteXmlSchema method writes the schema for the products DataSet to the specified XML file.

Code
<?xml version="1.0" standalone="yes"?> <xs:schema id="Products" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="Products" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="Constraint1" msdata:PrimaryKey="true"> <xs:selector xpath=".//NewProducts" /> <xs:field xpath="ProductID" /> </xs:unique> <xs:unique name="Constraint2"> <xs:selector xpath=".//NewProducts" /> <xs:field xpath="ProductID" /> <xs:field xpath="ProductName" /> </xs:unique> </xs:element> </xs:schema>

Question
Complete the code to enforce unique constraints on the CustomerID and CustomerName columns without setting the Unique property of the column to true. Code
public partial class Form1 : Form { ... DataColumn customerName = newCustomers.Columns.Add ("CustomerName"); customerName.ReadOnly = true; UniqueConstraint custUnique = new UniqueConstraint(new DataColumn[] {newCustomers. Columns["CustomerID"], newCustomers.Columns["CustomerName"]}); customers.Tables["NewCustomers"].INSERT THE MISSING CODE(custUnique);

Answer
You need to add the UniqueConstraint object to the ConstraintCollection of the table to enforce unique constraints for the CustomerID and CustomerName columns. Correct answer(s): 1. Constraints.Add

Question
In your application, you want to remove the unique constraint enforced on the CustomerName column when the number for new customers exceeds 500. Complete the code to remove the constraint. Code
public partial class Form1 : Form { ... DataColumn customerName = newCustomers.Columns.Add ("CustomerName"); customerName.ReadOnly = true; UniqueConstraint custUnique = new UniqueConstraint(new DataColumn[] {newCustomers. Columns["CustomerID"], newCustomers.Columns["CustomerName"]}); customers.Tables["NewCustomers"].Constraints.Add(custUnique); if(customerID.AutoIncrementStep > 500)

{ customerName.INSERT THE MISSING CODE; }

Answer
You set the value of the Unique attribute of the CustomerName column to false to remove the unique constraint on the column. Correct answer(s): 1. Unique = false

Summary
Untyped DataSets are used to handle data that has unknown structure at design time. You can create DataSets programmatically by first creating a DataSet instance. You then add a DataTable object to the DataSet, add columns to the table created, and finally, set the properties of the columns. DataColumn object properties enable you to control the data entered in the column. You can set a column or column array as the primary key using the PrimaryKey property of the DataColumn object. If you want a particular column or column array to contain unique values in each row, you can enforce unique constraints using the UniqueConstraint object or the Unique property. After creating the untyped DataSet, you can create an XML schema for the untyped DataSet using the WriteXmlSchema method.

Table of Contents
| Print | Contents | Close |

Working with DataAdapters using Visual C# 2010


Learning Objectives
After completing this topic, you should be able to

recognize how to programmatically fill an existing DataTable using a DataAdapter identify how to use a DataAdapter object to retrieve data recognize the steps for using the Fill method of a DataAdapter to page through query results

1. Loading data into Datasets


When you create a DataSet, it is initially empty. You need to load data by adding data to the DataTable objects in the DataSet.

Note
When data is loaded into a DataSet, events are raised and constraints checked. You can load data into a DataSet by
creating and filling DataRow objects You can create DataRow objects and add them to a DataTable object to load data in a table that already contains data. To create a new DataRow object, you call the NewRow method of the DataTable object, which creates a new row with the same schema as the DataTable object. Data is then inserted in the new row using individual column names or the column index. After this is done, you use the Add method to add the DataRow objects to the DataRowCollection of the table. using a DataReader object Using a DataReader object it obtains a stream of sequential and read-only data from a data source you can load data into a DataSet. You use a DataReader object when you need to load a large amount of data from the data source and want to only display data and not update the data source with data changes. Using a DataReader object to load data requires you to create a Command object, use the ExecuteReader method to create a DataReader object, and then load the data to the DataSet. using the ReadXml method When you want to load data from an XML document to a DataSet, you use the ReadXml method. Using the ReadXml method, you can also read the XML schema of the data source along with the data. However, to do this, you need to use an overload of the ReadXml method that takes the mode as a parameter. When you use the overloaded method, you need to set the value of the mode parameter as ReadSchema. using the Fill method of a DataAdapter

The Fill method of a DataAdapter object uses the existing schema of the DataSet to load data into the tables. If the DataSet does not have tables and rows, the Fill method also creates them. DataSets use DataAdapters to control the interaction with data sources. You use DataAdapters for loading data when you want to retrieve data from a data source and update the data source with changes.

Question
Which of these options actually load data into DataSets? Options:
1. 2. 3. 4. 5. The Fill method of a DataAdapter object Addition of DataRow objects in a DataTable Using a DataReader object The Update method The ReadXml method

Answer
Option 1: Correct. The Fill method is the most commonly used method of retrieving data from the data source and populating the DataSet. Option 2: Incorrect. You create DataRow objects as the first step towards adding data to a table. However, you then need to add data to each row column. Option 3: Correct. You can use a DataReader object to obtain a stream of read-only data from a source. To create a DataReader, you first create a Command object and then use its ExecuteReader method to create and use the DataReader. Option 4: Incorrect. The Update method is a command used to update the data source with changes made to the data in the DataSet. This is used in conjunction with the Fill method. Option 5: Correct. You use the ReadXml method when you want to load data from an XML document to a DataSet. Correct answer(s): 1. The Fill method of a DataAdapter object 3. Using a DataReader object 5. The ReadXml method The most common method used to populate a DataSet is using a DataAdapter. DataAdapters act as a link between DataSets and data sources and enable interaction between the two.

DataAdapters

are provider specific. For example, if you are using a SQL Server database, you need to create and use a SqlDataAdapter. A DataAdapter connects to the data source using a Connection object and uses SQL commands to interact or work with data from the data source. The DataAdapter uses any of the four properties SelectCommand, InsertCommand, UpdateCommand, and DeleteCommand to work with data. You use the SelectCommand property to retrieve data from the data source and the remaining properties are used to update the data source with data changes. To use a DataAdapter to load a DataSet, you must first create one. Suppose you want to populate an untyped DataSet with data from the Emplnfo table present in the SQL database InvestmentWorks. You first specify a SQL command, such as the Select command, and a Connection object. The variable, selectString, which stores the Select statement is also created. This statement is the actual SQL command that enables data retrieval from the data source.

Graphic
The line of code that creates a string variable to store the SQL command to retrieve data from a specific data source is: selectString = "Select * from EmpInfo";

Code
using System.Data; using System.Data.SqlClient; public class UntypedDatasetClass { public static void Main() { string selectString; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet();

You then connect to a SQL database using the SqlConnection object, and also type the code to create an untyped DataSet object.

Graphic
The code that creates a SQLConnection object and a new untyped DataSet object is: myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet();

Code
using System.Data; using System.Data.SqlClient; public class UntypedDatasetClass { public static void Main() { string selectString; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet();

Try It
You can now create an instance of a SqlDataAdapter class using the connection object and the Select statement. The existing code is: using System.Data; using System.Data.SqlClient; public class UntypedDatasetClass { public static void Main() {

string selectString; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet(); myAdapter = MISSING CODE(selectString, myConn); To complete the task
1. Type new SqlDataAdapter and click the Enter key provided Type new SqlDataAdapter in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to create a SQLDataAdapter object is added.

2. Using DataAdapters to fill DataTables


You use the Fill method of the DataAdapter object to retrieve the data from the data source and fill the DataSet. Some of the overloaded Fill methods you can use to populate a DataSet are
Fill(DataSet)

You use this method to copy rows from a data source and add them to the DataSet.
Fill(DataTable, IDataReader)

If you want to populate a specific DataTable object with rows of data using a specified DataReader, you use this method.
Fill(array<DataTable>[]()[], IDataReader, Int32, Int32)

When you want to load data into a specified range of records in a collection of DataTable objects, you use this method.
Fill(DataSet, String, IDataReader, Int32, Int32)

To load data into a specified range of rows in a DataSet, you use this method.

If the connection with the data source is open when an overload of the Fill method is called, the method retrieves the data and leaves the connection open. If the connection is closed, the method opens the connection, retrieves data, and then closes the connection.

Try It
You want to populate the DataSet you created, myDataSet, using the Fill method of the SqlDataAdapter. The existing code is: using System.Data; using System.Data.SqlClient; public class DataTableDemo { public static void Main() { string selectString; SqlConnection myConn; DataTable myDataTable; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, ); myAdapter.MISSING CODE; To complete the task
1. Type Fill(myDataSet) and click the Enter key provided Type Fill(myDataSet) in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

myConn

The code to fill the DataSet with data from the table in the data source is specified.

Once a DataSet is populated with data from the data source, you can loop through the results. You do this to display one row at a time on the console. For example, if you want to display the contents of the first three columns of each record in the DataSet to the user, you use this looping construct.

Graphic
The code used to loop through the records in a dataset is: foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); }

Code
... try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(myDataSet); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.ReadLine(); } catch (SqlException ex) { Console.WriteLine("Could not open connection:" + ex.Message); Console.ReadLine(); } finally { myConn.Close(); } } }

You can use the Fill method not only to load a DataSet but also to load just a DataTable object. For example, if you want to load data into the EmpInfo table instead of loading a DataSet, you can do so using the appropriate overload of the Fill method. Before calling the Fill method, you create the DataTable object as given in the code.

Graphic
The line of code that creates a new DataTable object is: myDataTable = new DataTable();

Code
public class DataTableDemo { public static void Main() { string selectString; SqlConnection myConn; DataTable myDataTable; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataTable = new DataTable(); myAdapter = new SqlDataAdapter(selectString, myConn);

Then you create a DataAdapter, myAdapter, using the Connection object and the Select statement.

Graphic
The code that creates the DataAdapter is: myAdapter = new SqlDataAdapter(selectString, myConn);

Code
public class DataTableDemo { public static void Main() { string selectString; SqlConnection myConn; DataTable myDataTable; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataTable = new DataTable();

myAdapter = new SqlDataAdapter(selectString, myConn);

Try It
You want to fill the DataTable, myDataTable, using a SqlDataAdapter. Complete the code to fill the DataTable object. The existing code is: public class DataTableDemo { public static void Main() { string selectString; SqlConnection myConn; DataTable myDataTable; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataTable = new DataTable(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.MISSING CODE; Console.WriteLine( myDataTable.Rows[0][0] + "" + myDataTable.Rows[0][1] + "" + myDataTable.Rows[0][2]); Console.ReadLine(); } catch (SqlException ex) { Console.WriteLine("Could not open connection:" + ex.Message); Console.ReadLine(); } finally { myConn.Close(); }

To complete the task


1. Type Fill(myDataTable) and click the Enter key provided Type Fill(myDataTable) in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to fill the DataTable object from the data in a data source is specified. After loading the DataTable object, you can access the records in the table and even display the data to the user on the console. For example, you use this code to display the data in the first three columns of the first row in myDataTable.

Graphic
The line of code that displays the data in the first three columns of the first record of myDataTable is: Console.WriteLine( myDataTable.Rows[0][0] + " " + myDataTable.Rows[0][1] + " " + myDataTable.Rows[0][2]);

Code
public class DataTableDemo { public static void Main() { ... try { myDataTable = new DataTable(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(myDataTable); Console.WriteLine( myDataTable.Rows[0][0] + " " + myDataTable.Rows[0][1] + " " + myDataTable.Rows[0][2]); Console.ReadLine(); } catch (SqlException ex) { Console.WriteLine("Could not open connection:" + ex.Message); Console.ReadLine(); } finally { myConn.Close(); }

If the table in the DataSet contains a primary key, the Fill method overwrites data in the DataSet object with data from the data source for the rows with matching primary key column values. The Fill method appends the data to the table if the primary key is not present.

But the DataAdapter object does not set the primary key value when you use a SelectCommand property to return the result of an OUTER JOIN. In these cases, you can explicitly define the primary key to ensure that rows are not duplicated. Sometimes, multiple result sets need to be added to the DataSet. The DataAdapter object then creates a table in the DataSet for each result set. The DataAdapter object also names the tables automatically using integer values along with the table name specified. For example, if three result sets are returned and the name specified for the table is Employees, the DataAdapter object creates three tables and names them Employees, Employees1, and Employees2. If you want to add a table to the DataSet and configure the schema to match the schema of the data source, you use the FillSchema method. In the syntax for this method, the SchemaType parameter specifies how to insert the schema in the DataSet. The DataSet parameter specifies the name of the DataSet.

Graphic
The part of syntax that specifies the parameters of the FillSchema method are: DataSet dataSet, SchemaType schemaType

Syntax
public override DataTable[] FillSchema( DataSet dataSet, SchemaType schemaType ) The FillSchema method of the DataAdapter object first adds a table to the DataSet and then adds columns to the table. The method then sets the column properties such as AllowDBNull, AutoIncrement, MaxLength, ReadOnly, and Unique to match the datasource. The FillSchema method also sets the PrimaryKey and Constraints properties in the DataSet to match the data source.

Syntax
public override DataTable[] FillSchema( DataSet dataSet, SchemaType schemaType )

Question
You want to load data into a DataTable object using a DataAdapter object. Identify the steps that are part of the process of creating the DataAdapter object. Options:
1. 2. 3. 4. 5. Create a connection object Specify the SQL command Create a DataSet object Create a DataTable object Create a DataAdapter object

Answer
Option 1: Correct. You need to create and use a connection object to connect to the data source. This object is then passed to the constructor of DataAdapter object. Option 2: Correct. You need to specify the SQL command that enables you to retrieve data from the data source. You pass the SQL command as a string to the constructor of the DataAdapter class. Option 3: Incorrect. You don't need to create a DataSet object in this scenario; you just need to load a DataTable object. Option 4: Incorrect. Although you need to create a DataTable object for this scenario, you don't need it to create a DataAdapter object. Option 5: Correct. You create a DataAdapter object using the constructor that accepts the connection object and SQL command as parameters. Correct answer(s): 1. Create a connection object 2. Specify the SQL command 5. Create a DataAdapter object

Question
You are creating an untyped DataSet, custDataSet, to hold data from the CustInfo table of a SQL database. To populate the DataSet object, you use a SqlDataAdapter object named custAdapter. Complete the code to create the DataAdapter object using the Connection object and the Select statement. Code
using System.Data;

using System.Data.SqlClient; public class { UntypedDatasetClass

public static void Main() { string selectString; SqlConnection custConn; DataSet custDataSet; SqlDataAdapter custAdapter; selectString = "Select * from CustInfo"; custConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=PrivilegedCust;" + "Integrated Security=true"); try { custDataSet = new DataSet(); custAdapter = new SqlDataAdapter (INSERT THE MISSING CODE);

Answer
To create the SqlDataAdapter object, you need to specify the Select statement and the connection object. Correct answer(s):
1. selectString, custConn

Question
Using the SqlDataAdapter, custAdapter, that you created, you want to populate the DataSet, custDataSet. Complete the code to populate the DataSet. Code
using System.Data; using System.Data.SqlClient; public class { UntypedDatasetClass

public static void Main() { string selectString; SqlConnection custConn; DataSet custDataSet; SqlDataAdapter custAdapter; selectString = "Select * from CustInfo"; custConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=PrivilegedCust;" + "Integrated Security=true"); try

{ custDataSet = new DataSet(); custAdapter = new SqlDataAdapter(selectString, custConn); custAdapter.INSERT THE MISSING CODE;

Answer
To populate the DataSet using the SqlDataAdapter object, you use the Fill method and pass the name of the DataSet as the parameter to the Fill method. Correct answer(s):
1. Fill(custDataSet)

3. Paging through query results


When you load data from a table in the data source into a DataSet, the query results returned contain all the records, which can be a large amount of content to process. To ensure that results are returned as manageable chunks of records, you can page through query results using the startRecord and maxRecords parameters of the Fill method.

Syntax
DataAdapter.Fill(DataSet, startRecord, maxRecords, "TableName");

Note
Pages are smaller chunks of data returned. The DataSet parameter is used to specify the name of the DataSet to be loaded. The startRecord parameter specifies the number of the starting record in the data source. The number of records to be displayed in a page is specified using the maxRecords attribute, and the name of the table is specified using the TableName parameter.

Syntax
DataAdapter.Fill(DataSet, startRecord, maxRecords, "TableName"); Suppose you want to fill the DataSet, myDataSet, with pages of records from the EmpInfo table of the database. To enhance readability, you want each page to have just ten records. You declare and initialize an integer variable pageSize with the value 10 to use as the maxRecords parameter. Because you want to start from the beginning of the EmpInfo table, you

declare an integer variable currentIndex to use as the startRecord parameter of the Fill overload.

Graphic
The lines of code that declare and initialize the integer variables are: int currentIndex = 0; int pageSize = 10;

Code
using System.Data; using System.Data.SqlClient; using System.Data.Common; public class PageResultsDemo { public static void Main() { int currentIndex = 0; int pageSize = 10; string selectString; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo" + " ORDER BY EmpID ASC"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(myDataSet);

Try It
To page through the query results, you use the overload of the Fill method of myAdapter. You need to specify the DataSet and startRecord parameters to complete the code for calling the Fill overload. The existing code is: using System.Data; using System.Data.SqlClient; using System.Data.Common; public class PageResultsDemo

{ public static void Main() { int currentIndex = 0; int pageSize = 10; string selectString; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo" + " ORDER BY EmpID ASC"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(MISSING CODE, To complete the task
1. Type myDataSet, currentIndex and click the Enter key provided Type myDataSet, currentIndex in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

pageSize, "EmpInfo");

The code to fill the DataSet with pages of records from the data source is added. After filling the DataSet, you want to display the records in the first page on the console to the user. To do this, you access each row of data and display the values as given in the code.

Graphic
The lines of code that displays each row of data on the console is: foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); }

Code

public class PageResultsDemo { public static void Main() { ... try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter (selectString, myConn); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.WriteLine();

Next you want to check if the user wants to load the DataSet with the next page of results and view it. So you add this code.

Graphic
The line of code that prompts the user to confirm viewing the data in the next page is: Console.WriteLine("Press n then" + " Enter for next set of records");

Code
public class PageResultsDemo { public static void Main() { ... try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.WriteLine(); Console.WriteLine("Press n then" + " Enter for next set of records");

You want to append the next page of records to the DataSet when the user confirms by typing "n." To do this, you first need to increment the value of the startRecord parameter, currentIndex, by the value of the maxRecords parameter, pageSize. You add this code within a looping construct.

Graphic
The line of code that increments the value of the startRecord parameter is: currentIndex = currentIndex + pageSize;

Code
public class PageResultsDemo { public static void Main() { ... try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.WriteLine(); Console.WriteLine("Press n then" + " Enter for next set of records"); string nextSet; nextSet = Console.ReadLine(); if(nextSet == "n") { currentIndex = currentIndex + pageSize; }

Try It
Before you append the next page of records to the Dataset, you need to clear the existing rows of data from the EmpInfo table in the Dataset. The existing code is: try { ... myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); }

Console.WriteLine(); Console.WriteLine("Press n then" + " Enter for next set of records"); string nextSet; nextSet = Console.ReadLine(); if (nextSet == "n") { currentIndex = currentIndex + pageSize; myDataSet.Tables["EmpInfo"].MISSING CODE(); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); To complete the task
1. Type Rows.Clear and click the Enter key provided Type Rows.Clear in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key.

The code to clear the existing rows of data in the EmpInfo table is added. The DataSet that you populated now resides in memory. You want to display the data one line at a time to the user. To do this, you add this code.

Graphic
The lines of code that displays each record of the page in the DataSet is: foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); }

Code
if (nextSet == "n") { currentIndex = currentIndex + pageSize; myDataSet.Tables["EmpInfo"].Rows.Clear(); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1]

+ " " + dr[2]); } Console.ReadLine(); } else { Console.WriteLine("Goodbye!"); Console.ReadLine(); }

If the user doesn't confirm, you display a message to the user by adding this code. This ensures that the user clearly knows that the next set of records is not added to the DataSet.

Graphic
The lines of code that ensure a message is displayed if the user doesn't type n is: else { Console.WriteLine("Goodbye!");

Code
try { ... myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.WriteLine(); Console.WriteLine("Press n then" + " Enter for next set of records"); string nextSet; nextSet = Console.ReadLine(); if (nextSet == "n") { currentIndex = currentIndex + pageSize; myDataSet.Tables["EmpInfo"].Rows.Clear(); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.ReadLine(); } else { Console.WriteLine("Goodbye!"); Console.ReadLine(); }

You can use alternative names for columns in the database. For example, you might need to follow a software naming convention in your organization that requires column names to be different from those used in the data source. You can implement alternate names using the DataTableMapping objects in ADO.NET.

DataTableMapping objects map the data returned from a data source to the data in a DataTable object. A DataAdapter object can contain many DataTableMapping objects. You can store the collection of DataTableMapping objects of a DataAdapter in its TableMappings property. When a DataAdapter object fills a DataTable, it uses the mapping to check the column names.

In the SqlDataAdapter, myAdapter, you want to create a DataTableMapping object called mapping and add it to the collection of DataTableMapping objects called myTables. You want to include the DataTableMapping code in a user-defined function, ShowTableMappings, and call the function from your class.

Graphic
The line of code that calls the function is: ShowTableMappings(myAdapter);

Code
public class DataTableMappingClass { public static void Main() { string selectString; SqlConnection myConn; DataSet myDataSet; SqlDataAdapter myAdapter; selectString = "Select * from EmpInfo"; myConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=InvestmentWorks;" + "Integrated Security=true"); try { myDataSet = new DataSet(); myAdapter = new SqlDataAdapter(selectString, myConn); myAdapter.Fill(myDataSet); ShowTableMappings(myAdapter); } catch ( SqlException ex ) { Console.WriteLine("Could not open connection:" + ex.Message); Console.ReadLine(); } finally { myConn.Close(); } }

In the ShowTableMappings function, you first create a DataTableMappingCollection object myTables. Next you create a DataTableMapping object. While creating the object, you can specify an alternative name for the table used from the data source.

Graphic

The lines of code that creates a DataTableMappingCollection object is: DataTableMappingCollection myTables = myAdapter.TableMappings;

Code
public static void ShowTableMappings(SqlDataAdapter myAdapter) { DataTableMappingCollection myTables = myAdapter.TableMappings; }

Try It
You now want to create a DataTableMapping object called mapping, and you want to specify the alternative name for the table Emplnfo as DataEmpInfo. The existing code is: public static void ShowTableMappings ( SqlDataAdapter myAdapter) { DataTableMappingCollection myTables = myAdapter.TableMappings; DataTableMapping mapping = MISSING CODE 1 ("EmpInfo", "MISSING CODE 2"); } To complete the task
1. Type new DataTableMapping and click the Enter key provided Type new DataTableMapping in the MISSING CODE field. The Courseware Player cannot recognize input from your keyboard Enter key. Therefore, please click the Enter button provided instead of pressing the Enter key. 2. Type DataEmpInfo and click the Enter key provided The existing code is: public static void ShowTableMappings ( SqlDataAdapter myAdapter) { DataTableMappingCollection myTables = myAdapter.TableMappings; DataTableMapping mapping = new DataTableMapping ("EmpInfo", "MISSING CODE 2"); }

The code to create a DataTableMapping object is added.

Because the Add method of DataTableMappingCollection takes the parameter of the Object type, you convert the DataTableMapping instance type to Object.

Graphic
The line of code that converts the DataTableMapping object is: myTables.Add((Object)mapping);

Code
public static void ShowTableMappings(SqlDataAdapter myAdapter) { DataTableMappingCollection myTables = myAdapter.TableMappings; DataTableMapping mapping = new DataTableMapping("EmpInfo", "DataEmpInfo"); myTables.Add((Object)mapping); }

In the ShowTableMappings function, you want to specify new names for three columns of the EmpInfo table. To map EmpInfo table columns to new column names, you use the Add method of the ColumnMappings property of the DataTableMapping object.

Graphic
The lines of code that map the columns EmpID to DataEmpID, DeptID to DataDeptID, and EmpName to DataEmpName are: mapping.ColumnMappings.Add("EmpID", "DataEmpID"); mapping.ColumnMappings.Add("DeptID", "DataDeptID"); mapping.ColumnMappings.Add("EmpName", "DataEmpName");

Code
public static void ShowTableMappings(SqlDataAdapter myAdapter) { DataTableMappingCollection myTables = myAdapter.TableMappings; DataTableMapping mapping = new DataTableMapping("EmpInfo", "DataEmpInfo"); myTables.Add((Object)mapping); Console.WriteLine("Table {0} added to {1}" + " table mapping collection.", mapping.ToString(), myTables.ToString()); Console.WriteLine(); mapping.ColumnMappings.Add("EmpID", "DataEmpID"); mapping.ColumnMappings.Add("DeptID", "DataDeptID"); mapping.ColumnMappings.Add("EmpName", "DataEmpName"); Console.WriteLine("Col Index\tCol Name");

Console.WriteLine(); for (int i = 0; i < mapping.ColumnMappings.Count; i++) { Console.WriteLine(" {0}\t" + "{1}", i, mapping.ColumnMappings[i].ToString()); } Console.ReadLine(); }

You then display the column index and column names on the console using this code.

Graphic
The lines of code that display the column index and the column names are: Console.WriteLine("Col Index\tCol Name"); Console.WriteLine(); for (int i = 0; i < mapping.ColumnMappings.Count ; i++) { Console.WriteLine(" {0}\t" + "{1}", i, mapping.ColumnMappings[i].ToString()); }

Code
public static void ShowTableMappings(SqlDataAdapter myAdapter) { DataTableMappingCollection myTables = myAdapter.TableMappings; DataTableMapping mapping = new DataTableMapping("EmpInfo", "DataEmpInfo"); myTables.Add((Object)mapping); Console.WriteLine("Table {0} added to {1}" + " table mapping collection.", mapping.ToString(), myTables.ToString()); Console.WriteLine(); mapping.ColumnMappings.Add("EmpID", "DataEmpID"); mapping.ColumnMappings.Add("DeptID", "DataDeptID"); mapping.ColumnMappings.Add("EmpName", "DataEmpName"); Console.WriteLine("Col Index\tCol Name"); Console.WriteLine(); for (int i = 0; i < mapping.ColumnMappings.Count; i++) { Console.WriteLine(" {0}\t" + "{1}", i, mapping.ColumnMappings[i].ToString()); } Console.ReadLine(); }

While DataAdapters objects can be configured programmatically, you can also configure them in Visual Studio 2010 using the DataAdapter Configuration Wizard. You can edit the properties of an existing DataAdapter object or create a new DataAdapter object and set its properties.

Supplement
Selecting the link title opens the resource in a new browser window. Learning Aid Access the learning aid Using the Data Adapter Configuration Wizard to learn how to configure and set the properties of a DataAdapter object.

Question
In your application, you added code to create an untyped DataSet and populate it with data from the DataTable, CustInfo using a SqlDataAdapter, custAdapter. However, you want to be able to page through the query results. Complete the code that adds the first page of data to the DataSet. Code
using System.Data; using System.Data.SqlClient; using System.Data.Common; public class PageResultsDemo { public static void Main() { int currentIndex = 0; int pageSize = 10; string selectString; SqlConnection custConn; DataSet custDataSet; SqlDataAdapter custAdapter; selectString = "Select * from CustInfo" + " ORDER BY CustID ASC"; custConn = new SqlConnection("Data Source=(local);" + "Initial Catalog=PrivilegedCust;" + "Integrated Security=true"); try { custDataSet = new DataSet(); custAdapter = new SqlDataAdapter(selectString, custConn); custAdapter.Fill(custDataSet, currentIndex, INSERT THE MISSING CODE);

Answer

To add the first page of data to the DataSet using the SqlDataAdapter object, you specify the page size and table name as parameters for the appropriate overload of the Fill method. Correct answer(s):
1. pageSize, "CustInfo"

Question
While populating an untyped Dataset using a SqlDataAdapter object, you decide to page through query results. So you add the code to fill the DataSet with a page of records. Now specify the code to increase the currentIndex to the starting index for the next page of data. Code
try { ... myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.WriteLine(); Console.WriteLine("Press n then" + " Enter for next set of records"); string nextSet; nextSet = Console.ReadLine(); if (nextSet == "n") { currentIndex = INSERT THE MISSING CODE myDataSet.Tables["EmpInfo"].Rows.Clear(); myAdapter.Fill(myDataSet, currentIndex, pageSize, "EmpInfo"); foreach(DataRow dr in myDataSet.Tables[0].Rows) { Console.WriteLine(dr[0] + " " + dr[1] + " " + dr[2]); } Console.ReadLine(); } else { Console.WriteLine("Goodbye!"); Console.ReadLine(); }

Answer
To add the next page of data to the DataSet, you need to increment the current index with the page size. Correct answer(s):
1. currentIndex + pageSize;

Summary
You can load data into an untyped DataSet using DataRow objects, a DataReader, the ReadXml method, or the Fill method of DataAdapter. The most common method is to use a DataAdapter. To load an untyped DataSet with data, you can use one of the overloads of the Fill method of DataAdapter. You can also use the FillSchema method of DataAdapter to load a DataSet where the schema of the DataSet matches the data source. Sometimes, you might want to add records to a DataSet incrementally. To do this, you use the appropriate overload of the Fill method and page through the query results. While loading data, you can use the DataTableMapping feature to map the names of tables and columns to alternate names.

Table of Contents
| Print | Contents | Close |

Working with Untyped DataSets using Visual C# 2010


Learning Objectives
After completing this topic, you should be able to

create an untyped DataSet and DataTable structure enforce and remove unique constraints use a DataAdapter to fill a DataSet and enable results to be paged through

Exercise Overview
In this exercise, you are required to work with untyped DataSets using Visual C# 2010. This involves the following tasks:

creating untyped DataSets and DataTables working with unique constraints populating DataSets using DataAdapter

Creating untyped DataSets and DataTables


You are developing an application that retail outlets can use to obtain product information from the head office. You are unsure of the data structure, so you want to ensure that your application

can handle data of any structure. You want to create a DataSet that works with all types of data structures.

Question
Add code to create an appropriate DataSet object, products named "Products" that enables you to handle data of any structure. Enter code with a space on either side of the equal sign. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { INSERT THE MISSING CODE;

Answer
To work with data of unknown structure, you need to create an untyped DataSet. Correct answer(s):
1. DataSet products = new DataSet("Products")

Question
To the untyped DataSet created, you want to add a DataTable object called NewProducts to store the details of new products. Complete the code to do this. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts =INSERT THE MISSING CODE("NewProducts");

Answer
To add the table to the DataSet you use the appropriate method of the Tables property of the DataSet. Correct answer(s):
1. products.Tables.Add

Question

You want a column added to the DataTable newProducts to store the product ID in it. Specify the code that adds the column named ProductID. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add("NewProducts"); DataColumn productID = INSERT THE MISSING CODE("ProductID", Type.GetType("System.Int32"));

Answer
To add a column to store the product ID, you use the Add method of the Columns property of the DataTable. Correct answer(s):
1. newProducts.Columns.Add

Question
Complete the code to ensure that the ProductID column autoincrements for every row. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { DataSet products = new DataSet("Products"); DataTable newProducts = products.Tables.Add("NewProducts"); DataColumn productID = newProducts.Columns.Add ("ProductID", Type.GetType("System.Int32")); newProducts.PrimaryKey = new DataColumn[] { newProducts.Columns["productID"] }; productID.INSERT THE MISSING CODE;

Options:
1. AutoIncrement = true 2. AutoIncrementSeed = true 3. AutoIncrementStep = true

Answer
To set the column as an autoincrementing column, you need to set the value of the AutoIncrement property of the DataColumn to True. Option 1: Correct. You need to set the value of the AutoIncrement property of the DataColumn to true to set the column as an autoincrementing column. Option 2: Incorrect. The AutoIncrementSeed is used to set the starting value of the first row of the autoincrementing column in a table. Option 3: Incorrect. The AutoIncrementStep property is used to set the increment value for each row of an autoincrementing column. Correct answer(s):
1. AutoIncrement = true

Working with unique constraints


After creating an untyped DataSet and adding a DataTable NewProducts to which you specified column properties, you now want to ensure that all the rows in the ProductID and ProductName columns contain unique values. You also want to ensure that the column ProductName cannot have unique values if the number of products in the table exceeds 100.

Question
Complete the code to enforce unique constraints on the columns ProductID and ProductName without setting the value of the Unique property. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { ... DataColumn description = newProducts.Columns.Add ("Description", Type.GetType("System.String")); description.AllowDBNull = true; UniqueConstraint productUnique = new UniqueConstraint(new DataColumn[] {newProducts. Columns["ProductID"], newProducts. Columns["ProductName"]});

products.Tables["NewProducts"].INSERT THE MISSING CODE.Add(productUnique);

Answer
You add the UniqueConstraint object to the table's Constraints property to enforce unique constraints for the ProductID and ProductName columns. Correct answer(s):
1. Constraints

Question
To ensure that the column ProductName doesn't have unique values if the number of products in the table is more than 100, you decide to remove the unique constraint. Specify the code to remove the unique constraint on the ProductName column when the number of products exceeds 100. Enter code with a space on either side of the equal sign. Code
public partial class Form1 : Form { private void Form1_Load(object sender, EventArgs e) { ... DataColumn description = newProducts.Columns.Add ("Description", Type.GetType("System.String")); description.AllowDBNull = true; UniqueConstraint productUnique = new UniqueConstraint(new DataColumn[] {newProducts. Columns["ProductID"], newProducts. Columns["ProductName"]}); products.Tables["NewProducts"].Constraints.Add(productUnique); if (productID.AutoIncrementStep > 100) { productName.INSERT THE MISSING CODE; }

Answer
You set the value of the Unique attribute of the ProductName column to false to remove the unique constraint on the column.

Correct answer(s):
1. Unique = false

Populating DataSets using DataAdapters


You are now creating another untyped DataSet, products, to store data from the OldProducts table of the SQL database, RedRock. To do this, you need to create a SqlDataAdapter object and name it as productsAdapter. You also want to fill the DataSet using the SqlDataAdapter object. However, you want to add the details of each product only after confirming that the user wants to view the next record.

Question
Complete the code to create a new SqlDataAdapter productsAdapter to populate the untyped DataSet you are creating. Code
public class Module1 { ... public static void Main() { currIndex = 0; pageSize = 1; builder = new SqlConnectionStringBuilder(); builder["Data Source"] = "local"; builder["Integrated Security"] = true; builder["Initial Catalog"] = "RedRock"; quantityPerUnit.AllowDBNull = true; products.Tables["OldProducts"].Constraints.Add(productUnique); selectString = "SELECT ProductID, ProductName, " + "QuantityPerUnit, UnitPrice FROM dbo.Products " + "WHERE Discontinued='True'"; myConn = new SqlConnection(builder.ConnectionString); try { productsAdapter = new SqlDataAdapter (INSERT THE MISSING CODE); Console.WriteLine ("These products can no longer be ordered: "); GetData(); while (commandLine == "n") { products.Tables["OldProducts"].Rows.Clear(); currIndex += 1; GetData(); }

Answer

To create the SqlDataAdapter object, you need to use the Select statement and the connection object. Correct answer(s):
1. selectString, myConn

Question
You want to populate the DataSet using productsAdapter, the SqlDataAdapter you created. Specify code to fill the DataSet with the record of each product only after the user presses "n" to view the next record. Code
public static void GetData() { if (null != products.Tables["OldProducts"]) products.Tables["OldProducts"].Rows.Clear(); INSERT THE MISSING CODE(products, currIndex, pageSize, "OldProducts"); foreach(DataRow dr in products.Tables[0].Rows) { Console.WriteLine(dr[0].ToString() + " " + dr[1] + ", " + dr[2] + " - " + dr[3].ToString()); } products.Tables[0].TableName = "OldProducts"; commandLine = Console.ReadLine(); }

Answer
To populate the DataSet with details of each product, you need to use the appropriate overload of the Fill method. Correct answer(s):
1. productsAdapter.Fill

Table of Contents

Configuring Typed DataSets and TableAdapters


Learning Objectives
After completing this topic, you should be able to

recognize the steps for creating a typed DataSet

identify the steps for creating a TableAdapter, using the TableAdapter Configuration Wizard recognize the different ways to create TableAdapters recognize the steps for updating a TableAdapter

1. Creating a typed DataSet


A typed DataSet is a generated class that is inherited directly from the DataSet class and uses a specified XSD schema to define its structure. As a result, this class inherits all functionalities of the DataSet class and uses strongly typed properties, methods, and events. This allows rows and columns to be accessed directly by name. In addition to providing strongly typed access to data, a typed DataSet

allows the .NET code editor to automatically complete statements as you type enables type mismatch errors to be detected when the code is compiled rather than at run time

You can create typed DataSets using a command line utility or Visual Studio 2010. When using a command line utility, you first create an XML schema file that maps to the XML Schema Definition (XSD) standard. You then generate a DataSet using the XSD.exe command line utility provided with the .NET Framework Software Development Kit (SDK). The syntax to create an XML schema file is given.

Syntax
xsd.exe /d /l:CS XSDSchemaFile.xsd /n:XSDSchema.Namespace

Note
/d is used to specify a DataSet, /l the language to be used, in this case Visual C# (CS), and optionally /n can be used to create a namespace. You can compile the generated DataSet as a library using the VB or C# compiler. It is used to specify a library and it specifies any dependent libraries. The syntax to specify a library is given.

Syntax
csc.exe /t:library XSDSchemaFile.cs /r:System.dll /r:System.Data.dll To create typed DataSets using Visual Studio 2010, you can use two methods the Data Source Configuration Wizard or by adding a new DataSet item to the application and configuring it using DataSet Designer. When you create a typed DataSet in Visual Studio, the schema on which the DataSet is based is automatically generated.

As a .NET developer, you may want to connect your application to data in Visual Studio. You can perform this task using the Data Source Configuration Wizard. To do this, you create a DataSet using the Data Source Configuration Wizard. Suppose you are accessing customer data from a database. You want to enable type-safety by creating a typed DataSet.

Try It
You've created a new Windows Application project in Visual Studio and want to use the Data Source Configuration Wizard to create a DataSet. To do this, you first open the wizard. To complete the task
1. Select Data - Add New Data Source

The Data Source Configuration Wizard opens. The Choose a Data Source Type page enables you to establish a connection to a database or a service. Because you want to retrieve data from a database, you select Database.

Graphic
The Choose a Data Source Type page provides three options Database, Service, and Object. You can choose the objects required for generating data-bound controls.

Note
The Database option is the default selection on the Choose a Data Source Type page. After selecting the type of data source, you click Next to choose an appropriate data connection.

Graphic
In this case, Database is selected as the data source type. Then, you specify the data model you want to use. You can choose between a DataSet or an Entity Data Model. After you have made your choice, you can click Next.

Graphic
In this case, DataSet is selected as the data model.

Next you choose the connection string that connects your application to the selected data source. You can either select a connection string from the Which data connection should your application use to connect to the database drop-down list or configure a new connection by selecting the New Connection button. In this example, you are connecting to the RedRockMountainCycling database. To choose the data connection, you open the drop-down list and select srv100\sqlexpress.RedRockMountainCycling.dbo. Then you click Next to save the connection string in your application configuration file. The Save the Connection String to the Application Configuration File page lets you store the selected connection string in the application configuration file. To save the RedRockMountainCyclingConnectionString, you retain the default settings and click Next.

Graphic
RedRockMountainCyclingConnectionString is specified in the Yes, save the connection as text box. Saving the connection string helps you easily maintain and deploy your data connection.

Note
The Yes, save the connection as checkbox is selected by default. After saving the connection string, you select the database objects you want to include in your DataSet. A default name is provided for the DataSet but you can change that name if you wish.

Graphic
The Choose Your Database Objects page lists these nodes in the Which database objects do you want in your dataset? field Tables, Views, Stored Procedures, and Functions. The DataSet name field contains the DataSet name as RedRockMountainCyclingDataSet.

Try It
Suppose your company wants to mail promotional pamphlets to all existing customers. So you want to retrieve customer details from the Customers table by adding the table to the RedRockMountainCyclingDataSet. To complete the task
1. Expand the + (plus sign) icon for the Tables database object 2. Select the Customers (SaleOrders) checkbox and click Finish

The Customers table is included in the RedRockMountainCyclingDataSet. When you create the DataSet using the Data Source Configuration Wizard, it is added to your project.

Graphic
The RedRockMountainCyclingDataSet is added to the WindowsApplication5 node in the Solution Explorer. When you double-click RedRockMountainCyclingDataSet.xsd in Solution Explorer, the Customers DataTable which has been generated by mapping to the original database table is displayed in graphical format in DataSet Designer.

Graphic
The Customers DataTable contains these DataRows CustomerID, AccountNumber, Address, City, ZipCode, State, and CreditCardID. It also contains CustomersTableAdapter and the Fill and GetData () methods. To switch to the Data Sources window, you click the Data Sources tab. The Customers DataTable also appears as a subnode of the RedRockMountainCyclingDataSet parent node in the Data Sources window.

SkillCheck
You are developing an application to access the Employees table from the Northwind database. You want to ensure runtime type-safety. You've already created a new Windows Application project in Visual Studio. Create a typed DataSet using the Employees table. Task:
1. 2. 3. 4. 5. Open the Data Source Configuration Wizard. Choose a data source type that stores employee data. Choose an appropriate data connection to connect to the NORTHWIND database. Ensure you store the connection string in the application configuration file. Choose the database object that you want in your DataSet, select the appropriate DataTable, and then close the wizard.

Answer
To complete the task Step 1: Select Data - Add New Data Source

Step 2: Ensure Database is selected and click Next twice Step 3: Select srv100\sqlexpres.NORTHWIND.MDF.dbo from the Which data connection should your application use to connect to the database drop-down list and click Next Step 4: Ensure the Yes, save the connection as checkbox is selected and click Next Step 5: Click the + icon for the Tables database object, select the Employees checkbox, and click Finish

Try It
Visual Studio also provides a drag-and-drop method to configure a typed DataSet using DataSet Designer. Suppose you want to configure a typed DataSet using DataSet Designer for ensuring runtime type-safety. To do this, you first add a new DataSet template to your project. To complete the task
1. Select Project - Add New Item 2. Scroll down to select DataSet and click Add

The DataSet1 template is added to the project. When you add the newly created DataSet, it appears in the Data Sources window.

Graphic
The Data Sources window contains the DataSet, DataSet1. When you add the new DataSet, the DataSet Designer window opens. You can use the drag anddrop method in DataSet Designer to configure the DataSet by dragging items from the DataSet tab in the Toolbox or from a Server Explorer connection.

Graphic
The DataSet1.xsd window contains this text Use the DataSet Designer to visually create and edit typed datasets. Drag database items from Server Explorer or the DataSet tab in the Toolbox into the Designer area, or right-click here to add new item. Database Designer enables you to add database items associated with a data source and a connection using the Server Explorer. Additionally, you can use the DataSet tab in the Toolbox to configure items such as an adapter and DataTable for the DataSet.

Graphic
The DataSet Toolbox contains these options Pointer, TableAdapter, Query, DataTable, and Relation.

Question
You want to use the drag-and-drop method to create a typed DataSet. Which option is best suited to do this? Options:
1. the .NET Framework SDK tool 2. the DataSet Designer 3. the Data Source Configuration Wizard

Answer
Option 1: Incorrect. You can generate a typed DataSet using the XSD.exe command line utility provided with the .NET Framework SDK tool. Option 2: Correct. You can create a DataSet by dragging items such as adapters and tables onto the DataSet Designer. Option 3: Incorrect. When you use the DataSource Configuration Wizard to create a DataSet, the DataSet is configured according to the options you specify in the wizard. However, you can subsequently open the DataSet in the DataSet Designer to edit it. Correct answer(s): 2. the DataSet Designer

2. Creating a TableAdapter
Like untyped DataSets, typed DataSets use an adapter to connect to a data source, load source data, or update DataSet changes to the data source. However, typed DataSets use a specific type of adapter called a TableAdapter.
TableAdapters are optimized to interact with typed DataSets. They combine the functions the Command and DataAdapter objects and link the functionality to the DataSet structure.

of

Note
If required, you could configure a typed DataSet to use a DataAdapter but you can't configure an untyped DataSet to use a TableAdapter.

Technically, TableAdapters aren't part of ADO.NET or the .NET Framework. They are created as part of typed DataSets within Visual Studio at design time. However, a TableAdapter class is generated within a separate namespace when a TableAdapter is configured and a new instance of that class can be created to access the adapter programmatically. Unlike a DataAdapter, a TableAdapter can support any number of SQL queries, each exposed in a simple method call.
TableAdapters

include methods, such as the Fill, GetData, and Update methods to read and update data in the database. The data that the methods return must conform to the schema of the associated DataTable.

Note
The Fill methods are used to fill existing DataTables, while the GetData methods are used to return new filled DataTables.

Question
What are the benefits of using TableAdapters? Options:
1. 2. 3. 4. Support a number of SQL statements exposed in separate method calls Enable you to communicate with a data source using both untyped and typed DataSets Provide methods such as Fill and Update Allow data to be loaded that doesn't match the schema of the associated DataTable

Answer
Option 1: Correct. Unlike DataAdapters, TableAdapters can support multiple queries exposed in separate method calls. Option 2: Incorrect. TableAdapters can't be used with untyped DataSets. Option 3: Correct. TableAdapters include the Fill and Update methods to read and update data. Option 4: Incorrect. TableAdapter methods must return data that matches the schema of the associated DataTable. Correct answer(s):

1. Support a number of SQL statements exposed in separate method calls 3. Provide methods such as Fill and Update
TableAdapters are created inside strongly typed DataSets. TableAdapter in Visual Studio 2010:

There are three ways to create a

using the TableAdapter Configuration Wizard using Server Explorer database objects using the Data Source Configuration Wizard

When you run the Data Source Configuration Wizard to create a typed DataSet in Visual Studio, a TableAdapter is defined by default. Suppose you want to create a TableAdapter within a DataSet that retrieves product details from your SQL database. To do this, you first open the Data Source Configuration Wizard by clicking the Add New Data Source link in the Data Sources window. The wizard lets you connect to a data source type, such as a database or web service, and choose the database objects for your application. When you complete the wizard, a new DataSet, with one or more TableAdapters, is created.

Graphic
In this example, Orders is the ADO.NET DataTable and the TableAdapter is OrdersTableAdapter. Another way to create a TableAdapter is to use the TableAdapter Configuration Wizard. This wizard creates a single TableAdapter for each DataTable. To create a TableAdapter using this method, you first open an existing DataSet in DataSet Designer. Then you drag a TableAdapter from the DataSet tab of the Toolbox onto the Design surface. This opens up the TableAdapter Configuration Wizard. Next you run the wizard and add a new TableAdapter and DataTable to the DataSet.

Note
You can also right-click the DataSet Designer interface and select TableAdapter to create a TableAdapter. The third method for creating a TableAdapter is to use Server Explorer database objects. To do this, you first open a DataSet in DataSet Designer.

Next you drag a database object from a data connection in Server Explorer onto the DataSet Designer surface. This automatically adds the components, including the DataTables and the TableAdapter, to the DataSet. A TableAdapter is not part of the .NET Framework, but once it's created, you can access an instance of a specific TableAdapter programmatically and use its associated methods, such as Fill and GetData.

Syntax
DataSet1TableAdapters.EMPTableAdapter TableAdapterEmployees; TableAdapterEmployees = new DataSet1TableAdapters.EMPTableAdapter();

Question
You've created a DataSet to ensure runtime safety of your data. How do you establish communication between an application and a database in Visual Studio? Options:
1. Drag a TableAdapter from the Toolbox onto the Design surface 2. Drag a TableAdapter onto the Data Sources window 3. Drag a DataSet onto the Server Explorer Database object

Answer
Option 1: Correct. When you drag a TableAdapter from the Toolbox onto the Design surface, the TableAdapter Configuration Wizard opens. You can run this wizard to add a TableAdapter to the DataSet and connect it to the database. Option 2: Incorrect. You can open the Data Source Configuration Wizard by clicking Add New Data Source in the Data Sources window. When you run this wizard, a TableAdapter is created by default. Option 3: Incorrect. You drag a database object from Server Explorer onto the DataSet Designer surface. This automatically adds the components, including the DataTables and the TableAdapter, to the DataSet. Correct answer(s): 1. Drag a TableAdapter from the Toolbox onto the Design surface Suppose you want to retrieve the details of all products manufactured by your company. You have created a typed DataSet in DataSet Designer.

Graphic
The DataSet1.xsd is selected in Solution Explorer and the corresponding Designer window is opened. You now want to create a TableAdapter to execute queries and return DataTables to fill the DataSet. To do this, you first drag a TableAdapter from the DataSet tab of the Toolbox onto the Design surface. This opens the TableAdapter Configuration Wizard. You can use this wizard to create and edit TableAdapters.

Graphic
The Which data connection should your application use to connect to the database? drop-down list contains RedRockMountainCyclingConnectionString (Settings) as the default selection.

Try It
Next you specify the connection string required to connect to the database. You decide to use the NORTHWIND_MDF connection string to connect to the Northwind database. To complete the task
1. Select NORTHWIND_MDF.dbo from the drop-down list and click Next twice

The NORTHWIND_MDFConnectionString is selected. On the Choose a Command Type page, you select the type of command that the TableAdapter uses to access the database. You can use SQL statements or stored procedures to access the database. You can also create a new stored procedure using the SQL statements you specify into the wizard.

Graphic
The Choose a Command Type page contains three options Use SQL statements, Create new stored procedures, and Use existing stored procedures. The Use SQL statements option specifies a SQL statement. If you provide a single-table SELECT statement, the wizard can generate INSERT, UPDATE, and DELETE statements for you.

The Create new stored procedures option specifies a SQL statement and the wizard will create a new stored procedure. If you provide a single-table SELECT statement, the wizard can generate INSERT, UPDATE, and DELETE stored procedures for you. The Use existing stored procedures option chooses an existing stored procedure for each command, including SELECT, INSERT, UPDATE, and DELETE. You want to specify a SQL statement to access the database, so you retain the default Use SQL statements radio button and click Next. Then you specify a SQL statement that, when executed, returns data from the database. This data is used by the TableAdapter to fill the DataTable. To specify a statement, you can either type the statement or use the Query Builder. The Query Builder helps you select the data you want to load in the DataTable.

Graphic
The Enter a SQL Statement page contains a text field for typing the SQL statement and also provides the Query Builder button.

Try It
You decide to use Query Builder to construct a SQL statement that accesses the Products table. To complete the task
1. Click Query Builder 2. Select Products 3. Click Add and then click Close

The Products table is selected. You can construct a SQL statement for retrieving data from specific columns, or data from all columns in the table. In this example, you retrieve all columns by selecting the * (All Columns) checkbox and clicking OK.

Graphic
The SQL statement for fetching all the columns in the Products table is SELECT Products.* FROM Products You then click Next to move to the next page.

Note
Before moving to the next page of the wizard, you can use the Advanced Options button to access and modify default options relating to generation of Insert, Update and Delete options, use of optimistic currency, and the use of a Select statement to refresh table data when an Insert or Update statement has been executed. On the Choose Methods to Generate page, you can add a TableAdapter method to access and update data. You can fill a DataTable, return a filled DataTable, or create methods to make changes directly to the database. By default, all methods are selected.

Graphic
The Choose Methods to Generate page provides three options Fill a DataTable, Return a DataTable, and Create methods to send updates directly to the database (GenerateDBDirectMethods). The Fill a DataTable option creates a method that takes a DataTable or DataSet as a parameter and executes the SQL statement or SELECT stored procedure entered on the previous page. The Method name field for this option contains Fill. The Return a DataTable option creates a method that returns a new DataTable filled with the results of the SQL statement or SELECT stored procedure entered on the previous page. The Method name field for this option contains GetData. The Create methods to send updates directly to the database (GenerateDBDirectMethods) option creates Insert, Update, and Delete methods that can be called to send individual row changes directly to the database. Suppose you want to return a new DataTable filled with the results of the SQL statement you created. To do this, you clear the Fill a DataTable and Create methods to send updates directly to the database (GenerateDBDirectMethods) checkboxes and click Next. The Wizard Results page displays the list of tasks the wizard has performed. Finally, you click Finish to add the DataTable and the TableAdapter to your DataSet and close the wizard.

Graphic
The Wizard Results page displays these details: The 'Products' DataTable and 'ProductsTableAdapter' were configured successfully. - Generated SELECT statement.

- Generated INSERT statement. - Generated UPDATE statement. - Generated DELETE statement. - Generated table mappings. - Generated Get method. - Generated update methods. The ProductsTableAdapter is added to the Design surface of DataSet Designer and contains data from all columns of the NORTHWIND database.

Graphic
The Products table contains these rows ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, and Discontinued.

SkillCheck
You want to create a TableAdapter to fill an existing DataSet with all details of the Customers DataTable. Configure the CustomersTableAdapter using a SQL statement and the Fill method and add it to the NORTHWINDDataSet. The Add Table dialog box of the Query Builder is open in the TableAdapter Configuration Wizard. Task:
1. 2. 3. 4. 5. Select the DataTable to construct a SQL statement and include it to the Query Builder. Select the columns in the Customers table that you want to retrieve. Open the Choose Methods to Generate page of the wizard. Clear the methods that you don't want to add to the TableAdapter. Implement the changes and close the wizard.

Answer
To complete the task Step 1: Select Customers and click Add Step 2: Click Close and then select the * (All Columns) checkbox Step 3: Click OK and then click Next Step 4: Clear the Return a DataTable and Create methods to send updates directly to the database (GenerateDBDirectMethods) checkboxes Step 5: Click Next and then click Finish

3. Updating a TableAdapter
After you create a TableAdapter, you can update it by adding queries. These queries appear as typed methods that take the correct parameter type as an argument. Suppose you add a query called FillByProducts that accepts the Category parameter. When executed, this query fills the Products table with all products within the specified category. In addition to using the Fill and FillBy queries to fill a DataTable, you can create GetData and GetDataBy queries. These queries return new DataTables containing the result of SQL statements or stored procedures. You can add queries to an existing TableAdapter in two ways:
the TableAdapter Query Configuration Wizard You can access the TableAdapter Query Configuration Wizard from DataSet Designer. You can right-click the desired TableAdapter, and then click Add Query to open the wizard. You can also open this wizard by dragging Query from the DataSet tab of the Toolbox onto the Designer. the Search Criteria Builder dialog box You can use the Search Criteria Builder dialog box to add a new query to an existing TableAdapter. To do this, you first select a TableAdapter and then select its smart tag. Then click Add Query to access the Search Criteria Builder dialog box. Currently, the Search Criteria Builder dialog box contains NORTHWIND_MDFDataSet.Customers in the Select data source table drop-down list. The Select a parameterized query to load data section contains two radio buttons New query name and Existing query name. Currently, the New query name radio button is selected and contains FillBy in the corresponding field. The Query Text field currently contains the SELECT statement for retrieving customer details from the Customers column.

Suppose you want to add a FillBy parameterized query to the Order DetailsTableAdapter. To do this, you first open a DataSet in DataSet Designer. Then you open the TableAdapter Query Configuration Wizard by right-clicking the Order DetailsTableAdapter and selecting Add Query.

Graphic

The shortcut menu contains options including, Add Query, Configure, Delete, Rename, Preview Data, View Code, and Properties. Then you can specify the type of command used to access the database. You can use a SQL statement or an existing stored procedure. You can also create a new stored procedure that uses a SELECT statement to select records.

Try It
You decide to create a new stored procedure to access the database. To complete the task
1. Select the Create new stored procedure radio button and click Next

The Choose a Query Type page opens. Now you select the type of SQL query you want to execute. You can choose the SELECT query to return rows, columns, or a single value. You can also use the UPDATE, DELETE, or INSERT queries to update the table. By default, the SELECT which returns rows radio button is selected. You decide to retain the default selection and click Next.

Graphic
The Choose a Query Type page contains these radio buttons SELECT which returns rows, SELECT which returns a single value, UPDATE, DELETE, and INSERT. The Select which returns rows option returns one or many rows or columns. The Select which returns a single value option returns a single value (for example, Sum, Count, or any other aggregate function). The UPDATE option changes existing data in a table. The DELETE option removes rows from a table. The INSERT option adds a new row to a table. The Generate the stored procedure page allows you to specify the SELECT statement that determines what data is loaded into the table. You can create a parametrized query by specifying the WHERE clause. For example, you can specify a query where ProductID=@ProductID. To accept the current statement, you click Next.

Graphic

Currently, the SELECT statement returns the OrderID, ProductID, UnitPrice, Quantity, and Discount columns from the Order Details table where ProductID is @ProductID. After generating the stored procedure, you specify its name. The default name for a stored procedure containing SELECT statements is SelectQuery. To retain the name, you click Next.

Graphic
You can use the Preview SQL Script button to preview the SQL script used to generate the stored procedure and optionally copy it for your own procedure. Then, you select the methods you want to add to the TableAdapter. You can use either the Fill method or GetData method to fill a DataTable or return a DataTable.

Graphic
The Method name field of the Fill a DataTable option contains FillBy and the Method name of the Return a DataTable option contains GetDataBy.

Try It
You want to fill the Order Details table based on a specified ProductID. To complete the task
1. Type ProductID in the Method name field of the Fill a DataTable checkbox and clear the Return a DataTable checkbox 2. Click Next

The Wizard Results page opens. The Wizard Results page displays the list of tasks the wizard has performed. Finally, you click Finish to complete the wizard.

Graphic
The TableAdapter query 'SelectQueryTableAdapter' was configured successfully. The Wizard Results page contains these details: - Generated SELECT statement. - Generated Fill method. - Generated update methods.

The TableAdapter query SelectQueryTableAdapter is created and the FillByProductID(@ProductID) method is added to the TableAdapter. You can edit the main Fill method or additional queries associated with a TableAdapter. Changing a TableAdapters Fill method automatically changes the schema of the associated DataTable. To edit a TableAdapter, you select that TableAdapter in DataSet Designer. You then right-click the TableAdapter name and select Configure. This opens the TableAdapter Configuration Wizard. You can use this wizard to modify the query. You can remove or insert columns from the query or the Fill method.

SkillCheck
You have created a typed DataSet to return the Shippers data from the Northwind database. Add a parametrized SelectQueryTableAdapter query to the ShippersTableAdapter using a WHERE clause that returns ShipperID as @ShipperID. Use the method GetDataByShipperID to return the DataTable with rows mapping to a specified company name. The TableAdapter Query Configuration Wizard is open and a new stored procedure is selected as the command type. Task:
1. 2. 3. 4. Choose a SQL query that returns rows. Add a WHERE clause that returns ShipperID as @ShipperID. Retain the default name of the new stored procedure. Create a method that returns a new DataTable filled with the results of the SQL statement and is based on the ProductName column and complete the wizard.

Answer
To complete the task Step 1: Ensure the SELECT which returns rows radio button is selected and click Next Step 2: Type WHERE ShipperID=@ShipperID in the What data should the table load field and click Next Step 3: Ensure the stored procedure is named SelectQuery and click Next Step 4: Type ShipperID in the Method name field of the Return a DataTable checkbox, click Next, and then click Finish

Summary
A typed DataSet is a generated class that is inherited directly from the DataSet class and uses a specified XSD schema to define its structure. So it uses strongly typed properties, methods, and events. You can create a typed DataSet using the XML Schema Definition tool or Visual Studio. To create a DataSet in Visual Studio, you can either use the Data Source Configuration Wizard or DataSet Designer. A typed DataSet typically uses a TableAdapter to connect it with the data source. A TableAdapter executes queries and either returns a new DataTable or fills an existing DataTable. You can create a TableAdapter by running the TableAdapter Configuration Wizard or the Data Source Configuration Wizard.

Table of Contents
| Print | Contents | Close |

Você também pode gostar