Friday, September 30, 2011
Accessing and Updating Data in ASP.NET: Accessing Database Data
Introduction
One of the coolest new features of ASP.NET 2.0 is its new Data Source controls. The Data Source controls are a collection of Web controls designed to provide a declarative approach to accessing and modifying data. In short, with the Data Source controls you can work with data without having to write a lick of data access code. Compare this to ASP.NET 1.x, which required oft-repeated code to access data. For example, to display the contents of a database table in a web page with ASP.NET 1.x, you'd need to write code to:
- Connect to the database,
- Issue the command,
- Retrieve the results, and
- Work with the results / bind them to a data Web control (such as a DataGrid)
SELECT
query to use and any parameters in the WHERE
clause as properties of the Data Source controls. The code to connect to the database, issue the command, and retrieve the results is handled internally by the Data Source control. With the declarative, "code free" Data Source controls you can create a page that displays database data in under 15 seconds and without having to write a line of code. Read on to learn more!Accessing Database Data
ASP.NET 2.0 provides two Data Source controls designed specifically to access data from a database:
- SqlDataSource - useful for accessing data from any database that resides in a relational database. The "Sql" in the control name does not refer to Microsoft SQL Server, but rather the SQL syntax for querying relational databases, for the SqlDataSource control can be used to access not only Microsoft SQL Server databases, but Microsoft Access databases, Oracle databases... basically any OLE-DB or ODBC-compliant data store.
- AccessDataSource - the AccessDataSource is very similar to the SqlDataSource. The key difference is that instead of requiring a connection string to the database, the AccessDataSource control allows you to simply specify the file path to the Access
.MDB
file through itsDataFile
property.
App_Data
folder.) In this article we'll examine the SqlDataSource in detail, just touching upon the one difference in the AccessDataSource. Furthermore, the SqlDataSource and AccessDataSource controls can be used to both access and modify data; however, this article only examines accessing data. We'll see how to use these Data Source controls to modify database data in a future installment. Also, we'll focus on working with these Data Source controls in Visual Studio 2005 through the Design view. While you can set all of these properties by hand in the Source view, the Design view offers a time-saving wizard for quickly specifying which database to connect to and what query to issue. To get started, fire up Visual Studio 2005 and create a new website or, alternatively, download the code example at the end of this article. For this article I'm going to demonstrate connecting to, retrieving, and displaying data from the Microsoft Access Northwind database, which is included in the download. (Note that I'm using a slightly modified, slimmed down version of Northwind, one whose Forms, Modules, Reports, and Macros have all been deleted to reduce the file size.)
ASP.NET 2.0 introduces the
App_Data
folder, a folder that you can add to your website specifically for storing data files, such as XML files, Access database files (.mdb
), and SQL Server 2000 and SQL Server 2005 Express edition database files (.mdf
). In the code download you'll find the Northwind.mdb
file in the App_Data
folder. Of course, ASP.NET can work with databases that are not in the App_Data
folder. When using professional grade database systems, like Microsoft SQL Server, typically the database is registered on a separate database server. Regardless of where your database is located, once you have a database to work with in order to access its contents you'll want to add a SqlDataSource (or AccessDataSource) control to the page. Start by creating a new ASP.NET page and go to the Design view. Next, drag a SqlDataSource control from the Toolbox onto the Designer. The Data Source controls display in the Designer as a little gray box; they do not emit any HTML markup when a browser visits the page. In order to correctly access data, we must set a variety of SqlDataSource properties that indicate the database to use and the query to execute. While these can be set through the Properties pane, it's usually quicker to use the wizard, which can be reached by clicking on the "Configure Data Source" link in the SqlDataSource control's smart tag.
Configuring the SqlDataSource Control Using the Wizard
When you click the "Configure Data Source" link a wizard appears, prompting you to select the data to retrieve. The first screen, shown below, prompts you to select the database from which the data will be queried. The drop-down list lists existing connection strings defined in the
Web.config
's <connectionStrings>
section and those in the App_Data
folder. To connect to a database registered somewhere else, click the New Connection button and specify the database server and authentication information. (For more information on working with the <connectionStrings>
section refer to an earlier article of mine, Working with Databases in ASP.NET 2.0 and Visual Studio 2005.) <connectionStrings>
defined for this database in Web.config
, the wizard will kindly do so for you, if you like. There are a plethora of advantages to defining your connection strings in Web.config
, the key one being that it makes your application more maintainable. Imagine that, down the road, you move database servers and need to update the connection string. If you have it defined in Web.config
, you have one place to update. If not, you have to find each and every page that references the connection string and update it accordingly. Furthermore, by placing your connection string information in Web.config
you can encrypt your connection strings to further protect your site. (However, the wizard won't automatically write to the <connectionStrings>
section if you have it encrypted.) The screenshot below shows this second step; as you can see, I've opted to save the connection string to Web.config
using the name NorthwindConnectionString
. Contrasting the AccessDataSource... |
---|
The AccessDataSource differs from the SqlDataSource in one minor aspect - rather than specifying a connection string as with the SqlDataSource, the AccessDataSource prompts for the path to the database file. Therefore, the screen shots above are different for the AccessDataSource. Specifically, the first screen shot examined prompts for a file location; since there is no connection string, the second screen shot does not appear in the AccessDataSource. However, the remaining screen shots are identical for both the AccessDataSource and SqlDataSource. |
On the next screen you are asked to choose your
SELECT
statement. As you can see by the two radio buttons at the top of the screen you can either pick a table or view and select the columns to return (as shown in the screen shot below), or write a custom SQL statement or pick a stored procedure. If you opt to specify columns from a table or view, simply pick the table or view from the drop-down list and then check those columns you want to return. Here you can see that I'm returning the ProductID
, ProductName
, UnitPrice
, and UnitsInStock
columns from the Products
table. This query will return all records from the Products
table; we'll see how to filter the returned records in a future article. ORDER BY
clause to the SELECT
statement. JOIN
s. Or the data to retrieve might be accessible from a stored procedure. In these cases we must select the "Specify a custom SQL statement of stored procedure" radio button and click Next. Doing so will take us to the screen shot shown below. Here you can type in a SQL query by hand into the SELECT
tab, or pick a stored procedure from those listed in the drop-down list. JOIN
syntax. The screen shot below shows the Query Builder in action. As you can see, I've added two tables to the query window - Categories
and Products
- and checked those columns from the tables I want to return. I also specified that the results should be ordered first by the CategoryName
column, and then by ProductName
. With just a few mouse clicks the Query Builder has kindly created the SELECT
query. SELECT
statement - either by selecting a table or view or by picking a stored procedure or writing a custom SQL statement, click the Next button to reach the final screen. Here you can run the query you've configured against the database and observe the results. To complete the wizard, click the Finish button. Configuring the SqlDataSource Through the Properties Pane
Prior to ASP.NET 2.0 I was firmly in the camp "doing things by hand." I didn't like wizards, I felt that they abstracted away too much and were too often used as a crutch and enabled developers to be able to achieve some goal without really learning what it was they just did. This fact often jumps up and bites the head of the wizard-using developer when something goes wrong. Since they've relied on the wizard, they don't know how to fix whatever bug or problem has surfaced. I still have that overall feeling about wizards, in general, but have really been turned on to the wizards in Visual Studio 2005. Partly because they save so much time, but primarily because all they're doing is setting properties. They're not writing oodles of code on your behalf, which is when the wizards become dangerous. However, if you're still a fan of not using wizards, that's quite all right, as configuring the SqlDataSource through its properties is also a walk in the park. Simply set the following properties, either through the Properties pane or as declarative markup:
ConnectionString
- Set this to your database's connection string. This can either be a fully-qualified connection string or a reference to a connection string name in theWeb.config
's<connectionStrings>
section. If it's a reference to aWeb.config
connection string use the syntax:ConnectionString="<%$ ConnectionStrings:ConnectionStringName %>"
. For the AccessDataSource control set theDataFile
property to the path to the Access database file, like~/App_Data/Northwind.mdb
.ProviderName
- the name of the database provider to use. By default, the SqlDataSource uses the SqlClient provider, which is a provider designed specially for Microsoft SQL Server. If you are using a different database, set this property accordingly. The .NET Framework ships with four database providers:- System.Data.SqlClient - used for fast access to Microsoft SQL Server (the default)
- System.Data.OleDb - used for accessing OLE-DB-compliant data stores, such as Access
- System.Data.Odbc - used for accessing older, ODBC-compliant data stores
- System.Data.OracleClient - used for fast access to Oracle databases
Web.config
you can use the following syntax to set this property to the value defined there:ProviderName="<%$ ConnectionStrings:ConnectionStringName.ProviderName %>"
SelectCommand
- theSELECT
query to issue to the database, such asSELECT [ProductID], [ProductName] FROM [Products] ORDER BY [ProductName]
.
Retrieving the Data from a SqlDataSource Control
Typically, when accessing data from a database we want to display or process it in some manner. To simply display the data, use any data Web control (such as the GridView) and set the data Web control's
DataSourceID
property to the ID
of the SqlDataSource. (This can most easily be done through the data Web control's smart tag in the Design view. There you'll find a drop-down list from which you can select the SqlDataSource control to bind to the data Web control.) When the page is visited, the data Web control will automatically grab the data from the associated SqlDataSource control and display it. There's a demo of doing just this in this article's download. The screen shot below shows the GridView when displaying the ProductID
, ProductName
, UnitPrice
, and UnitsInStock
columns from the Products
table ordered by UnitPrice
ascending with ties broken by the alphabetic sorting of ProductName
. - DataReaders - a DataReader offers forward-only, read-only access to a database, and requires an active connection. More efficient than a DataSets/DataTables/DataViews.
- DataSet/DataTable/DataView - a DataTable offers a random access, editable representation of a query. A DataSet is a collection of DataTables. DataViews can be used to retrieve a filtered or sorted subset of records in a DataTable.
DataSourceMode
property, with the default being DataSet. In order to utilize the GridView's built-in sorting and paging capabilities, or the SqlDataSource's caching capabilities (another topic for a future article!) you'll need to have the SqlDataSource return a DataSet. (The AccessDataSource control also has the DataSourceMode
property.) To programmatically access the contents of a SqlDataSource control call the
Select()
method. This method expects a single input parameter of type DataSourceSelectArguments
. This parameter can be used to pass along requests for the SqlDataSource to message the data before returning it. For example, when working with a sortable GridView, sorting a column calls the Select()
method passing in a DataSourceSelectArguments
instance with its SortExpression
property set to the column name the end user sorted the data by. If you don't want the SqlDataSource to sort, filter, or return only a "page" of data, simply pass in DataSourceSelectArguments.Empty
, as shown in the code example below. The
Select()
method returns an object that implements IEnumerable
. Specifically, it returns a DataView
if the DataSourceMode
property is set to DataSet, and a DataReader object if its set to DataReader. The following two examples show how to programmatically access the data from a SqlDataSource. The first one, written in VB, returns a DataSet and then is iterated through, displaying a random subset of results. The second example, written in C#, uses a SqlDataSource that returns a scalar value (the average price of all products) and displays that in a Label Web control on the page. HTML Portion<asp:Label ID="RandomProductNames" runat="server"></asp:Label> <asp:SqlDataSource ID="rndProductsDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>" SelectCommand="SELECT [ProductID], [ProductName] FROM [Products]"> </asp:SqlDataSource> Page_Load Event Handler'Programmatically access the SqlDataSource - get back a DataView Dim dv As DataView = _ CType(rndProductsDataSource.Select(DataSourceSelectArguments.Empty), DataView) Dim rndCoinFlip As New Random RandomProductNames.Text = String.Empty For Each dr As DataRow In dv.Table.Rows 'See if we want to show this If rndCoinFlip.Next(2) = 0 Then 'Show RandomProductNames.Text &= _ dr("ProductName").ToString() & "<br />" Else 'Don't show End If Next |
HTML PortionThe average price of all products in the Products database table is: <asp:Label ID="AvgPrice" runat="server" ForeColor="Red"></asp:Label> <asp:SqlDataSource ID="avgPriceDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>" SelectCommand="SELECT AVG(UnitPrice) FROM Products"> </asp:SqlDataSource> Page_Load Event Handler// Retreive the results from the SqlDataSource as a DataReader OleDbDataReader reader = (OleDbDataReader) avgPriceDataSource.Select(DataSourceSelectArguments.Empty); // Read in the value if (reader.Read()) // Format the price as a currency AvgPrice.Text = string.Format("{0:c}", reader[0]); else // Whoops, there were no results! AvgPrice.Text = "I don't know!"; // Close the reader reader.Close(); |
Note that in both examples I explicitly cast the return value from the
Select()
method either into a DataView or OleDbDataReader
object, depending on the SqlDataSource's DataSourceMode
property. (Since DataReaders are provider-specific objects, if I was using the SqlClient provider I'd be using a SqlDataReader
instead; if I was using the OracleProvider, I'd be using an OracleDataReader
, and so on.) Conclusion
In this installment we saw the basics of accessing data from databases using the SqlDataSource and AccessDataSource controls. While we only looked at retrieving all records from a table, we'll turn our attention to more useful SqlDataSource tasks in future articles. When querying data with the SqlDataSource control you can indicate the database to connect to and the data to return via the "Configure Data Source" wizard, which is accessible through the SqlDataSource control's smart tag. Once the Data Source control has been configured, the data can be displayed by binding it to a data Web control or processed programmatically by calling the SqlDataSource control's
Select()
method. The precise code you use for programmatically working with a SqlDataSource control's data depends on how the data is returned (which depends on the DataSourceMode
property's value).