Chapter 6
Working with Data in a Connected environment
What Are Command Objects?
To execute SQL statements and stored procedures against a database (from your application), you use Command objects. Command objects contain the necessary information to execute SQL statements, stored procedures, functions, and so on against a data source; return data to your application; and perform database catalog operations such as creating, altering, and deleting database objects. In other words, you can use Command objects to execute any valid SQL statement.
Just like Connection objects, there are Command objects for each of the .NET Framework Data Providers. Select the specific Command object that coincides with the .NET Framework Data Provider being used to communicate with your data source.
The primary properties of a Command object are the CommandText, CommandType, and Connection properties.
Common Command Object Methods
|
Cancel
|
Tries to cancel the execution of the command.
|
|
ExecuteNonQuery
|
Executes SQL statements or stored procedures that do not return data.
|
|
ExecuteReader
|
Executes commands that return tabular (or rows) of data.
|
|
ExecuteScalar
|
Executes SQL statements or stored procedures that return a single value. If you call ExecuteScalar with a statement that returns rows of data, the query executes but returns only the first column of the first row returned by the query. Additional columns or rows are ignored.
|
|
ExecuteXMLReader
|
Returns XML formatted data. Returns a System.Xml.XmlReader object.
|
Creating and Configuring Command Objects
Create Command objects by declaring an instance of the desired Command object and setting the CommandType and CommandText properties. To execute the command, you must also set the command’s Connection property to a valid Connection object.
Creating a Command Object That Executes a SQL Statement
To execute commands that run SQL statements against a database, set the CommandType property to Text and set the CommandText property to the SQL statement you want to execute.
SqlCommand CustomersCommand = new SqlCommand();
CustomersCommand.Connection = NorthwindConnection;
CustomersCommand.CommandType = CommandType.Text;
CustomersCommand.CommandText = “SELECT CustomerID, CompanyName FROM Customers”;
Creating a Command Object That Executes a Stored Procedure
To execute commands that run existing stored procedures in a database, set the CommandType property to StoredProcedure and set the CommandText property to the name of the stored procedure you want to execute.
SqlCommand TopTenCommand = new SqlCommand();
TopTenCommand.Connection = NorthwindConnection;
TopTenCommand.CommandType = CommandType.StoredProcedure;
TopTenCommand.CommandText = “Ten Most Expensive Products”;
Creating a Command Object That Performs Catalog Operations
To execute commands that run DML actions (Data Manipulation Language), create commands that run SQL statements and call the ExecuteNonQuery method of the command.
SqlCommand CreateTableCommand = new SqlCommand();
CreateTableCommand.Connection = NorthwindConnection;
CreateTableCommand.CommandType = CommandType.Text;
CreateTableCommand.CommandText = “CREATE TABLE SalesPersons (” +
“[SalesPersonID] [int] IDENTITY(1,1) NOT NULL, ” +
“[FirstName] [nvarchar](50) NULL, ” +
“[LastName] [nvarchar](50) NULL)”;
// ExecuteNonQuery to be done later
Creating a Command Object That Returns a Single Value
To execute commands that return single values (scalar values), create commands that run SQL statements or stored procedures that return a single value (as opposed to commands that return rows or tabular data). Set the CommandText property to a SQL statement or stored procedure that returns a single value and call the ExecuteScalar method of the command. Declare a variable with the data type of the single value being returned from the database and cast the results of the ExecuteScalar call to the expected data type. (The ExecuteScalar method returns an Object, so you must cast the ExecuteScalar method to the equivalent of the returned data type.)
// C#
SqlCommand ExecuteScalarCommand = new SqlCommand();
ExecuteScalarCommand.Connection = NorthwindConnection;
ExecuteScalarCommand.CommandType = CommandType.Text;
ExecuteScalarCommand.CommandText = “SELECT Count(*) FROM Customers”;
// Open the connection and execute the command
ExecuteScalarCommand.Connection.Open();
int CustomerCount = (int)ExecuteScalarCommand.ExecuteScalar();
MessageBox.Show(“There are ” + CustomerCount.ToString() + ” customers”); ExecuteScalarCommand.Connection.Close();
Executing Commands Asynchronously
Executing commands asynchronously is the process of having the command execute on a separate thread from the rest of your application so users do not have to wait for the command to complete before continuing work in other parts of the application.
|
BeginExecuteNonQuery
|
Starts the asynchronousl version of ExecuteNonQuery
|
|
BeginExecuteReader
|
Starts the asynchronous version of the ExecuteReader method.
|
|
BeginExecuteXmlReader
|
Starts the asynchronous version of the ExecuteXmlReader method.
|
|
EndExecuteNonQuery
|
Call this method after the StatementComplete event fires to complete execution of the command.
|
|
EndExecuteReader
|
Call this method after the StatementComplete event fires to return the DataReader with the data returned by the command.
|
|
EndExecuteXMLReader
|
Call this method after the StatementComplete event fires to return the XmlReader with the data returned by the command.
|
When executing commands asynchronously, you explicitly call the Begin and End methods of the selected Command object. Calling the Begin method sends the command (SQL statement or stored procedure call) to the database, and then you can perform other operations in your application. When the command finishes executing, the StatementCompleted event fires, notifying the application that it can call the End method of the command and access the data for further processing.
// C#
System.Text.StringBuilder results = new System.Text.StringBuilder();
SqlConnection NorthWindConnection = new SqlConnection(“Data Source=.\\;Initial Catalog=Northwind;” +
“Integrated Security=True; asynchronous processing = true“);
SqlCommand command1 = new SqlCommand(“WAITFOR DELAY ’00:00:05′; ” +
Select * From [Order Details]“, NorthWindConnection);
NorthWindConnection.Open();
IAsyncResult r = command1.BeginExecuteReader();
MessageBox.Show(“The command has been executed but processing is free ” + “to display this message before the results have been returned!”);
SqlDataReader reader = command1.EndExecuteReader(r);
while (reader.Read())
{
for (int i = 0; i< reader.FieldCount – 1; i++)
{
results.Append(reader[i].ToString() + “\t”);
}
results.Append(Environment.NewLine);
}
reader.Close();
command1.Connection.Close();
MessageBox.Show(results.ToString());
Executing Multiple SQL Statements Using a DataReader
In addition to returning the results from a single SQL statement, you can use a Command object and DataReader to return the results of multiple SQL statements. To execute more than one SQL statement, set the CommandText property of a Command object to multiple SQL statements separated by semicolons (;). After calling the ExecuteReader method, the DataReader will hold the number of result sets equal to the number of SQL statements executed. To access the data returned by the additional statements, call the NextResult method of the DataReader.
ExecuteSqlCommand.CommandText = “SELECT CustomerID, CompanyName FROM Customers; SELECT ProductName, UnitsInStock FROM Products”;
SqlDataReader reader = ExecuteSqlCommand.ExecuteReader();
bool MoreResults = false;
do
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
results.Append(reader[i].ToString() + “\t”);
}
results.Append(Environment.NewLine);
}
MoreResults = reader.NextResult();
} while (MoreResults);
Working with Parameters in SQL Commands
A parameter can be thought of as a type of variable that you can use to pass and return values between your application and a database. Just like a variable in your application, parameters are created to contain a certain data type. Parameter data types are assigned using the types defined in the System.Data.SqlDbType enumeration. The SqlDbType enumeration contains a list of the types available in SQL Server as opposed to application variables that are typically assigned one of the .NET Framework base data types.
Types of Parameters
When executing Command objects, you typically use parameters to send data to the database. This type of parameter is referred to as an Input parameter. In addition to Input parameters, you might also want to use a parameter to retrieve information coming out of the database; this type of parameter is called an Output parameter. There is also a third type of parameter, which is referred to as an InputOutput parameter. InputOutput parameters are used to both send and receive data when executing a command. The type of parameter is designated in the Direction property of the parameter and is assigned a value from the ParameterDirection enumeration. In other words, when creating a parameter, you can set its Direction property to Input, Output, InputOutput, or ReturnValue.
Parameters are Input type by default.
Creating Parameters
Create parameters by declaring an instance of the Parameter class and setting its name and data type to coincide with the parameter name and data type expected by the data source. You can also set the parameter’s ParameterDirection property to choose the type of parameter to create.
SqlParameter TotalCostParameter = new SqlParameter();
TotalCostParameter.ParameterName = “@TotalCost”;
TotalCostParameter.SqlDbType = SqlDbType.Money;
Adding Parameters to Command Objects
GetCostCommand.Parameters.Add(TotalCostParameter);
Saving and Retrieving BLOB Values in a Database
BLOBs in a database are more complex than simple strings containing names and addresses or numeric values containing integers or money values. BLOBs are things like graphics and photos, documents saved in binary formats, and even complete assemblies or executables that you want to store in a database.
Working with BLOBs
Saving and fetching binary data presents interesting problems that are typically not encountered when querying standard rows of data. The problems arise because you will probably not want to move the entire BLOB in one piece but will likely need to break it up into smaller portions. For example, consider having to move a large binary that is several megabytes in size. Loading the entire BLOB into a variable consumes a lot of memory and can seriously affect the performance of your application. Having to work with a table of these BLOBs, you can quickly see the dilemma.
The good thing is that the .NET Framework provides classes that are specifically designed for moving large amounts of binary data. Specifically, access to these classes—for example, the BinaryReader and BinaryWriter classes, the FileStream and MemoryStream classes, and so on—is enabled in the System.IO namespace. Although this lesson does not use all the available stream objects, it should provide enough of a starting point to understand the basics of saving and fetching binary data from a database.
BLOBs and the DataReader
By setting its CommandBehavior to SequentialAccess, you can then call the GetBytes method, which allows you to read the data in smaller, user-definable amounts. The bytes that make up a BLOB are transported in and out of the database to your application using byte arrays.