Professional Documents
Culture Documents
NET - Part 1
What is ADO.NET?
ADO.NET is not a different technology. In simple terms, you can think of ADO.NET, as a set
of classes (Framework), that can be used to interact with data sources like Databases and XML
files. This data can, then be consumed in any .NET application. ADO stands for Microsoft
ActiveX Data Objects.
The following are, a few of the different types of .NET applications that use ADO.NET to connect
to a database, execute commands, and retrieve data.
ASP.NET Web Applications
Windows Applications
Console Applications
What are .NET Data Providers?
Databases only understand SQL. If a .NET application (Web, Windows, Console etc..) has to
retrieve data, then the application needs to
1. Connect to the Database
2. Prepare an SQL Command
3. Execute the Command
4. Retrieve the results and display in the application
Sample ADO.NET code to connect to SQL Server Database and retrieve data. Notice that
we are using SQLConnection, SQLCommand and SQLDataReader classes . All the objects
are prefixed with the word SQL. All these classes are present
inSystem.Data.SqlClient namespace. So, we can say that the .NET data provider for SQL
Server is System.Data.SqlClient.
SqlConnection con = new SqlConnection("data source=.; database=Sample; integrated
security=SSPI");
SqlCommand cmd = new SqlCommand("Select * from tblProduct", con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
GridView1.DataSource = rdr;
GridView1.DataBind();
con.Close();
Sample ADO.NET code to connect to Oracle Database and retrieve data. Notice that we are
using OracleConnection, OracleCommand and OracleDataReader classes . All the objects
are prefixed with the word Oracle. All these classes are present in System.Data.OracleClient
namespace. So, we can say that the .NET data provider for Oracle is
System.Data.OracleClient.
OracleConnection con = new OracleConnection("Oracle Database Connection String");
OracleCommand cmd = new OracleCommand("Select * from tblProduct", con);
con.Open();
OracleDataReader rdr = cmd.ExecuteReader();
GridView1.DataSource = rdr;
GridView1.DataBind();
con.Close();
Please note that, depending on the provider, the following ADO.NET objects have a different
prefix
1. Connection - SQLConnection, OracleConnection, OleDbConnection, OdbcConnection etc
2. Command - SQLCommand, OracleCommand, OleDbCommand, OdbcCommand etc
3. DataReader - SQLDataReader, OracleDataReader, OleDbDataReader, OdbcDataReader etc
4. DataAdapter - SQLDataAdapter, OracleDataAdapter, OleDbDataAdapter, OdbcDataAdapter
etc
The DataSet object is not provider specific. Once we connect to a Database, execute
command, and retrieve data into .NET application. The data can then be stored in a DataSet
and work independently of the database.
The first thing that we will have to do, when working with databases is to create a connection
object. There are 2 ways to create an instance of SqlConnection class as shown below.
Create an instance of SqlConnection class uwing the constructor that takes
ConnectionString parameter
SqlConnection connection = new SqlConnection("data source=.; database=SampleDB;
integrated security=SSPI");
//First create an instance of SqlConnection class using the parameter-less constructor
SqlConnection connection = new SqlConnection();
//Then set the ConnectionString property of the connection object
connection.ConnectionString = "data source=.; database=SampleDB; integrated
security=SSPI";
The ConnectionString parameter is a string made up of Key/Value pairs that has the
information required to create a connection object.
To create a connection object with windows authentication
string ConnectionString = "data source=.; database=SampleDB; integrated security=SSPI";
To create a connection object with SQL Server authentication
string ConnectionString = "data source=.; database=SampleDB; user id=MyUserName;
password=MyPassword";
The "data source" is the name or IP Address of the SQL Server that we want to connect to. If
you are working with a local instance of sql server, you can just specify DOT(.). If the server is
on a network, then use Name or IP address.
Sample ADO.NET code that
1. Creates a connection
2. The created connection object is then passed to the command object, so that the command
object knows on which sql server connection to execute this command.
3. Execute the command, and set the command results, as the data source for the gridview
control.
4. Call the DataBind() method
5. Close the connection in the finally block. Connections are limited and are very valuable.
Connections must be closed properly, for better performance and scalability.
Note: Connections should be opened as late as possible, and should be closed as early as
possible.
}
Common Interview Question: What are the 2 uses of an using statement in C#?
1. To import a namespace. Example: using System;
2. To close connections properly as shown in the example above
<connectionStrings>
<add name="DatabaseConnectionString"
connectionString="data source=.; database=Sample_Test_DB; Integrated
Security=SSPI"
providerName="System.Data.SqlClient" />
</connectionStrings>
The sample code below, executes a T-SQL statement, that returns multiple rows of data
using ExecuteReader() method. In this example, we are creating an instance of SqlCommand
class, in just one line, by passing in the command text, and the connection object. For this
purpose, we are using an overloaded constructor of the SqlCommand class that takes 2
parameters(cmdText, connection).
In the example below, we are using ExecuteScalar() method, as the T-SQL statement
returns a single value.
protected void Page_Load(object sender, EventArgs e)
{
string ConnectionString =
ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection("data source=.; database=Sample;
integrated security=SSPI"))
{
//Create an instance of SqlCommand class, specifying the T-SQL command
//that we want to execute, and the connection object.
SqlCommand cmd = new SqlCommand("Select Count(Id) from tblProductInventory",
connection);
connection.Open();
//As the T-SQL statement that we want to execute return a single value,
//use ExecuteScalar() method of the command object.
//Since the return type of ExecuteScalar() is object, we are type casting to int datatype
int TotalRows = (int)cmd.ExecuteScalar();
Response.Write("Total Rows = " + TotalRows.ToString());
}
}
The following example performs an Insert, Update and Delete operations on a SQL server
database using the ExecuteNonQuery() method of the SqlCommand object.
protected void Page_Load(object sender, EventArgs e)
{
string ConnectionString =
ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection("data source=.;
database=Sample_Test_DB; integrated security=SSPI"))
{
//Create an instance of SqlCommand class, specifying the T-SQL command
//that we want to execute, and the connection object.
SqlCommand cmd = new SqlCommand("insert into tblProductInventory values (103, 'Apple
Laptops', 100)", connection);
connection.Open();
//Since we are performing an insert operation, use ExecuteNonQuery()
//method of the command object. ExecuteNonQuery() method returns an
//integer, which specifies the number of rows inserted
int rowsAffected = cmd.ExecuteNonQuery();
Response.Write("Inserted Rows = " + rowsAffected.ToString() + "<br/>");
//Set to CommandText to the update query. We are reusing the command object,
//instead of creating a new command object
cmd.CommandText = "update tblProductInventory set QuantityAvailable = 101 where Id =
101";
//use ExecuteNonQuery() method to execute the update statement on the database
rowsAffected = cmd.ExecuteNonQuery();
Response.Write("Updated Rows = " + rowsAffected.ToString() + "<br/>");
//Set to CommandText to the delete query. We are reusing the command object,
//instead of creating a new command object
cmd.CommandText = "Delete from tblProductInventory where Id = 102";
//use ExecuteNonQuery() method to delete the row from the database
rowsAffected = cmd.ExecuteNonQuery();
Response.Write("Deleted Rows = " + rowsAffected.ToString() + "<br/>");
}
}
If you want to following along, use the following sql script to create the table.
Create table tblProductInventory
(
Drag and drop a TextBox, Button and a GridView control onto the webform. Change the ID
of the TextBox to ProductNameTextBox and GridView toProductsGridView. Change the ID
of the Button to GetProductsButton and the Textto "Get Products". At this point the HTML of
the webform should be as shown below.
<asp:TextBox ID="ProductNameTextBox" runat="server"></asp:TextBox>
<asp:Button ID="GetProductsButton" runat="server" Text="Get Products" />
<br /><br />
<asp:GridView ID="ProductsGridView" runat="server">
</asp:GridView>
Now double click the Button control to generate the Click event handler in the code behind
file, and then copy and paste the following code. In this example, we are building the query
dynamically by concatenating the strings that the user has typed into the textbox. This is
extremely dangerous, as it is vulnerable to SQL injection attacks.
protected void GetProductsButton_Click(object sender, EventArgs e)
{
string ConnectionString =
ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection("DatabaseConnectionString"))
{
//Build the query dynamically, by concatenating the text, that the user has
//typed into the ProductNameTextBox. This is a bad way of constructing
//queries. This line of code will open doors for sql injection attack
SqlCommand cmd = new SqlCommand("Select * from tblProductInventory where
ProductName like '" + ProductNameTextBox.Text + "%'", connection);
connection.Open();
ProductsGridView.DataSource = cmd.ExecuteReader();
ProductsGridView.DataBind();
}
}
Now, run the project. Enter letter "i" into the textbox and click Get Products button.
The iPhone and ipad products will be listed in the gridview as expected. But remember, user
can type some dangerous sql queries into the textbox, which in turn will be executed by the
application on the database. To give you a flavour of that, just imagine what could happen if the
user types the following into the TextBox, and clicks Get Products button.
i'; Delete from tblProductInventory -Now execute the following select query on the database
Select * from tblProductInventory
The entire data from tblProductInventory table is deleted. This is called SQL
injection attack. I have seen a lot of new developers building queries dynamically by
concatenating the strings, that end users enter into user interface controls like textboxes. Just
imagine the extent of damage that can happen as a result of sql injection.
However, sql injection can be easily avoided, by using parameterized queries or stored
procedures. We will talk about these in our next video session.
The following ADO.NET code is from Part 5. This is the code, that let's sql injection happen.
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
string Command = "Select * from tblProductInventory where ProductName like '" +
TextBox1.Text + "%'";
SqlCommand cmd = new SqlCommand(Command, con);
con.Open();
GridView1.DataSource = cmd.ExecuteReader();
GridView1.DataBind();
}
The above code can be easily re-written using parameterized queries to prevent sql
injection attack. The re-written code is shown below. Notice, that the query now uses parameter
- @ProductName. The value for this parameter is then provided using theAddWithValue()
method. The parameter is associated with the command object usingPrameters collection
property of the command object.
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
// Parameterized query. @ProductName is the parameter
string Command = "Select * from tblProductInventory where ProductName like
@ProductName" ;
SqlCommand cmd = new SqlCommand(Command, con);
// Provide the value for the parameter
cmd.Parameters.AddWithValue("@ProductName", TextBox1.Text + "%");
con.Open();
GridView1.DataSource = cmd.ExecuteReader();
GridView1.DataBind();
}
Sql injection can also be prevented using stored procedures. So, first let's write a stored
procedure, that returns the list of products. This stored procedure takes an input
parameter @ProductName.
Create Procedure spGetProductsByName
@ProductName nvarchar(50)
as
Begin
Select * from tblProductInventory
where ProductName like @ProductName + '%'
End
To test this procedure execute the follwing command in sql server management studio.
Execute spGetProductsByName 'ip'
Now, let's re-write the code, to use stored procedure spGetProductsByName.
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
// The command, that we want to execute is a stored procedure,
// so specify the name of the procedure as cmdText
SqlCommand cmd = new SqlCommand("spGetProductsByName", con);
// Specify that the T-SQL command is a stored procedure
cmd.CommandType = System.Data.CommandType.StoredProcedure;
// Associate the parameter and it's value with the command object
cmd.Parameters.AddWithValue("@ProductName", TextBox1.Text + "%");
con.Open();
GridView1.DataSource = cmd.ExecuteReader();
GridView1.DataBind();
}
If you type the following input into the TextBox, the entire content of the TextBox is now
treated as a value for the parameter - @ProductName not as a seperate sql statement.
i'; Delete from tblProductInventory -So the conclusion is that, always used parameterized queries or stored procedures, to
avoid sql injection attacks.
Script to insert sample data. Notice, that in the insert statement we are not providing a
value for EmployeeId Column.
Insert into tblEmployees values('Mike','Male',5000)
Insert into tblEmployees values('Pam','Female',3500)
Insert into tblEmployees values('John','Male',2350)
Insert into tblEmployees values('Sara','Female',5700)
Insert into tblEmployees values('Steve','Male',4890)
Insert into tblEmployees values('Sana','Female',4500)
1. spAddEmployee stored procedure inserts a row into tblEmployees tables.
2. @Name, @Gender and @Salary are input parameters.
3. @EmployeeId is an output parameter
4. The stored procedure has got only 2 lines of code with in the body. The first line inserts a row
into the tblEmployees table. The second line, gets the auto generated identity value of the
EmployeeId column.
5. This procedure, will later be called by a dot net application.
Create Procedure spAddEmployee
@Name nvarchar(50),
@Gender nvarchar(20),
@Salary int,
@EmployeeId int Out
as
Begin
Insert into tblEmployees values(@Name, @Gender, @Salary)
Select @EmployeeId = SCOPE_IDENTITY()
End
At this point, we have done everything that is required for our demo, from a database
perspective. Now let's flip to visual studio. Create an asp.net web application. Copy and
Paste the following HTML onto a webform.
<table style="border: 1px solid black; font-family:Arial">
<tr>
<td>
Employee Name
</td>
<td>
<asp:TextBox ID="txtEmployeeName" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Gender
</td>
<td>
<asp:DropDownList ID="ddlGender" runat="server">
<asp:ListItem>Male</asp:ListItem>
<asp:ListItem>Female</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
Salary
</td>
<td>
<asp:TextBox ID="txtSalary" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td colspan="2">
<asp:Button ID="btnSubmit" runat="server" Text="Submit"
onclick="btnSubmit_Click" />
</td>
</tr>
<tr>
<td colspan="2">
<asp:Label ID="lblMessage" runat="server"></asp:Label>
</td>
</tr>
</table>
The design of the webform, should be as shown below.
Copy and paste the following code in the code behind page.
protected void btnSubmit_Click(object sender, EventArgs e)
{
//Read the connection string from Web.Config file
string ConnectionString
=ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(ConnectionString))
{
//Create the SqlCommand object
SqlCommand cmd = new SqlCommand("spAddEmployee", con);
//Specify that the SqlCommand is a stored procedure
cmd.CommandType = System.Data.CommandType.StoredProcedure;
//Add the input parameters to the command object
cmd.Parameters.AddWithValue("@Name", txtEmployeeName.Text);
cmd.Parameters.AddWithValue("@Gender", ddlGender.SelectedValue);
cmd.Parameters.AddWithValue("@Salary", txtSalary.Text);
SqlDataReader.
SqlCommand command = new SqlCommand("Select * from tblProductInventory", connection);
SqlDataReader reader = command.ExecuteReader();
sourceTable.Columns.Add("DiscountedPrice");
while (reader.Read())
{
//Calculate the 10% discounted price
int OriginalPrice = Convert.ToInt32(reader["UnitPrice"]);
double DiscountedPrice = OriginalPrice * 0.9;
// Populate datatable column values from the SqlDataReader
DataRow datarow = sourceTable.NewRow();
datarow["ID"] = reader["ProductId"];
datarow["Name"] = reader["ProductName"];
datarow["Price"] = OriginalPrice;
datarow["DiscountedPrice"] = DiscountedPrice;
//Add the DataRow to the DataTable
sourceTable.Rows.Add(datarow);
}
// Set sourceTable as the DataSource for the GridView
ProductsGridView.DataSource = sourceTable;
ProductsGridView.DataBind();
}
}
SQL script to create the table we used in the Demo.
Create table tblProductInventory
(
ProductId int identity primary key,
ProductName nvarchar(50),
UnitPrice int
)
Script to populate data
Insert into tblProductInventory values('iPhone',350)
Insert into tblProductInventory values('Apple Laptops',1250)
Insert into tblProductInventory values('Books',110)
Insert into tblProductInventory values('Acer Laptops',1150)
Insert into tblProductInventory values('iPads',450)
Note: Please make sure you have the following namespace declarations in the code
behind file.
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
ProductsGridView.DataBind();
CategoriesGridView.DataSource = reader;
CategoriesGridView.DataBind();
}
}
To retrieve the second result-set from SqlDataReader object, use the NextResult() as
shown in the code snippet below. The NextResult() method returns true and advances to the
next result-set.
string ConnectionString
=ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
SqlCommand command = new SqlCommand("select * from tblProductInventory; select * from
tblProductCategories", connection);
using (SqlDataReader reader = command.ExecuteReader())
{
ProductsGridView.DataSource = reader;
ProductsGridView.DataBind();
while (reader.NextResult())
{
CategoriesGridView.DataSource = reader;
CategoriesGridView.DataBind();
}
}
}
The SqlDataReader object's Read() method is used to loop thru the rows in a given result
set, where as the NextResult() method is used to loop thru multiple result sets.
Sql script to create and populate the required tables we used in this demo.
Create table tblProductInventory
(
ProductId int identity primary key,
ProductName nvarchar(50),
UnitPrice int
)
Insert into tblProductInventory values('iPhone',350)
Insert into tblProductInventory values('Apple Laptops',1250)
Insert into tblProductInventory values('Books',110)
Insert into tblProductInventory values('Acer Laptops',1150)
Insert into tblProductInventory values('iPads',450)
as
Begin
Select ProductId, ProductName, UnitPrice
from tblProductInventory
where ProductId = @ProductId
End
To execute stored procedure spGetProductInventoryById, we need to associate
parameter @ProductId to the SqlDataAdapeter object's SelectCommand as shown below.
string ConnectionString
=ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
// Create an instance of SqlDataAdapter, specifying the stored procedure
// and the connection object to use
SqlDataAdapter dataAdapter = new SqlDataAdapter("spGetProductInventoryById",
connection);
// Specify the command type is an SP
dataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
// Associate the parameter with the stored procedure
dataAdapter.SelectCommand.Parameters.AddWithValue("@ProductId", 1);
DataSet dataset = new DataSet();
dataAdapter.Fill(dataset);
GridView1.DataSource = dataset;
GridView1.DataBind();
}
Copy and paste the following code into the code behind page.
string ConnectionString
=ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
SqlDataAdapter dataAdapter = newSqlDataAdapter("spGetProductAndCategoriesData",
connection);
dataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
DataSet dataset = new DataSet();
dataAdapter.Fill(dataset);
GridViewProducts.DataSource = dataset;
GridViewProducts.DataBind();
GridViewCategories.DataSource = dataset;
GridViewCategories.DataBind();
}
When you run the project now, notice that both the gridview controls show the same
data. This is because, by default, the first table from the dataset is used as the data source for
both the gridview controls. We actually want to show products data in one gridview control and
categories data in the other. To specify the specific DataTable, that you want to bind to a
gridview control, use the Tables collection property of the dataset object, as shown below.
string ConnectionString
=ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
SqlDataAdapter dataAdapter = newSqlDataAdapter("spGetProductAndCategoriesData",
connection);
dataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
DataSet dataset = new DataSet();
dataAdapter.Fill(dataset);
GridViewProducts.DataSource = dataset.Tables[0];
GridViewProducts.DataBind();
GridViewCategories.DataSource = dataset.Tables[1];
GridViewCategories.DataBind();
}
By default the tables in the DataSet will have table names as Table, Table1, Table2etc. So
if you want to give the tables in the DataSet a meaningful name, use the TableName property as
shown below.
dataset.Tables[0].TableName = "Products";
dataset.Tables[1].TableName = "Categories";
These table names can then be used when binding to a GridView control, instead of using
the integral indexer, which makes your code more readable, and maintainable.
GridViewProducts.DataSource = dataset.Tables["Products"];
GridViewProducts.DataBind();
GridViewCategories.DataSource = dataset.Tables["Categories"];
GridViewCategories.DataBind();
Create an asp.net web application project, and add the following database connectionstring
to the web.config file.
<connectionStrings>
<add name="DBCS"
connectionString="data source=.; database=Sample_Test_DB; Integrated Security=SSPI"
providerName="System.Data.SqlClient" />
</connectionStrings>
Drag and drop 2 button controls, a label and a gridview control onto the webform.
1. Set the ID of the first button control to btnLoadData and Text to Load Data
2. Set the ID of the second button control to btnClearnCache and Text to Clear Cache
3. Set the ID of the label control to lblMessage and remove the Text property
4. Set the ID of the GridView to gvProducts
At this stage the HTML of your webform, should be as shown below.
<asp:Button ID="btnLoadData" runat="server" Text="Load Data"
onclick="btnLoadData_Click" />
<asp:Button ID="btnClearnCache" runat="server" Text="Clear Cache"
onclick="btnClearnCache_Click" />
<br />
<br />
<asp:Label ID="lblMessage" runat="server"></asp:Label>
<br />
<br />
<asp:GridView ID="gvProducts" runat="server">
</asp:GridView>
Now, copy and paste the following code in the code behind page
protected void btnLoadData_Click(object sender, EventArgs e)
{
// Check if the DataSet is present in the cache
if (Cache["Data"] == null)
{
// If the dataset is not in the cache load data from the database into the DataSet
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection connection = new SqlConnection(CS))
{
SqlDataAdapter dataAdapter = new SqlDataAdapter("Select * from tblProductInventory",
connection);
DataSet dataset = new DataSet();
dataAdapter.Fill(dataset);
gvProducts.DataSource = dataset;
gvProducts.DataBind();
// Store the DataSet in the Cache
Cache["Data"] = dataset;
lblMessage.Text = "Data loaded from the Database";
}
}
// If the DataSet is in the Cache
else
{
// Retrieve the DataSet from the Cache and type cast to DataSet
gvProducts.DataSource = (DataSet)Cache["Data"];
gvProducts.DataBind();
lblMessage.Text = "Data loaded from the Cache";
}
}
protected void btnClearnCache_Click(object sender, EventArgs e)
{
// Check if the DataSet is present in the cache
if (Cache["Data"] != null)
{
// Remove the DataSet from the Cache
Cache.Remove("Data");
lblMessage.Text = "DataSet removed from the cache";
}
// If the DataSet is not in the Cache
else
{
lblMessage.Text = "There is nothing in the cache to remove";
}
}
Now, run the application. The first time you click Load Data button, the data will be loaded
from the database, as we don't have the DataSet in the Cache yet. Once the Data is loaded into
the DataSet. The DataSet is then cached. If you click the Load Data button now, then the Data
will be loaded from the cache. At this point we don't need to have any connection to the
Database.
To prove this stop the sql server service on your machine.
1. In the run window, type services.msc and press enter key
2. In the services window, find SQL Server service
3. Right click on the SQL Server service and stop it.
Since we have stopped the service, sql server is no longer running on our machine. Now click
the Load Data button. The data will be loaded from the cache. Now, clear the cache, by click on
Clear Cache button. This will remove the DataSet from the cache. Now, try to load the data by
clicking on Load Data button. Since, the DataSet is no longer present in the cache, and the sql
server service is not running you will receive an error stating - A network-related or instancespecific error occurred while establishing a connection to SQL Server. The server was not found
or was not accessible. Verify that the instance name is correct and that SQL Server is
configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not
open a connection to SQL Server)
Start the service, and click the Load Data button. The data should now be loaded and cached.
Please Note: Step 2, can also be done in single line as shown below. Here, we are passing the
SqlDataAdapter instance as an argument to SqlCommandBuilder class constructor
SqlCommandBuilder builder = new SqlCommandBuilder(dataAdapter);
Gender
</td>
<td>
<asp:DropDownList ID="ddlGender" runat="server">
<asp:ListItem Text="Select Gender" Value="-1"></asp:ListItem>
<asp:ListItem Text="Male" Value="Male"></asp:ListItem>
<asp:ListItem Text="Female" Value="Female"></asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
Total Marks
</td>
<td>
<asp:TextBox ID="txtTotalMarks" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td colspan="2">
<asp:Button ID="btnUpdate" runat="server" Text="Update"
OnClick="btnUpdate_Click" />
<asp:Label ID="lblStatus" runat="server" Font-Bold="true">
</asp:Label>
</td>
</tr>
</table>
</div>
ASPX.CS Code:
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnGetStudent_Click(object sender, EventArgs e)
{
string connectionString =
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection connection = new SqlConnection(connectionString);
string selectQuery = "Select * from tblStudents where ID = " +
txtStudentID.Text;
SqlDataAdapter dataAdapter = new SqlDataAdapter(selectQuery, connection);
dr["Gender"] = ddlGender.SelectedValue;
dr["TotalMarks"] = txtTotalMarks.Text;
dr["Id"] = txtStudentID.Text;
int rowsUpdated = dataAdapter.Update(ds, "Students");
if (rowsUpdated == 0)
{
lblStatus.ForeColor = System.Drawing.Color.Red;
lblStatus.Text = "No rows updated";
}
else
{
lblStatus.ForeColor = System.Drawing.Color.Green;
lblStatus.Text = rowsUpdated.ToString() + " row(s) updated";
}
}
}
Please make sure to include the following using declarations.
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
This is continuation to Part 13. Please watch Part 13 from ADO.NET tutorial, before
proceeding.
Two common reasons why SqlDataAdapter.Update does not work
1. SqlCommandBuilder object not associated with SqlDataAdapter object. Without this
association SqlCommandBuilder object does not know how to generate INSERT, UPDATE and
DELETE statements.
SqlCommandBuilder builder = new SqlCommandBuilder(dataAdapter);
If the above line is not present in your code, SqlDataAdapter.Update() method will throw an
exception - Update requires a valid UpdateCommand when passed DataRow collection with
modified rows.
2. The SelectCommand that is associated with SqlDataAdapter, does not return atleast
one primary key or unique column. If this is the case you will get an exception -Dynamic SQL
generation for the UpdateCommand is not supported against a SelectCommand that does not
return any key column information.
For troubleshooting purposes, if you want to see the autogenerated INSERT, UPDATE, and
DELETE T-SQL statements, use GetInsertCommand(), GetUpdateCommand() and
GetDeleteCommand().
lblInsert.Text = builder.GetInsertCommand().CommandText;
lblUpdate.Text = builder.GetUpdateCommand().CommandText;
lblDelete.Text = builder.GetDeleteCommand().CommandText;
A DataSet is an in-memory data store that can hold one or more tables. DataSets only hold data
and do not interact with the underlying database table. The DataSet object has no knowledge of
the underlying Data Source. It is the SqlDataAdapter object that retrieves data from the
datasource.
This is how it works.
1. You create an instance of SqlDataAdapter by specifying a select command and a connection
object
string connectionString =ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection connection = new SqlConnection(connectionString);
string selectQuery = "Select * from tblStudents";
SqlDataAdapter dataAdapter = new SqlDataAdapter(selectQuery, connection);
2. When SqlDataAdapter.Fill() method is invoked, SqlDataAdapter opens the connection to the
database, executes the select command, and the DataSet is populated with the data that is
retrieved. The SqlDataAdapter automatically closes the connection.
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Students");
3. You now have data in the DataSet and there is no active connection to the database. At this
point you can make any changes(insert, update, delete) to the data in the DataSet. Only the
data in the DataSet is changed, the underlying database table data is not changed.
4. To update the underlying database table, invoke SqlDataAdapter.Update() method. Make
sure there is an UPDATE, DELETE and INSERT command are associated with SqlDataAdapter
object when Update() method is called, otherwise there would be a runtime exception.
dataAdapter.Update(DataSetObject, "Students");
ASPX Code:
<div style="font-family: Arial">
<asp:Button ID="btnGetDataFromDB" runat="server" Text="Get Data from Database"
onclick="btnGetDataFromDB_Click" />
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" onrowediting="GridView1_RowEditing"
onrowcancelingedit="GridView1_RowCancelingEdit"
onrowdeleting="GridView1_RowDeleting"
onrowupdating="GridView1_RowUpdating">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ID" HeaderText="ID" InsertVisible="False"
ReadOnly="True" SortExpression="ID" />
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
{
GridView1.DataSource = (DataSet)Cache["DATASET"];
GridView1.DataBind();
}
}
protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
// Set row in editing mode
GridView1.EditIndex = e.NewEditIndex;
GetDataFromCache();
}
protected void GridView1_RowCancelingEdit(object sender,GridViewCancelEditEventArgs e)
{
GridView1.EditIndex = -1;
GetDataFromCache();
}
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// Retrieve dataset from cache
DataSet dataSet = (DataSet)Cache["DATASET"];
// Find datarow to edit using primay key
DataRow dataRow = dataSet.Tables["Students"].Rows.Find(e.Keys["ID"]);
// Update datarow values
dataRow["Name"] = e.NewValues["Name"];
dataRow["Gender"] = e.NewValues["Gender"];
dataRow["TotalMarks"] = e.NewValues["TotalMarks"];
// Overwrite the dataset in cache
Cache.Insert("DATASET", dataSet, null, DateTime.Now.AddHours(24),
System.Web.Caching.Cache.NoSlidingExpiration);
// Remove the row from edit mode
GridView1.EditIndex = -1;
// Reload data to gridview from cache
GetDataFromCache();
}
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
DataSet dataSet = (DataSet)Cache["DATASET"];
dataSet.Tables["Students"].Rows.Find(e.Keys["ID"]).Delete();
Cache.Insert("DATASET", dataSet, null, DateTime.Now.AddHours(24),
System.Web.Caching.Cache.NoSlidingExpiration);
GetDataFromCache();
}
protected void btnGetDataFromDB_Click(object sender, EventArgs e)
{
GetDataFromDB();
}
protected void btnUpdateDatabaseTable_Click(object sender, EventArgs e)
{
if (Cache["DATASET"] != null)
{
string connectionString =
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection connection = new SqlConnection(connectionString);
string selectQuery = "Select * from tblStudents";
SqlDataAdapter dataAdapter = new SqlDataAdapter(selectQuery, connection);
// Update command to update database table
string strUpdateCommand = "Update tblStudents set Name = @Name, Gender =
@Gender, TotalMarks = @TotalMarks where Id = @Id";
// Create an instance of SqlCommand using the update command created above
SqlCommand updateCommand = new SqlCommand(strUpdateCommand, connection);
// Specify the parameters of the update command
updateCommand.Parameters.Add("@Name", SqlDbType.NVarChar, 50, "Name");
updateCommand.Parameters.Add("@Gender", SqlDbType.NVarChar, 20,"Gender");
updateCommand.Parameters.Add("@TotalMarks", SqlDbType.Int, 0,"TotalMarks");
updateCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "Id");
// Associate update command with SqlDataAdapter instance
dataAdapter.UpdateCommand = updateCommand;
// Delete command to delete data from database table
string strDeleteCommand = "Delete from tblStudents where Id = @Id";
// Create an instance of SqlCommand using the delete command created above
SqlCommand deleteCommand = new SqlCommand(strDeleteCommand, connection);
// Specify the parameters of the delete command
deleteCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "Id");
// Associate delete command with SqlDataAdapter instance
dataAdapter.DeleteCommand = deleteCommand;
// Update the underlying database table
dataAdapter.Update((DataSet)Cache["DATASET"], "Students");
lblStatus.Text = "Database table updated";
}
}
}
Please make sure to include the following using declarations:
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
This is continuation to Part 15. Please watch Part 15, before proceeding.
To understand AcceptChanges() and RejectChanges() methods better, we need to
understand Row States and Row Versions.
Every DataRow that is present in DataTable of a DataSet has RowState property. Please check
the following MSDN link, for different values of RowState property and their description.
Different DataRowVersion enumeration values and their description is also present.
http://msdn.microsoft.com/en-us/library/ww3k31w0.aspx
HasVersion() method can be used to check if a row has got a specific DataRowVersion.
DataRow.HasVersion(DataRowVersion.Original)
When we call DataAdapter.Fill() method, data is loaded into the DataSet and theRowState of
all the rows will be Unchanged. When we edit a row the row state becomes Modified. If we
delete a row, the row state becomes Deleted. At this point with in the DataSet, we have got
Unchanged, Deleted and Modified rows. If we then invoke, DataAdapter.Update() method,
based on the RowState, respective INSERT, UPDATE and DELETE commands are executed
against the underlying database table andAcceptChanges() is called automatically.
When AcceptChanges() is invoked RowState property of each DataRow changes. Added and
Modified rows become Unchanged, and Deleted rows are removed.
When RejectChanges() is invoked RowState property of each DataRow changes. Added rows
are removed. Modified and Deleted rows becomes Unchanged.
Both AcceptChanges() and RejectChanges() methods can be invoked at the following
levels
1. At the DataSet level - When invoked at the DataSet level, they get called automatically on
each DataTable with in the DataSet, and on each DataRow within each DataTable.
2. At the DataTable level - When invoked at the DataTable level, they get called automatically
on each DataRow within each DataTable.
3. At the DataRow level - Gets called only for the row, on which it is invoked.
{
if (!IsPostBack)
{
string connectionString =
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection connection = new SqlConnection(connectionString);
string selectQuery = "Select * from tblStudents";
SqlDataAdapter dataAdapter = new SqlDataAdapter(selectQuery, connection);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Students");
Session["DATASET"] = dataSet;
GridView1.DataSource = from dataRow indataSet.Tables["Students"].AsEnumerable()
select new Student
{
ID = Convert.ToInt32(dataRow["Id"]),
Name = dataRow["Name"].ToString(),
Gender = dataRow["Gender"].ToString(),
TotalMarks = Convert.ToInt32(dataRow["TotalMarks"])
};
GridView1.DataBind();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
DataSet dataSet = (DataSet)Session["DATASET"];
if (string.IsNullOrEmpty(TextBox1.Text))
{
GridView1.DataSource = from dataRow in dataSet.Tables["Students"].AsEnumerable()
select new Student
{
ID = Convert.ToInt32(dataRow["Id"]),
Name = dataRow["Name"].ToString(),
Gender = dataRow["Gender"].ToString(),
TotalMarks = Convert.ToInt32(dataRow["TotalMarks"])
};
GridView1.DataBind();
}
else
{
GridView1.DataBind();
}
else
{
GridView1.DataSource = from student in studentsDataTable
where student.Name.ToUpper().StartsWith(TextBox1.Text.ToUpper())
select new { student.ID, student.Name, student.Gender, student.TotalMarks };
GridView1.DataBind();
}
}
}
Part 18 - Load xml data into sql server table using sqlbulkcopy
Suggested Videos
Part 15 - Disconnected data access in asp.net
Part 16 - Dataset.rejectchanges and dataset.acceptchanges methods
Part 17 - Strongly typed datasets
Notice that we have Departments and Employees data in the XML file. We would like to load
Employees data into Employees table and Departments data into Departments table. After the
data is loaded, the database tables should look as shown below.
The following are the steps to achieve this using SqlBulkCopy class
Step 1 : Create the database tables using the following sql script
Create table Departments
(
ID int primary key,
Name nvarchar(50),
Location nvarchar(50)
)
GO
Step 3 : Add a new xml file to the project. Name it Data.xml. Copy and paste the following XML.
<Data>
<Department Id="1">
<Name>IT</Name>
<Location>New York</Location>
</Department>
<Department Id="2">
<Name>HR</Name>
<Location>London</Location>
</Department>
<Department Id="3">
<Name>Payroll</Name>
<Location>Mumbai</Location>
</Department>
<Employee Id="1">
<Name>Mark</Name>
<Gender>Male</Gender>
<DepartmentId>1</DepartmentId>
</Employee>
<Employee Id="2">
<Name>John</Name>
<Gender>Male</Gender>
<DepartmentId>1</DepartmentId>
</Employee>
<Employee Id="3">
<Name>Mary</Name>
<Gender>Female</Gender>
<DepartmentId>2</DepartmentId>
</Employee>
<Employee Id="4">
<Name>Steve</Name>
<Gender>Male</Gender>
<DepartmentId>2</DepartmentId>
</Employee>
<Employee Id="5">
<Name>Ben</Name>
<Gender>Male</Gender>
<DepartmentId>3</DepartmentId>
</Employee>
</Data>
Step 4 : Include the database connection string in web.config file
<connectionStrings>
<add name="CS"
connectionString="server=.;database=Sample;integrated security=true"/>
</connectionStrings>
Step 5 : Add a new WebForm to the project. Drag and drop a button control on the webform.
Double click the button control to generate the click event handler. Copy and paste the following
code in the the click event handler method.
string cs = ConfigurationManager.ConnectionStrings["CS"].ConnectionString;
using (SqlConnection con = new SqlConnection(cs))
{
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
Part 19 - Copying data from one table to another table using SqlBulkCopy
Suggested Videos
Part 16 - Dataset.rejectchanges and dataset.acceptchanges methods
Part 17 - Strongly typed datasets
Part 18 - Load xml data into sql server table using sqlbulkcopy
In this video we will discuss copying data from one table to another table. The source and
destination tables may be in the same database or in different databases and these database
can be on the same sql server or in different servers. In Part 18 we discussed, loading xml data
into sql server table using sqlbulkcopy. We will be continuing with the example we worked with
in Part 18.
Step 1 : Create a new database. Name it SourceDB. Execute the following sql script to create
Departments and Employees tables, and to populate with data.
Create table Departments
(
ID int primary key identity,
Name nvarchar(50),
Location nvarchar(50)
)
GO
<add name="SourceCS"
connectionString="server=.;database=SourceDB;integrated security=true"/>
<add name="DestinationCS"
connectionString="server=.;database=DestinationDB;integrated security=true"/>
</connectionStrings>
Step 4 : Copy and paste the following code in the button click event handler method in the
code-behind file
string sourceCS =
ConfigurationManager.ConnectionStrings["SourceCS"].ConnectionString;
string destinationCS =
ConfigurationManager.ConnectionStrings["DestinationCS"].ConnectionString;
using (SqlConnection sourceCon = new SqlConnection(sourceCS))
{
SqlCommand cmd = new SqlCommand("Select * from Departments", sourceCon);
sourceCon.Open();
}
}
not required.
BatchSize property - Specifies the number of rows in a batch that will be copied to the
destination table. The BatchSize property is very important as the performance of data transfer
depends on it. The default batch size is 1. In the example below, BatchSize is set to 10000. This
means once the reader has read 10000 rows they will be sent to the database as a single batch
to perform the bulk copy operation.
NotifyAfter property - Defines the number of rows to be processed before raising
SqlRowsCopied event. In the example below, NotifyAfter property is set to 5000. This means
once every 5000 rows are copied to the destination table SqlRowsCopied event is raised.
SqlRowsCopied event - This event is raised every time the number of rows specified by
NotifyAfter property are processed. This event is useful for reporting the progress of the data
transfer.
Let us now understand these properties with an example.
Step 1 : Execute the following SQL script to create Products_Source table and populate it with
test data.
Create Table Products_Source
(
[Id] int primary key,
[Name] nvarchar(50),
[Description] nvarchar(250)
)
GO
Print @Id
Set @Id = @Id + 1
End
GO
Step 2 : Create Products_Destination table
Create Table Products_Destination
(
[Id] int primary key,
[Name] nvarchar(50),
[Description] nvarchar(250)
)
GO
Step 3 : Create a new console application. Name it Demo. Include the database connection
string in App.config file
<connectionStrings>
<add name="CS"
connectionString="server=.;database=Sample;integrated security=SSPI"/>
</connectionStrings>
Step 4 : Copy and paste the following code in Program.cs file.
using System;
using System.Configuration;
using System.Data.SqlClient;
namespace Demo
{
class Program
{
static void Main()
{
string cs = ConfigurationManager.ConnectionStrings["CS"].ConnectionString;
using (SqlConnection sourceCon = new SqlConnection(cs))
{
SqlCommand cmd = new
SqlCommand("Select * from Products_Source", sourceCon);
sourceCon.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
using (SqlConnection destinationCon = new SqlConnection(cs))
{
using (SqlBulkCopy bc = new SqlBulkCopy(destinationCon))
{
bc.BatchSize = 10000;
bc.NotifyAfter = 5000;
bc.SqlRowsCopied +=
new SqlRowsCopiedEventHandler(bc_SqlRowsCopied);
bc.DestinationTableName = "Products_Destination";
destinationCon.Open();
bc.WriteToServer(rdr);
}
}
}
}
}
The table has got 2 Accounts (A1 and A2). We want to design a web application totransfer $10
from Account A1 to Account A2. The design of the webform should be as shown below.
When we click "Transfer $10 from Account A1 to Account A2" button, we should subtract 10
from A1 account and add 10 to A2 account. So there will be 2 database UPDATE statements.
What do you think will happen if only the first update statement is executed successfully and not
the second statement. $10 is deducted from the first account, but not added to the second
account. This is definitely not desirable. Either both the statements should succeed or both of
them should fail. If one succeeds and other fails we should also rollback the changes made by
the first statement to maintain the integrity of the data. This can be achieved using transactions
in ado.net.
Step 1 : Create the Accounts table using the following SQL script
Create Table Accounts
(
AccountNumber nvarchar(10) primary key,
CustomerName nvarchar(50),
Balance int
)
GO
</td>
<td>
<asp:Label ID="lblBalance1" runat="server"></asp:Label>
</td>
<td>
<asp:Label ID="lblBalance2" runat="server"></asp:Label>
</td>
</tr>
</table>
<br />
<asp:Button ID="btnTransfer" runat="server"
Text="Transfer $10 from Account A1 to Account A2"
OnClick="btnTransfer_Click" />
<br />
<br />
<asp:Label ID="lblMessage" runat="server" Font-Bold="true"></asp:Label>
</div>
Step 5 : Copy and paste the following code in the code-behind file.
using System;
using System.Configuration;
using System.Data.SqlClient;
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
lblAccountNumber2.Text = "A2";
lblName2.Text = rdr["CustomerName"].ToString();
lblBalance2.Text = rdr["Balance"].ToString();
}
}
}
}
Let's now deliberately introduce a change that would crash the application at run time after
executing the first update statement.
CHANGE THE FOLLOWING LINE
cmd = new SqlCommand("Update Accounts set Balance = Balance + 10 where AccountNumber
= 'A2'", con, transaction);
TO
cmd = new SqlCommand("Update Accounts1 set Balance = Balance + 10 where
AccountNumber = 'A2'", con, transaction);
Run the apllication again and click the "Transfer $10 from Account A1 to Account A2"button.
Notice that the transaction is rolled back and the data integrity is not lost.