1. Trang chủ
  2. » Công Nghệ Thông Tin

Beginning C# 2005 Databases From Novice to Professional phần 4 potx

52 277 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Introducing Commands in C#
Trường học Unknown
Chuyên ngành Databases
Thể loại Document
Năm xuất bản 2006
Thành phố Unknown
Định dạng
Số trang 52
Dung lượng 765,37 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Summary In this chapter, we covered quite a few things: • What an ADO.NET command is and does • How to create a command • How to associate a command with a connection • How to set comman

Trang 1

// define delete statement

Figure 6-6.Executing statements

Trang 2

string sqldel = @"

delete from employeeswhere

firstname = 'Zachariah'and

lastname = 'Zinn'

";

Then you create two commands The first is cmdqry, which encapsulates the scalarquery to count the rows in the Employeestable You use this command several times tomonitor the number of rows as you insert and delete employees The second is cmdnon,which you use twice, first to insert a row, then to delete the same row You initially set its CommandTextto the INSERTstatement SQL

SqlCommand cmdnon = new SqlCommand(sqlins, conn);

and later reset it to the DELETEstatement SQL

Console.WriteLine("After INSERT: Number of Employees is: {0}",

Trang 3

Command Parameters

When you insert the new row into Employees, you hard-code the values Although this

is perfectly valid SQL, it’s something you almost never want (or need) to do You need

to be able to store whatever values are appropriate at any given time There are two

approaches to doing this Both are reasonable, but one is far more efficient than the

other

The less efficient alternative is to dynamically build a SQL statement, producing

a string that contains all the necessary information in the CommandTextproperty For

example, you could do something like this:

string fname = "Zachariah";

string lname = "Zinn";

string vals = "('" + fname + "'," + "'" + lname +"')" ;

string sqlins = @"

insert into employees(

firstname,lastname)

Note Of course, we’re using fnameand lnamesimply as rudimentary sources of data Data most likely

comes from some dynamic input source and involves many rows over time, but the technique is nonetheless

the same: building a SQL string from a combination of hard-coded SQL keywords and values contained in

variables

A much better way to handle this is with command parameters A command

parame-ter is a placeholder in the command text where a value will be substituted In SQL Server,

named parameters are used They begin with @followed by the parameter name with no

intervening space So, in the following INSERTstatement, @MyNameand @MyNumberare both

parameters:

INSERT INTO MyTable VALUES (@MyName, @MyNumber)

Trang 4

Note Some data providers use the standard SQL parameter marker, a question mark (?), instead ofnamed parameters.

Command parameters have several advantages:

• The mapping between the variables and where they’re used in SQL is clearer

• Parameters let you use the type definitions that are specific to a particularADO.NET data provider to ensure that your variables are mapped to the correctSQL data types

• Parameters let you use the Preparemethod, which can make your code runfaster because SQL Server parses the SQL in a “prepared” command only thefirst time it’s executed Subsequent executions run the same SQL, changing only parameter values

• Parameters are used extensively in other programming techniques, such asstored procedures (see Chapter 13) and working with irregular data (see Chapter 18)

Try It Out: Using Command Parameters

Follow these steps:

1. Add a new C# Console Application project named CommandParametersto yourChapter06solution Rename Program.csto CommandParameters.cs

2. Replace the code in CommandParameters.cswith the code in Listing 6-5 This is avariation of Listing 6-4, with salient changes highlighted

static void Main()

Trang 5

lastname = @lname

";

Trang 6

// create commandsSqlCommand cmdqry = new SqlCommand(sqlqry, conn);SqlCommand cmdnon = new SqlCommand(sqlins, conn);

// add parameters to the command for statements cmdnon.Parameters.Add("@fname", SqlDbType.NVarChar, 10); cmdnon.Parameters.Add("@fname", SqlDbType.NVarChar, 20);

try{// open connectionconn.Open();

// execute query to get number of employeesConsole.WriteLine(

"Before INSERT: Number of employees {0}\n"

, cmdqry.ExecuteScalar());

// execute nonquery to insert an employee

cmdnon.ExecuteNonQuery();

Console.WriteLine(

"After INSERT: Number of employees {0}\n"

, cmdqry.ExecuteScalar());

// execute nonquery to delete an employeecmdnon.CommandText = sqldel;

Console.WriteLine(

"Executing statement {0}"

, cmdnon.CommandText);

Trang 7

Console.WriteLine(

"After DELETE: Number of employees {0}\n"

, cmdqry.ExecuteScalar());

}catch (SqlException ex){

Console.WriteLine(ex.ToString());

}finally{conn.Close();

Console.WriteLine("Connection Closed.");

}}}}

3. Make it the startup project, and then run it with Ctrl+F5 You should see the resultshown in Figure 6-7

How It Works

First, you set up the sample data:

// set up rudimentary data

Figure 6-7.Using command parameters

Trang 8

string fname = "Zachariah";

string lname = "Zinn";

Then you add two parameters, @fnameand @lname, to the Parameterscollectionproperty of the command you want to parameterize:

// create commandsSqlCommand cmdqry = new SqlCommand(sqlqry, conn);

SqlCommand cmdnon = new SqlCommand(sqlins, conn);

// add parameters to the command for statements cmdnon.Parameters.Add("@fname", SqlDbType.NVarChar, 10);

cmdnon.Parameters.Add("@fname", SqlDbType.NVarChar, 20);

Note that you provide the parameter names as strings, then specify the data types

of the columns you expect to use them with The SqlDbTypeenumeration contains amember for every SQL Server data type except cursorand table, which C# programscan’t directly use The Addmethod is overloaded Since nvarcharrequires you to specifyits maximum length, you include that as the third argument

Finally, you set the parameter values before executing the command:

// execute nonquery to insert an employee

cmdnon.Parameters["@fname"].Value = fname;

cmdnon.Parameters["@lname"].Value = lname;

Note The same command,cmdnon, is used to execute both the INSERTand DELETEstatements.The parameter values don’t change, even though the SQL in CommandTextdoes The Parameterscollection is the source of parameter values for whatever SQL is in CommandText The SQL doesn’t have touse all or even any of the parameters, but it cannot use any parameters not in the command’s

Parameterscollection

Notice in Figure 6-7 that when you display the SQL in CommandText, you see theparameter names rather than their values Values are substituted for parameters whenthe SQL is submitted to the database server, not when the values are assigned to themembers of the Parameterscollection

The Prepare Method

When you expect to execute a parameterized command multiple times, you shouldprepare it with the Preparemethod The syntax is simple:

Trang 9

changing only parameter values You never have to prepare any commands, but it’s

always the best practice to do this if you expect to execute a command multiple times

Note If you change its CommandTextafter you prepare a command, you must prepare the command

again to gain the advantage of prepared SQL For a command to stay prepared, only parameter values can

change between command executions

You can use Prepare()even if you only execute a command once, but it’s a waste ofyour time and the computer’s For example, you could change CommandParameters.csby

adding the following line in bold:

// execute nonquery to insert an employeecmdnon.Parameters["@fname"].Value = fname;

cmdnon.Parameters["@lname"].Value = lname;

Console.WriteLine(

"Executing statement {0}"

, cmdnon.CommandText);

It would still run as expected, but now you’ve added an unnecessary call to Prepare().Further, the prepared command is discarded when you change the CommandTextbefore

performing the DELETE

cmdnon.CommandText = sqldel;

Trang 10

because the new SQL statement is different (though it still uses the same parameters andthey stay in effect).

Tip If you prepare commands, use them for only one SQL query or statement Create as many commandobjects as you need to prepare

Summary

In this chapter, we covered quite a few things:

• What an ADO.NET command is and does

• How to create a command

• How to associate a command with a connection

• How to set command text

• How to use ExecuteScalar()for queries that return single values

• How to use ExecuteReader()to process result sets

• How to use ExecuteNonQuery()for statements

• What command parameters are and how to use them

• How to use the Preparemethod

In the next chapter, we’ll look at data readers

Trang 11

Introducing Data Readers

In Chapter 4, you used data readers to retrieve data from a multirow result set In this

chapter, we’ll look at data readers in more detail You’ll see how they’re used and their

importance in ADO.NET programming

In particular, you’ll see how to use data readers to do the following:

• Retrieve query results

• Get information with ordinal and column name indexers

• Get result set information

• Get schema information

• Process multiple result sets

Understanding Data Readers in General

The third component of a data provider, in addition to connections and commands, is

the data reader Once you’ve connected to a database and queried it, you need some

way to access the result set This is where the data reader comes in

Note If you’re from an ADO background, an ADO.NET data reader is like an ADO forward-only/read-only

client-side recordset, but it’s not a COM object

Data readers are objects that implement the System.Data.IDataReaderinterface

A data reader is a fast, unbuffered, forward-only, read-only connected stream that

retrieves data on a per-row basis It reads one row at a time as it loops through

a result set

141

C H A P T E R 7

■ ■ ■

Trang 12

You can’t instantiate a data reader directly; instead, you create one with theExecuteReadermethod of a command For example, assuming cmdis a SqlClient

command object for a query, here’s how to create a SqlClientdata reader:

SqlDataReader rdr = cmd.ExecuteReader();

You can now use this data reader to access the query’s result set

Tip One point that we’ll discuss further in the next chapter is choosing a data reader vs a dataset Thegeneral rule is to always use a data reader for simply retrieving data If all you need to do is display data, allyou need to use in most cases is a data reader

We’ll demonstrate basic data reader usage with a few examples The first example

is the most basic; it simply uses a data reader to loop through a result set

Let’s say you’ve successfully established a connection with the database, a query has been executed, and everything seems to be going fine—what now? The next sensiblething to do would be to retrieve the rows and process them

Try It Out: Looping Through a Result Set

The following steps show how to use a SqlDataReaderto loop through a result set andretrieve rows:

1. Create a new Console Application project named Chapter07 When SolutionExplorer opens, save the solution

2. Rename the Chapter07project to DataLooper Rename the Program.csfile toDataLooper.cs, and replace the generated code with the code in Listing 7-1

Listing 7-1.DataLooper.cs

using System;

using System.Data;

using System.Data.SqlClient;

Trang 13

namespace Chapter07

{

class DataLooper{

static void Main(string[] args){

// connection stringstring connString = @"

selectcontactnamefrom

customers

";

// create connectionSqlConnection conn = new SqlConnection(connString);

try{// open connectionconn.Open();

// create commandSqlCommand cmd = new SqlCommand(sql, conn);

// create data readerSqlDataReader rdr = cmd.ExecuteReader();

// loop through result setwhile (rdr.Read())

{// print one row at a timeConsole.WriteLine("{0}", rdr[0]);

}

Trang 14

// close data readerrdr.Close();

}catch(Exception e){

Console.WriteLine("Error Occurred: " + e);

}finally{//close connectionconn.Close();

}}}}

3. Run it with Ctrl+F5 You should see the result shown in Figure 7-1 (Only the last

20 rows are displayed in the figure.)

How It Works

SqlDataReaderis an abstract class that you can’t instantiate explicitly For this reason, you obtain an instance of a SqlDataReaderby executing the ExecuteReadermethod ofSqlCommand:

// create data readerSqlDataReader rdr = cmd.ExecuteReader();

Figure 7-1.Looping through a result set

Trang 15

ExecuteReader()doesn’t just create a data reader, it sends the SQL to the tion for execution When it returns, you can loop through each row of the result set

connec-and retrieve values column by column To do this, you call the Readmethod of

SqlDataReader, which returns trueif a row is available and advances the cursor (the

internal pointer to the next row in the result set) or returns falseif another row isn’t

available Since Read()advances the cursor to the next available row, you have to call

it for all the rows in the result set, so you call it as the condition in a whileloop:

// loop through result setwhile (rdr.Read())

{// print one row at a timeConsole.WriteLine("{0}", rdr[0]);

}

Once you call the Readmethod, the next row is returned as a collection and stored

in the SqlDataReaderobject itself To access data from a specific column, you can use

a number of methods (we’ll cover these in the next section), but for this application, you

use the ordinal indexer lookup method, giving the column number to the reader to

retrieve values (just as you’d specify an index for an array) Since in this case you choose

a single column from the Customerstable while querying the database, only the “zeroth”

indexer is accessible, so you hard-code the index as rdr[0]

To use the connection for another purpose or to run another query on the

data-base, it’s important to call the Closemethod of SqlDataReaderto close the reader

explicitly Once a reader is attached to an active connection, the connection remains

busy fetching data for the reader and remains unavailable for other use until the reader

has been detached from it That’s why you close the reader in the tryblock rather than

in the finallyblock (even though this simple program doesn’t need to use the

connec-tion for another purpose):

// close data readerrdr.Close();

Using Ordinal Indexers

You use an ordinal indexer to retrieve column data from the result set Let’s learn more

about ordinal indexers The code

rdr[0]

is a reference to the data reader’s Itemproperty, and returns the value in the column

specified for the current row The value is returned as an object

Trang 16

Try It Out: Using Ordinal Indexers

Let’s build a console application that uses an ordinal indexer:

1. Add a new C# Console Application project named OrdinalIndexerto yourChapter07solution Rename Program.csto OrdinalIndexer.cs

2. Replace the code in OrdinalIndexer.cswith the code in Listing 7-2

static void Main(string[] args){

// connection stringstring connString = @"

selectcompanyname,contactnamefrom

customerswherecontactname like 'M%'

";

// create connectionSqlConnection conn = new SqlConnection(connString);

Trang 17

try{// Open connectionconn.Open();

// create commandSqlCommand cmd = new SqlCommand(sql, conn);

// create data readerSqlDataReader rdr = cmd.ExecuteReader();

// print headingsConsole.WriteLine("\t{0} {1}",

}

// close readerrdr.Close();

}catch(Exception e){

Console.WriteLine("Error Occurred: " + e);

}finally{// close connectionconn.Close();

}}}}

Trang 18

3. Make this the startup project, and run it with Ctrl+F5 You should see the resultshown in Figure 7-2.

How It Works

You query the Customerstable for the columns CompanyNameand ContactName, where

contact names begin with the letter M:

// querystring sql = @"

selectcompanyname,contactnamefrom

customerswherecontactname like 'M%'

";

Since your query selects two columns, the returned data also comprises a collection

of rows from only these two columns, thus allowing access to only two possible ordinalindexers, 0 and 1

You read each row in a whileloop, fetching values of the two columns with theirindexers Since the returned value is an object, you need to explicitly convert the value

to a string so that you can use the PadLeftmethod to format the output:

Figure 7-2.Displaying multiple columns

Trang 19

// loop through result setwhile (rdr.Read())

{Console.WriteLine(" {0} | {1}",rdr[0].ToString().PadLeft(25),rdr[1].ToString().PadLeft(20));

}

After processing all rows in the result set, you explicitly close the reader to free theconnection:

// close readerrdr.Close();

Using Column Name Indexers

Most of the time we don’t really keep track of column numbers and instead prefer

retrieving values by their respective column names, simply because it’s much easier to

remember them by their names, which also makes the code more self-documenting

You use column name indexing by specifying column names instead of ordinalindex numbers This has its advantages For example, a table may be changed by the

addition or deletion of one or more columns, upsetting column ordering and raising

exceptions in older code that uses ordinal indexers Using column name indexers

avoids this issue, but ordinal indexers are faster, since they directly reference columns

rather than look them up by name

The following code snippet retrieves the same columns (CompanyNameandContactName) that the last example did, using column name indexers:

// loop through result setwhile (rdr.Read())

{Console.WriteLine(" {0} | {1}",rdr["companyname"].ToString().PadLeft(25),rdr["contactname"].ToString().PadLeft(20));

Trang 20

Using Typed Accessor Methods

When a data reader returns a value from a data source, the resulting value is retrievedand stored locally in a NET type rather than in the original data source type This in-place type-conversion feature is a trade-off between consistency and speed, so to givesome control over the data being retrieved, the data reader exposes typed accessormethods that you can use if you know the specific type of the value being returned.Typed accessor methods all begin with Get, take an ordinal index for data retrieval,and are type safe; C# won’t allow you to get away with unsafe casts These methods turnout to be faster than both the ordinal and the column name indexer methods Beingfaster than column name indexing seems only logical, as the typed accessor methodstake ordinals for referencing; however, we need to explain how it’s faster than ordinalindexing This is because even though both techniques take in a column number, theconventional ordinal indexing method needs to look up the data source data type of theresult and then go through a type conversion This overhead of looking up the schema isavoided with typed accessors .NET types and typed accessor methods are available foralmost all data types supported by SQL Server and OLE DB databases

Table 7-1 should give you a brief idea of when to use typed accessors and with whatdata type It lists SQL Server data types, their corresponding NET types, NET typedaccessors, and special SQL Server–specific typed accessors designed particularly forreturning objects of type System.Data.SqlTypes

Table 7-1.SQL Server Typed Accessors

SQL Server Data Type NET Type NET Typed Accessor

char String or Char[] GetString or GetChars

image or long varbinary Byte[] GetBytes

nchar String or Char[] GetString or GetChars

ntext String or Char[] GetString or GetChars

nvarchar String or Char[] GetString or GetChars

Trang 21

SQL Server Data Type NET Type NET Typed Accessor

smalldatetime DateTime GetDateTime

long varchar String or Char[] GetString or GetChars

varchar String or Char[] GetString or GetChars

Table 7-2 shows some available OLE DB data types, their corresponding NET types,and their NET typed accessors

Table 7-2.OLE DB Typed Accessors

DBTYPE_DBDATE DateTime GetDateTime

DBTYPE_DBTIME DateTime GetDateTime

DBTYPE_DBTIMESTAMP DateTime GetDateTime

DBTYPE_DECIMAL Decimal GetDecimal

DBTYPE_ERROR ExternalException GetValue

DBTYPE_FILETIME DateTime GetDateTime

Continued

Trang 22

Table 7-2.Continued

DBTYPE_LONGVARCHAR String GetString

DBTYPE_NUMERIC Decimal GetDecimal

To see typed accessors in action, let’s build a console application that uses them For this example, you’ll use the Productstable from the Northwind database

Table 7-3 shows the data design of the table Note that you can look up the datatypes given in the table for their corresponding typed accessor methods in Table 7-1,

so you can use them correctly in your application

Table 7-3.Northwind ProductsTable Data Types

Column Name Data Type Length Allow Nulls?

Trang 23

Try It Out: Using Typed Accessor Methods

Let’s build a console application that uses typed accessors:

1. Add a new C# Console Application project named TypedAccessorsto yourChapter07solution Rename Program.csto TypedAccessors.cs

2. Replace the code in TypedAccessors.cswith the code in Listing 7-3

static void Main(string[] args){

// connection stringstring connString = @"

selectproductname,unitprice,unitsinstock,discontinuedfrom

products

";

// create connectionSqlConnection conn = new SqlConnection(connString);

Trang 24

try{// open connectionconn.Open();

// create commandSqlCommand cmd = new SqlCommand(sql, conn);

// create data readerSqlDataReader rdr = cmd.ExecuteReader();

// fetch datawhile (rdr.Read()){

Console.WriteLine(

"{0}\t {1}\t\t {2}\t {3}",// nvarchar

rdr.GetString(0).PadRight(30),// money

rdr.GetDecimal(1),// smallintrdr.GetInt16(2),// bit

Console.WriteLine("Error Occurred: " + e);}

finally{// close connectionconn.Close();

}}}}

Trang 25

3. Make this the startup project, and run it with Ctrl+F5 You should see the resultshown in Figure 7-3 (Only the first 20 rows are displayed in the figure.)

How It Works

You query the Productstable for ProductName,UnitPrice,UnitsInStock, and Discontinued:

// querystring sql = @"

selectproductname,unitprice,unitsinstock,discontinuedfrom

products

";

We chose these columns to deal with different kinds of data types and to show how

to use relevant typed accessors to obtain the correct results:

// fetch datawhile (rdr.Read()){

Console.WriteLine(

"{0}\t {1}\t\t {2}\t {3}",// nvarchar

rdr.GetString(0).PadRight(30),

Figure 7-3.Using typed accessors

Trang 26

// moneyrdr.GetDecimal(1),// smallintrdr.GetInt16(2),// bit

rdr.GetBoolean(3));

}

Looking at Table 7-1, you can see that you can access the nvarchar,money,smallint,and bitdata types in SQL Server with the GetString,GetDecimal,GetInt16,and GetBooleanaccessor methods, respectively

This technique is fast and completely type safe By this, we mean that if implicitconversions from native data types to NET types fail, an exception is thrown for invalidcasts For instance, if you try using the GetStringmethod on a bitdata type instead ofusing the GetBooleanmethod, a “Specified cast is not valid” exception will be thrown

Getting Data About Data

So far, all you’ve done is retrieve data from a data source Once you have a populated datareader in your hands, you can do a lot more Here are a number of useful methods forretrieving schema information or retrieving information directly related to a result set.Table 7-4 describes some of the metadata methods and properties of a data reader

Table 7-4.Data Reader Metadata Properties and Methods

Method or Property Name Description

Depth A property that gets the depth of nesting for the current row FieldCount A property that holds the number of columns in the current row GetDataTypeName A method that accepts an index and returns a string containing the

name of the column data type GetFieldType A method that accepts an index and returns the NET Framework

type of the object GetName A method that accepts an index and returns the name of the

specified column GetOrdinal A method that accepts a column name and returns the column index GetSchemaTable A method that returns column metadata

HasRows A property that indicates if the data reader has any rows

RecordsAffected A property that gets the number of rows changed, inserted, or

deleted

Ngày đăng: 09/08/2014, 14:20