Professional ASP.NET 1.1 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Professional ASP.NET 1.1 [Electronic resources] - نسخه متنی

Alex Homeret

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








Common Data Access Tasks with .NET


To demonstrate the basics of working with relational data in .NET, we've put together a series of sample pages that show the various objects in action. Figure 8-7 shows the


default main menu page for the samples:





Figure 8-7:


You can download the samples to run on your own server at http://www.daveandal.net/books/8900/. You can also run many of them online at the same URL. The samples are available in both VB and C#, and you can choose which to install – or install both sets.


The examples for this chapter are in the Introduction to Relational Data Access in .NET section, and this link displays the


default page for these sample pages, as shown in Figure 8-8.





Figure 8-8:


The first three groups of links show the three basic techniques for accessing relational data. Each group demonstrates four different connection types: an OLEDB provider for SQL Server, a direct SQL Server TDS connection, a connection through the .NET ODBC driver, and a connection to an Access database file through the Jet provider for Microsoft Access. There is also an example of using a user control that returns a


DataSet . We'll be examining all these sample pages.



Setting Up the Samples on Your System



The downloadable sample files contain a


WroxBooks.mdb Access database, which you can use with the Jet examples, and a set of SQL scripts that you can use to create the sample


WroxBooks database on your own local SQL Server. Instructions for using the scripts are in the


readme.txt file located within the


database folder of the samples.


You'll also need to edit the connection strings in the


web.config file that is installed in the root folder of the examples to suit your setup. The


<appSettings> section of the


web.config file contains declarations of the connection strings for all of the examples for this book, but the ones that are relevant to this chapter are highlighted in the following code. Notice that there are four, one for each of the providers/drivers used in the example pages:


<configuration>
... other settings here ...
<appSettings>


<add key="DsnWroxBooksSql"


value="server=delmonte; database=WroxBooks; user id=sa; password=" />


<add key="DsnWroxBooksOleDb"


value="provider=SQLOLEDB.1; data source=delmonte;


initial catalog=WroxBooks; uid=sa; pw=" />


<add key="DsnWroxBooksJet"


value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" />


<add key="DsnWroxBooksOdbc"


value="DRIVER={SQL Server}; SERVER=delmonte;


DATABASE=WroxBooks; uid=sa; pw=;" />
... other settings here ...
</appSettings>
</configuration>



Any page within the samples can access and use these connection strings by using:




strSQLConnect = ConfigurationSettings.AppSettings("DsnWroxBooksSql")


strOLEDBConnect = ConfigurationSettings.AppSettings("DsnWroxBooksOleDb")


strJetConnect = ConfigurationSettings.AppSettings("DsnWroxBooksJet")


strOdbcConnect = ConfigurationSettings.AppSettings("DsnWroxBooksOdbc")



Setting Up the Required File Access Permissions



Some of the examples files require write access to the server's


wwwroot folder and subfolders below this. By default in Windows NT, Windows 2000, and Windows XP, ASP.NET runs under the context of the ASPNET account that is created by the installation and setup of the .NET Framework. This is a relatively unprivileged account that has similar permissions by default as the IUSR_machinename account that is used by Internet Information Services.


To give folders on your test server write access for ASP.NET, right-click on the


wwwroot folder in Windows Explorer and open the Properties dialog. In the Security tab, select the ASPNET account and give it Write permission or Full Control. Then click Advanced and tick the checkbox at the bottom of this page (Reset permissions on all child objects…).


Alternatively, configure ASP.NET to run under the context of the local System account by editing the


machine.config file located in the


config directory of the installation root. By default, this is the


C:\WINNT\Microsoft.NET\Framework\[version]\CONFIG\ directory. Change just the


userName attribute in the


<processModel> element within the


<system.web> section of this file to:




<processModel userName="system" password="autogenerate" ... />







Important


Do this only while experimenting and only on a development server. For production servers, set up only the minimal permissions required for your applications to run.





ASP.NET with IIS 6.0 and Windows Server 2003



While all the this is true for IIS 4.0 and IIS 5.0, as installed with Windows NT, Windows 2000, and Windows XP, the new version of IIS supplied with Windows Server 2003 (IIS 6.0) works in a slightly different way. Security and account permissions are discussed in Chapter 14. However, to enable the example pages to run on Windows Server 2003 you only need to know the basics here.


By default, in Windows Server 2003, web sites run within Application Pools and the worker processes used for accessing resources run under the context of an account named NETWORK SERVICE. Windows Server 2003 creates an account group called IIS_WPG, of which the IWAM_machinename, LOCAL SERVICE, NETWORK SERVICE and SYSTEM accounts are automatically members.


It means that you can use this group to configure access to resources for ASP.NET running under the default IIS 6.0 configuration. Alternatively, you can just assign the necessary Write permission directly to the NETWORK SERVICE account if you prefer more fine-grained control.


You can also configure IIS 6.0 to run in a special compatibility mode called IIS 5.0 Isolation Mode (in the Service tab of the Properties dialog for the Web Sites entry in the IIS Manager). In this case, IIS 6.0 runs ASP.NET just like it does under IIS 5.0, and the accounts used and permission settings you make are the same as in IIS 5.0.


So, the ASPNET account is used for ASP.NET resources, and the IWAM_machinename account is used for other resources. And an account named IWAM_machinename is used for out-of-process execution of components in this mode, just as in IIS 5.0.






Note


For more information of the IIS and ASP.NET security model in Windows Server 2003, open the Help file from IIS Manager and navigate to the Server Administration Guide | Security | Access Control | Web Site Permissions section.





Using a DataReader Object



The first group of links in the relational data access menu shows the


DataReader in action. This is the nearest equivalent to the


Connection /


Recordset data access technique used in traditional ADO. Figure 8-9 shows the result of running the OLEDB example. The others from the same group (the SQL TDS, ODBC and Jet provider examples) provide identical output, but with different connection strings.





Figure 8-9:


The code in the page (


datareader-oledb.aspx ) is placed within the


Page_Load event handler. So, it runs when the page loads. The code inserts the connection string, SQL


SELECT statement, and the results into


<div> elements within page. All the code is fully commented, and we've included elementary error handling to display any errors. However, only the relevant data access code has been shown here. You can examine the entire source code for any of the pages by clicking the [view source] link at the bottom.


The DataReader Example Code



The following code has been used in this example:




'get connection string from web.config file and display it


strConnect = ConfigurationSettings.AppSettings("DsnWroxBooksOleDb")


outConnect.InnerText = strConnect




'specify the SELECT statement to extract the data and display it


strSelect = "SELECT * FROM BookList WHERE ISBN LIKE '07645437%'"


outSelect.InnerText = strSelect




'create a new Connection object using the connection string


Dim objConnect As New OleDbConnection(strConnect)




'open the connection to the database


objConnect.Open()




'create a new Command using the connection object and select statement


Dim objCommand As New OleDbCommand(strSelect, objConnect)




'declare a variable to hold a DataReader object


Dim objDataReader As OleDbDataReader




'execute the SQL statement against the command to fill the DataReader


objDataReader = objCommand.ExecuteReader()



The first step is to get the connection string from the


web.config file, and then specify the SQL statement. These are displayed as the code runs in


<div> elements named


outConnect and


outSelect (located within the HTML of the page). Then, we create a new instance of an


OleDbConnection object, specifying the connection string as the single parameter of its constructor.


After opening the connection by calling the


Open method, you need an


OleDbCommand object. This will be used to execute the statement and return a new


OleDbDataReader object through which you can access the results of the query. The SQL statement and the active


Connection object are specified as the parameters to the


OleDbCommand object constructor: You can then call the


ExecuteReader method of the


OleDbCommand object. This returns an


OleDbDataReader object that is connected to the result rowset.


Displaying the Results



A


DataReader allows you to iterate through the results of a SQL query, much like you do with a traditional ADO


Recordset object. However, unlike in the ADO


Recordset , in a


DataReader you must call the


Read method first to be able to access the first row of the results. Afterwards, just call the


Read method repeatedly to get the next row of the results until it returns


False (which indicates that the end of the results set has been reached).






Note


We no longer have a


MoveNext method. Forgetting to include this statement was found by testers to be the most common reason for problems when working with the


Recordset object in ADO.




As was common practice in ASP 3.0 and earlier, you can build up an HTML


<table> to display the data. However, as you're working with ASP.NET now, this example actually creates the definition of the table as a string and then inserts it into a


<div> element elsewhere in the page (rather than the ASP-style technique of using


Response.Write directly). The following code was used to create the output shown in Figure 8-9:




Dim strResult As String = "<table>"




'iterate through the records in the DataReader getting field values


'the Read method returns False when there are no more records


Do While objDataReader.Read()


strResult += "<tr><td>" & objDataReader("ISBN") & "</td><td> &nbsp;" _


& objDataReader("Title") & "</td><td> &nbsp;" _


& objDataReader("PublicationDate") & "</td><td></tr>"


Loop




'close the DataReader and Connection


objDataReader.Close()


objConnect.Close()




'add closing table tag and display the results


strResult += "</table>"


outResult.InnerHtml = strResult







Note


You could, of course, simply declare an ASP.NET list control such as a


DataGrid in the page, and then bind the


DataReader to the control to display the results. However, the technique used here to display the data demonstrates how we can iterate through the rowset.




Closing the DataReader and the Connection



You have to explicitly close the


DataReader . You also have to explicitly close the connection by calling the


Connection object's


Close method. Although the garbage collection process will close the


DataReader when it destroys the object in memory after the page ends, it's good practice to always close reader objects connections as soon as you are finished with them.


It's even more important to close the connection after you finish with it. Database connections are a precious resource, and the number available is usually limited. For this reason, as you'll see in the next section, ADO.NET provides a useful method that will close a connection automatically.


The CommandBehavior Enumeration


One useful technique to bear in mind when using a


DataReader is to take advantage of the optional parameter for the


Command object's


ExecuteReader method. It can be used to force the connection to be closed automatically as soon as we call the


Close method of the


DataReader object:




objDataReader = objCommand.ExecuteReader(CommandBehavior.CloseConnection)



This is particularly useful if you pass a reference to the


DataReader to another routine, say if you return it from a method. By using the


CommandBehavior.CloseConnection option, you can be sure that the connection will be closed automatically when the routine using the


DataReader destroys the object reference.


Other values in the


CommandBehavior enumeration that you can use with the


ExecuteReader method (multiple values can be used with


And or


+ ) are:





SchemaOnly : The execution of the query will only return the schema (column information) for the results set, and not any data. It can be used, for example, to find the number of columns in the results set.





SequentialAccess : Can be used to allow the


DataReader to access large volumes of binary data from a column. The data is accessed as a stream rather than as individual rows and columns, and is retrieved using the


GetBytes or


GetChars methods of the


DataReader .





SingleResult : Useful if the query is only expected to return a single value, and can help the database to fine-tune the query execution for maximum efficiency. Alternatively, use the


ExecuteScalar method of the


Command object.





SingleRow : Useful if the query is only expected to return one row, and can help the database to fine-tune the query execution for maximum efficiency.





Overall, the techniques used in this example are not that far removed from working with traditional ADO in ASP. However, there are far more opportunities available in .NET for accessing and using relational data. These revolve around the


DataSet rather than the


DataReader .



A Simple DataSet Example



A


DataSet is a disconnected read/write container for holding one or more tables of data, and the relationships between these tables. In this example, we just extract a single table from the database and display the contents.


Figure 8-10 shows what the Simple DataSet object example using an OLEDB Provider (
simple-dataset oledb.aspx ) sample looks like when it's run:





Figure 8-10:


The Simple DataSet Example Code



We've used the same connection string and SQL statement as in the


DataReader example. We also create a new


OleDbConnection object using this connection string as before:




Dim objConnect As New OleDbConnection(strConnect)



To execute the SQL statement for the


OleDbDataReader object in the previous example, we used the


ExecuteReader method of the


OleDbCommand object. In this example, to fill a


DataSet object with data, we use an alternative object to specify the SQL statement – an


OleDbDataAdapter object. Again, we provide the SQL statement and the active


Connection object as the parameters to the object constructor:




Dim objDataAdapter As New OleDbDataAdapter(strSelect, objConnect)







Note


This technique still creates and uses a


Command object. When you create a


DataAdapter , a suitable


Command instance is created automatically behind the scenes, and assigned to the


SelectCommand property of your


DataAdapter . You could do this yourself, but it would mean writing the extra code, and there is no advantage in doing so.




Now create an instance of a


DataSet object and then fill it with data from the data source by calling the


Fill method of the


DataAdapter object. Specify as parameters the


DataSet object and the name of the source table in the database:




Dim objDataSet As New DataSet()


objDataAdapter.Fill(objDataSet, "Books")



Filling the Schema in a DataSet



The


Fill method of the


DataAdapter object that was used here creates the table in the


DataSet , and then creates the appropriate columns and sets the data type and certain constraints such as the column width (the number of characters). It doesn't automatically set the primary keys, unique constraints, read- only values, and defaults. However, you can call the


FillSchema method first (before you call


Fill ) to copy these settings from the data source into the table:




objDataAdapter.FillSchema(objDataSet, SchemaType.Mapped)



After all this, you've now got a disconnected


DataSet object that contains the results of the SQL query. The next step is to display that data.


Displaying the Results



In this and many of the other examples, we're using an ASP


DataGrid control to display the data in the


DataSet object. You saw how the


DataGrid control works in Chapter 7:




<asp:datagrid id="dgrResult" runat="server" />



However, you can't simply bind the


DataSet object directly to a


DataGrid and have the correct rows displayed, as a


DataSet can contain multiple tables. One solution is to create a


DataView based on the table you want to display, and bind the


DataView object to the


DataGrid . You get the default


DataView object for a table by accessing the


Tables collection of the


DataSet and specifying the table name:




Dim objDataView As New DataView(objDataSet.Tables("Books"))



Then, assign the


DataView to the


DataSource property of the


DataGrid , and call the


DataBind method to display the data:




dgrResult.DataSource = objDataView


dgrResult.DataBind()



However, it's actually better performance-wise, though not as clear when you read the code, to perform the complete property assignment in one statement:




dgrResult.DataSource = objDataSet.Tables("Books").DefaultView



There is also a third option, as the ASP.NET Server Controls provide a


DataMember property that defines which table or other item in the data source will supply the data. So you could use:




dgrResult.DataSource = objDataSet


dgrResult.DataMember = "Books"



We use a mixture of techniques in our examples.



A Multiple Tables DataSet Example



Having seen how to use a


DataSet to hold one results table, you'll now see how to add multiple tables to a


DataSet object. The Multiple tables DataSet object example using an OLEDB Provider (


multiple- dataset-oledb.aspx ) example creates a


DataSet object and fills it with three tables. It also creates relationships between these tables.


As you can see in Figure 8-11, the page displays the connection string and the three SQL statements that extract the data from three tables in the database. Following this are two


DataGrid controls showing the contents of the


DataSet object's


Tables collection and


Relations collection. Further down the page (not visible here) are two more


DataGrid controls, which show the related data that is contained in the


Authors and


Prices tables within the


DataSet .





Figure 8-11:


The Multiple Tables DataSet Example Code



While the principle for this example is similar to the previous Simple DataSet example, the way we've coded it is subtly different. We've demonstrated another way of using the


Command and


DataAdapter objects.


As before, first create a


Connection object using your connection string, shown in the following code. However, this time create a


Command object next using the default constructor with no parameters, and then set the properties of the


Command object in a way similar to that used in traditional ADO.


Specify the connection string, the command type (in this case


Text , as a SQL statement is being used), and the SQL statement itself for the


CommandText property. By doing it this way, you can change the SQL statement later to get a different set of rows from the database without having to create a new


Command object.




'create a new Connection object using the connection string


Dim objConnect As New OleDbConnection(strConnect)


'create a new Command object


Dim objCommand As New OleDbCommand()


'set the properties


objCommand.Connection = objConnect


objCommand.CommandType = CommandType.Text


objCommand.CommandText = strSelectBooks



Once you have a


Command object, you can use it within a


DataAdapter . You need a


DataAdapter to extract the data from the database and squirt it into your


DataSet object. After creating the


DataAdapter , assign the


Command object to its


SelectCommand property. This


Command will then be used when you call the


Fill method to get the data:


So, you've got a valid


DataAdapter object, and you can set about filling your


DataSet . Call the


Fill method three times, once for each table you want to insert into it. In between, you have to change the


CommandText property of the active


Command object to the appropriate SQL statement, as shown in the following code:




'create a new DataAdapter object


Dim objDataAdapter As New OleDbDataAdapter()


'and assign the Command object to it


objDataAdapter.SelectCommand = objCommand




'get the data from the "BookList" table in the database and


'put it into a table named "Books" in the DataSet object


objDataAdapter.Fill(objDataSet, "Books")




'change the SELECT statement in the Command object


objCommand.CommandText = strSelectAuthors


'then get data from "BookAuthors" table into the DataSet


objDataAdapter.Fill(objDataSet, "Authors")




'and do the same again to get the "BookPrices" data


objCommand.CommandText = strSelectPrices


objDataAdapter.Fill(objDataSet, "Prices")



Opening and Closing Connections with the DataAdapter



In the examples that use a


DataAdapter , we haven't explicitly opened or closed the connection. This is because the


DataAdapter looks after this automatically. If the connection is closed when the


Fill method is called, it is opened, the rows are extracted from the data source and pushed into the


DataSet , and the connection is automatically closed again.


However, if the connection is open when the


Fill method is called, the


DataAdapter will leave it open after the method has completed. This provides you with a useful opportunity to maximize performance by preventing the connection being opened and closed each time you call


Fill (if you are loading more than one table in the


DataSet ). Just open the connection explicitly before the first call, and close it again after the last one, as shown by the highlighted lines in the following code:


Dim objDataSet As New DataSet()
objCommand.CommandText = strSelectBooks


objConnect.Open()
objDataAdapter.Fill(objDataSet, "Books")
objCommand.CommandText = strSelectAuthors
objDataAdapter.Fill(objDataSet, "Authors")
objCommand.CommandText = strSelectPrices
objDataAdapter.Fill(objDataSet, "Prices")


objConnect.Close()



Adding Relationships to the DataSet



You've got three tables in your


DataSet , and can now create the relationships between them. Define a variable to hold a


DataRelation object and create a new


DataRelation by specifying the name you want for the relation (
BookAuthors ), the name of the primary key field (
ISBN ) in the parent table named
Books , and the name of the foreign key field (
ISBN ) in the
Authors child table.


Then add the new relation to the


DataSet object's


Relations collection, and do the same to create the relation between the


Books and


Prices tables in the


DataSet . As the relations are added to the


DataSet , an integrity check is carried out automatically. If, for example, there is a child record that has no matching parent record, an error is raised and the relation is not added to the


DataSet .




'declare a variable to hold a DataRelation object


Dim objRelation As DataRelation




'create a Relation object to link Books and Authors


objRelation = New DataRelation("BookAuthors", _


objDataSet.Tables("Books").Columns("ISBN"), _


objDataSet.Tables("Authors").Columns("ISBN"))


'and add it to the DataSet object's Relations collection


objDataSet.Relations.Add(objRelation)




'now do the same to link Books and Prices


objRelation = New DataRelation("BookPrices", _


objDataSet.Tables("Books").Columns("ISBN"), _


objDataSet.Tables("Prices").Columns("ISBN"))


objDataSet.Relations.Add(objRelation)



Displaying the Results



Having filled the


DataSet with three tables and two relations, you can now display the results. You use five


DataGrid controls to do this, as shown in the following code listing. The


DataSet object's


Tables and


Relations collections are bound directly to the first two


DataGrid controls, and for the tables within the


DataSet , we assign the


DataView returned by the


DefaultView property of the tables to the remaining three


DataGrid controls.




'bind the collection of Tables to the first DataGrid on the page


dgrTables.DataSource = objDataSet.Tables


dgrTables.DataBind()




'bind the collection of Relations to the second DataGrid on the page


dgrRelations.DataSource = objDataSet.Relations


dgrRelations.DataBind()




'create a DataView object to use with the tables in the DataSet


Dim objDataView As New DataView()




'get the default view of the Books table into the DataView object


objDataView = objDataSet.Tables("Books").DefaultView


'and bind it to the third DataGrid on the page


dgrBooksData.DataSource = objDataView


dgrBooksData.DataBind()


'then do the same for the Authors table


objDataView = objDataSet.Tables("Authors").DefaultView


dgrAuthorsData.DataSource = objDataView


dgrAuthorsData.DataBind()


'and finally do the same for the Prices table


objDataView = objDataSet.Tables("Prices").DefaultView


dgrPricesData.DataSource = objDataView


dgrPricesData.DataBind()




A User Control That Returns a DataSet Object



The preceding code is used in several examples in this and subsequent chapters, and to make it easier we've encapsulated it as a user control that returns a fully populated


DataSet . Change the page's file extension to


.ascx and change the


Page directive to a


Control directive:




<%@Control Language="VB"%>



Then, instead of placing the code in the


Page_Load event handler, place it in a


Public Function to which you provide the connection string and the


WHERE clause for the SQL statement as parameters. The function returns a


DataSet object, as shown in the following code. Note that the parameters passed to this function allow you to select a different set of books by varying the


strWhere parameter value when you use the control.




Public Function BooksDataSet(strConnect As String, _


strWhere As String) _


As DataSet


...


strSelectBooks = "SELECT * FROM BookList WHERE " & strWhere


strSelectAuthors = "SELECT * FROM BookAuthors WHERE " & strWhere


strSelectPrices = "SELECT * FROM BookPrices WHERE " & strWhere


Dim objDataSet As New DataSet()


...


... code to fill DataSet as before ...


...


Return objDataSet


End Function



The Using a control that creates and returns a DataSet object (


use-dataset-control.aspx ) example page contains the


Register directive and matching element to insert the user control containing the function just described into the page. Then, to get a


DataSet from the control, just create a variable of the correct type and set it to the result of the


BooksDataSet method – specifying the values for the connection string and


WHERE clause parameters when you make the call.




<%@ Register TagPrefix="wrox" TagName="getdataset"


Src=" %>


...


<wrox:getdataset id="ctlDataSet" runat="server"/>


Dim objDataSet As DataSet


objDataSet = ctlDataSet.BooksDataSet(strConnect, "ISBN LIKE '0764544%'")



The investigation of the


DataSet object will be continued in Chapters 9 and 10. You'll see how to use more complex data sets, and update and edit data using the ADO.NET relational data access classes. We'll also explore the ways that .NET combines the traditional relational database access techniques with the more recent developments in XML-based data storage and management.


/ 244