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

Apress-Visual CSharp 2010 Recipes A Problem Solution Approach_4 potx

95 400 0
Tài liệu đã được kiểm tra trùng lặp

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

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Database Access
Trường học Apress
Chuyên ngành Computer Science
Thể loại Recipe
Năm xuất bản 2010
Thành phố New York
Định dạng
Số trang 95
Dung lượng 2 MB

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

Nội dung

How It Works Using a specific data provider implementation the SQL Server data provider, for example simplifies your code, and may be appropriate if you need to support only a single ty

Trang 1

// Create a new SqlConnection object

using (SqlConnection con = new SqlConnection())

{

// Configure the SqlConnection object's connection string

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI";

// Create and configure a new command that includes the

// FOR XML AUTO clause

using (SqlCommand com = con.CreateCommand())

{

com.CommandType = CommandType.Text;

com.CommandText = "SELECT CustomerID, CompanyName" +

" FROM Customers FOR XML AUTO";

// Open the database connection

Trang 2

451

// Move the XmlReader back to the element node

reader.MoveToElement();

Console.WriteLine(Environment.NewLine);

}

}

}

}

}

}

public static void DisconnectedExample() {

XmlDocument doc = new XmlDocument(); // Create a new SqlConnection object using (SqlConnection con = new SqlConnection()) {

// Configure the SqlConnection object's connection string con.ConnectionString = @"Data Source = \sqlexpress;" + "Database = Northwind; Integrated Security=SSPI"; // Create and configure a new command that includes the // FOR XML AUTO clause SqlCommand com = con.CreateCommand(); com.CommandType = CommandType.Text; com.CommandText = "SELECT CustomerID, CompanyName FROM Customers FOR XML AUTO"; // Open the database connection con.Open(); // Load the XML data into the XmlDocument Must first create a // root element into which to place each result row element XmlReader reader = com.ExecuteXmlReader(); doc.LoadXml("<results></results>"); // Create an XmlNode from the next XML element read from the // reader XmlNode newNode = doc.ReadNode(reader); while (newNode != null) {

doc.DocumentElement.AppendChild(newNode); newNode = doc.ReadNode(reader); }

}

// Process the disconnected XmlDocument Console.WriteLine(doc.OuterXml); }

Trang 3

Use the BeginExecuteNonQuery, BeginExecuteReader, or BeginExecuteXmlReader method of the

System.Data.SqlClient.SqlCommand class to start the database operation as a background task These

methods all return a System.IAsyncResult object that you can use to determine the operation’s status or use thread synchronization to wait for completion Use the IAsyncResult object and the corresponding

EndExecuteNonQuery, EndExecuteReader, or EndExecuteXmlReader method to obtain the result of the

Trang 4

453

usually require the result of the operation before it can continue However, sometimes it’s useful to

execute a database operation asynchronously, meaning that you start the method in a separate thread and then continue with other operations

■ Note To execute asynchronous operations over a System.Data.SqlClient.SqlConnection connection, you

must specify the value Asynchronous Processing=true in its connection string

The SqlCommand class implements the asynchronous execution pattern similar to that discussed in

recipe 4-2 As with the general asynchronous execution pattern described in recipe 4-2, the arguments of

the asynchronous execution methods (BeginExecuteNonQuery, BeginExecuteReader, and

BeginExecuteXmlReader) are the same as those of the synchronous variants (ExecuteNonQuery,

ExecuteReader, and ExecuteXmlReader), but they take the following two additional arguments to support

asynchronous completion:

A System.AsyncCallback delegate instance that references a method that the

runtime will call when the asynchronous operation completes The method is

executed in the context of a thread-pool thread Passing null means that no

method is called and you must use another completion mechanism (discussed

later in this recipe) to determine when the asynchronous operation is complete

An object reference that the runtime associates with the asynchronous operation

The asynchronous operation does not use nor have access to this object, but it’s

available to your code when the operation completes, allowing you to associate

useful state information with an asynchronous operation For example, this object

allows you to map results against initiated operations in situations where you

initiate many asynchronous operations that use a common callback method to

perform completion

The EndExecuteNonQuery, EndExecuteReader, and EndExecuteXmlReader methods allow you to retrieve

the return value of an operation that was executed asynchronously, but you must first determine when it has finished Here are the four techniques for determining if an asynchronous method has finished:

Blocking: This method stops the execution of the current thread until the

asynchronous operation completes execution In effect, this is much the same as

synchronous execution However, in this case, you have the flexibility to decide

exactly when your code enters the blocked state, giving you the opportunity to

carry out some additional processing before blocking

Polling: This method involves repeatedly testing the state of an asynchronous

operation to determine whether it’s complete This is a very simple technique and

is not particularly efficient from a processing perspective You should avoid tight

loops that consume processor time It’s best to put the polling thread to sleep for a

period using Thread.Sleep between completion tests Because polling involves

maintaining a loop, the actions of the waiting thread are limited, but you can

easily update some kind of progress indicator

Trang 5

454

Waiting: This method uses an object derived from the

System.Threading.WaitHandle class to signal when the asynchronous method

completes Waiting is a more efficient version of polling and in addition allows you to wait for multiple asynchronous operations to complete You can also specify timeout values to allow your waiting thread to fail if the asynchronous operation takes too long, or if you want to periodically update a status indicator

Callback: This a method that the runtime calls when an asynchronous operation

completes The calling code does not need to take any steps to determine when the asynchronous operation is complete and is free to continue with other processing Callbacks provide the greatest flexibility, but also introduce the greatest complexity, especially if you have many concurrently active asynchronous operations that all use the same callback In such cases, you must use appropriate state objects to match completed methods against those you initiated

■ Caution When using the asynchronous capabilities of the SQL Server data provider, you must ensure that your code does not inadvertently dispose of objects that are still being used by other threads Pay particular attention to

SqlConnection and SqlCommand objects

The Code

Recipe 4-2 provides examples of all of the completion techniques summarized in the preceding list The following example demonstrates the use of an asynchronous call to execute a stored procedure on a SQL Server database The code uses a callback to process the returned result set

// A method to handle asynchronous completion using callbacks

public static void CallbackHandler(IAsyncResult result)

// Obtain the result of the stored procedure

using (SqlDataReader reader = cmd.EndExecuteReader(result))

{

Trang 6

455

// Display the results of the stored procedure to the console

lock (Console.Out)

{

Console.WriteLine( "Price of the Ten Most Expensive Products:"); while (reader.Read()) {

// Display the product details Console.WriteLine(" {0} = {1}", reader["TenMostExpensiveProducts"], reader["UnitPrice"]); }

}

}

}

}

public static void Main() {

// Create a new SqlConnection object using (SqlConnection con = new SqlConnection()) {

// Configure the SqlConnection object's connection string

// You must specify Asynchronous Processing=true to support

// asynchronous operations over the connection

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI;" +

"Asynchronous Processing=true";

// Create and configure a new command to run a stored procedure

// Do not wrap it in a using statement because the asynchronous

// completion handler will dispose of the SqlCommand object

SqlCommand cmd = con.CreateCommand();

cmd.CommandType = CommandType.StoredProcedure;

cmd.CommandText = "Ten Most Expensive Products";

// Open the database connection and execute the command

// asynchronously Pass the reference to the SqlCommand

// used to initiate the asynchronous operation

con.Open();

cmd.BeginExecuteReader(CallbackHandler, cmd);

Trang 7

456

// Continue with other processing

for (int count = 0; count < 10; count++)

Program to the ADO.NET data provider interfaces in the System.Data namespace, as opposed to the

concrete implementations, and do not rely on features and data types that are unique to specific database implementations Use factory classes and methods to instantiate the data provider objects you need to use

How It Works

Using a specific data provider implementation (the SQL Server data provider, for example) simplifies your code, and may be appropriate if you need to support only a single type of database or require access to specific features provided by that data provider, such as the asynchronous execution for SQL Server detailed in recipe 9-9 However, if you program your application against a specific data provider implementation, you will need to rewrite and test those sections of your code if you want to use a different data provider at some point in the future

Table 9-6 contains a summary of the main interfaces you must program against when writing generic ADO.NET code that will work with any relational database’s data provider The table also explains how to create objects of the appropriate type that implement the interface Many of the recipes

Trang 8

457

in this chapter demonstrate the use of ADO.NET data provider interfaces over specific implementation,

as highlighted in the table

Table 9-6 Data Provider Interfaces

IDbConnection Represents a connection to a relational database You must

program the logic to create a connection object of the appropriate type based on your application’s configuration

information, or use the DbProviderFactory.CreateConnection

factory method (discussed in this recipe)

Recipe 9-1

IDbCommand Represents a SQL command that is issued to a relational

database You can create IDbCommand objects of the appropriate type using the IDbConnection.CreateCommand or

DbProviderFactory.CreateCommand factory method

Recipe 9-5

IDataParameter Represents a parameter to an IDbCommand object You can create

IDataParameter objects of the correct type using the IDbCommand.CreateParameter, IDbCommand.Parameters.Add, or DbProviderFactory.CreateParameter factory method

Recipe 9-6

IDataReader Represents the result set of a database query and provides access

to the contained rows and columns An object of the correct type

will be returned when you call the IDbCommand.ExecuteReader

method

Recipes 9-5 and 9-6

IDbDataAdapter Represents the set of commands used to fill a

System.Data.DataSet from a relational database and to update

the database based on changes to the DataSet You must program

the logic to create a data adapter object of the appropriate type based on your application’s configuration information, or use the

DbProviderFactory.CreateAdapter factory method (discussed in

this recipe)

The System.Data.Common.DbProviderFactory class provides a set of factory methods for creating all

types of data provider objects, making it very useful for implementing generic database code Most

important, DbProviderFactory provides a mechanism for obtaining an initial IDbConnection instance,

which is the critical starting point for writing generic ADO.NET code Each of the standard data provider implementations (except the SQL Server CE data provider) includes a unique factory class derived from

DbProviderFactory Here is the list of DbProviderFactory subclasses:

Trang 9

You can obtain an instance of the appropriate DbProviderFactory subclass using the

DbProviderFactories class, which is effectively a factory of factories Each data provider factory is

described by configuration information in the machine.config file, similar to that shown here for the SQL

Server data adapter This can be changed or overridden by application-specific configuration

information if required

<configuration>

<system.data>

<DbProviderFactories>

<add name="SqlClient Data Provider" invariant="System.Data.SqlClient" ~CCC

description=".Net Framework Data Provider for SqlServer" type= ~CCC

"System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, ~CCC

Culture=neutral, PublicKeyToken=b77a5c561934e089" />

<add name="Odbc Data Provider" />

<add name="OleDb Data Provider" />

<add name="OracleClient Data Provider" />

<add name="SQL Server CE Data />

</DbProviderFactories>

</system.data>

</configuration>

You can enumerate the available data provider factories by calling DbProviderFactories

GetFactoryClasses, which returns a System.Data.DataTable containing the following columns:

Name, which contains a human-readable name for the provider factory Taken from

the name attribute in the configuration information

Description, which contains a human-readable description for the provider

factory Taken from the description attribute of the configuration information

InvariantName, which contains the unique name used to refer to the data provider

factory programmatically Taken from the invariant attribute of the configuration

information

AssemblyQualifiedName, which contains the fully qualified name of the

DbProviderFactory class for the data provider Taken from the type attribute of the

configuration information

Normally, you would allow the provider to be selected at install time or the first time the application

is run, and then store the settings as user or application configuration data The most important piece of

information is the InvariantName, which you pass to the DbProviderFactories.GetFactory method to obtain the DbProviderFactory implementation you will use to create your IDbConnection instances

Trang 10

459

The Code

The following example demonstrates the enumeration of all data providers configured for the local

machine and application It then uses the DbProviderFactories class to instantiate a DbProviderFactory object (actually a SqlClientFactory) from which it creates the appropriate IDbConnection It then uses

the factory methods of the data provider interfaces to create other required objects, resulting in code

that is completely generic

// Obtain the list of ADO.NET data providers registered in the

// machine and application configuration files

using (DataTable providers = DbProviderFactories.GetFactoryClasses())

{

// Enumerate the set of data providers and display details

Console.WriteLine("Available ADO.NET Data Providers:");

foreach (DataRow prov in providers.Rows)

// Obtain the DbProviderFactory for SQL Server The provider to use

// could be selected by the user or read from a configuration file

// In this case, we simply pass the invariant name

DbProviderFactory factory =

DbProviderFactories.GetFactory("System.Data.SqlClient");

// Use the DbProviderFactory to create the initial IDbConnection, and

// then the data provider interface factory methods for other objects

using (IDbConnection con = factory.CreateConnection())

{

// Normally, read the connection string from secure storage

// See recipe 9-3 In this case, use a default value

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI";

Trang 11

460

// Create and configure a new command

using (IDbCommand com = con.CreateCommand())

{

com.CommandType = CommandType.StoredProcedure; com.CommandText = "Ten Most Expensive Products"; // Open the connection con.Open(); // Execute the command and process the results using (IDataReader reader = com.ExecuteReader()) {

Console.WriteLine(Environment.NewLine); Console.WriteLine("Price of the Ten Most" + " Expensive Products."); while (reader.Read()) {

// Display the product details Console.WriteLine(" {0} = {1}", reader["TenMostExpensiveProducts"], reader["UnitPrice"]); }

}

}

}

// Wait to continue Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); }

}

}

9-11 Discover All Instances of SQL Server on Your Network Problem

You need to obtain a list of all instances of SQL Server that are accessible on the network

Solution

Use the GetDataSources method of the System.Data.Sql.SqlDataSourceEnumerator class

Trang 12

461

How It Works

The SqlDataSourceEnumerator class makes it easy to enumerate the SQL Server instances accessible on the network You simply obtain the singleton SqlDataSourceEnumerator instance via the static property

SqlDataSourceEnumerator.Instance and call its GetDataSources method The GetDataSources method

returns a System.Data.DataTable that contains a set of System.Data.DataRow objects Each DataRow

represents a single SQL Server instance and contains the following columns:

ServerName, which contains the name of the server where the SQL Server instance

is hosted

InstanceName, which contains the name of the SQL Server instance or the empty

string if the SQL Server is the default instance

IsClustered, which indicates whether the SQL Server instance is part of a cluster

Version, which contains the version of the SQL Server instance

The Code

The following example demonstrates the use of the SqlDataSourceEnumerator class to discover and

display details of all SQL Server instances accessible (and visible) on the network The IsClustered and

Version columns may be blank for some versions of SQL Server

// Obtain the DataTable of SQL Server instances

using (DataTable SqlSources =

SqlDataSourceEnumerator.Instance.GetDataSources())

{

// Enumerate the set of SQL Servers and display details

Console.WriteLine("Discover SQL Server Instances:");

foreach (DataRow source in SqlSources.Rows)

{

Console.WriteLine(" Server Name:{0}", source["ServerName"]);

Console.WriteLine(" Instance Name:{0}",

Trang 13

Use System.Data.DataSet to represent the data and System.Data.SqlClient.SqlDataAdapter to read and

sync data with the database

How It Works

The System.Data.DataSet class contains one or more instances of System.Data.DataTable, each of which contains instances of System.Data.DataRow, representing data rows from the database The

SqlDataAdapter class acts as the bridge between the database and the DataSet, allowing you to populate

the DataSet with data and write back any changes to the database when you are done The sequence for using a DataSet is as follows:

1 Create a SqlConnection to your database as normal (see recipe 9-1)

2 Create a new instance of DataSet using the default constructor

3 Create a new instance of SqlDataAdapter, passing in a query string for the data

you require and the SqlConnection you created in step 1 as constructor

arguments

4 Create an instance of SqlCommandBuilder, passing in the SqlDataAdapter you

created

5 Call the SqlDataAdapter.Fill instance method, passing the DataSet you

created in step 2 as a method argument

6 Use the DataSet to access the DataTables contained within—read and modify

data as required

7 Call the SqlDataAdapter.Update method to write any changes back to the

database

To create a new row in a table, call the DataTable.NewRow instance method to obtain an instance of

DataRow that has the same schema as the DataTable The new row is not automatically added to the table

Trang 14

463

when you call NewRow—call DataTable.Rows.Add once you have set the values for the row Changes that

you make to the data in the DataSet are not written back to the database until you call the

// Create a new SqlConnection object

using (SqlConnection con = new SqlConnection())

{

// Configure the SqlConnection object's connection string

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI";

// Open the database connection

con.Open();

// Create the query string

string query = "SELECT * from Region";

// Create the data set

DataSet dataset = new DataSet();

// Create the SQL data adapter

SqlDataAdapter adapter = new SqlDataAdapter(query, con);

// Create the command builder so we can do modifications

SqlCommandBuilder commbuilder = new SqlCommandBuilder(adapter);

// Populate the data set from the database

adapter.Fill(dataset);

Trang 15

464

// Print details of the schema

Console.WriteLine("\nSchema for table");

DataTable table = dataset.Tables[0];

foreach (DataColumn col in table.Columns)

{

Console.WriteLine("Column: {0} Type: {1}", col.ColumnName, col.DataType);

// Create a new row

DataRow newrow = table.NewRow();

newrow["RegionID"] = 5;

newrow["RegionDescription"] = "Central";

table.Rows.Add(newrow);

// Modify an existing row

table.Rows[0]["RegionDescription"] = "North Eastern";

// Enumerate the cached data again

// Enumerate the data we have received

Console.WriteLine("\nData in (modified) table"); foreach (DataRow row in table.Rows)

Trang 16

465

Running the example produces the following results:

Schema for table

Column: RegionID Type: System.Int32

Column: RegionDescription Type: System.String

Data in (modified) table

Data 1 North Eastern

Trang 17

Create an instance of System.Sql.DataSet and manually populate it with instances of

System.Data.Datatable Create a schema for each table and create rows to represent data elements

How It Works

In the previous recipe, we demonstrated how to use the DataSet and DataTable classes as part of a

memory cache, in order to achieve disconnected data manipulation However, you can create instances

of these classes to represent data programmatically by calling constructors for the classes directly The

example code for this recipe illustrates how to do this in order to create the same kind of DataSet and

DataTable that we used previously

The Code

The following code creates a DataSet that contains a single DataTable and populates it with instances of

DataRow Once populated, the same queries, modifications, and additions are performed upon it as in the

// Create the data set

DataSet dataset = new DataSet();

// Create the table and add it to the data set

DataTable table = new DataTable("Regions");

dataset.Tables.Add(table);

// Create the colums for the table

table.Columns.Add("RegionID", typeof(int));

table.Columns.Add("RegionDescription", typeof(string));

Trang 18

467

// Populate the table

string[] regions = { "Eastern", "Western", "Northern", "Southern" };

for (int i = 0; i < regions.Length; i++)

// Print details of the schema

Console.WriteLine("\nSchema for table");

foreach (DataColumn col in table.Columns)

// Create a new row

DataRow newrow = table.NewRow();

newrow["RegionID"] = 5;

newrow["RegionDescription"] = "Central";

table.Rows.Add(newrow);

// Modify an existing row

table.Rows[0]["RegionDescription"] = "North Eastern";

// Enumerate the cached data again

// Enumerate the data we have received

Console.WriteLine("\nData in (modified) table");

foreach (DataRow row in table.Rows)

Trang 19

468

Schema for table

Column: RegionID Type: System.Int32

Column: RegionDescription Type: System.String

Data in (modified) table

Data 1 North Eastern

Data 2 Western

Data 3 Northern

Data 4 Southern

Data 5 Central

Main method complete Press Enter

9-14 Perform a LINQ Query Problem

You need to use LINQ to query a database

Trang 20

LINQ performs queries on the IEnumerable<> type, which you can obtain from instances of DataTable

using the AsEnumerable instance method When using SQLDataAdapter to populate instances of DataTable

with data (see recipe 9-12), remember that you are working with cached data that will not reflect

changes made to the database See Chapter 16 for recipes that demonstrate LINQ features

The Code

The following example creates a DataSet that contains a DataTable with all of the rows of the Northwind

Region table, and then performs a LINQ query using the DataTable as the data source:

// Create a new SqlConnection object

using (SqlConnection con = new SqlConnection())

{

// Configure the SqlConnection object's connection string

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI";

// Open the database connection

con.Open();

// Create the query string

string query = "SELECT * from Region";

// Create the data set

DataSet dataset = new DataSet();

Trang 21

470

// Create the SQL data adapter

SqlDataAdapter adapter = new SqlDataAdapter(query, con); // Create the command builder so we can do modifications SqlCommandBuilder commbuilder = new SqlCommandBuilder(adapter);

// Populate the data set from the database

adapter.Fill(dataset);

// Obtain the data table

DataTable table = dataset.Tables[0];

// Perform the LINQ query

IEnumerable<string> result = from e in table.AsEnumerable() where e.Field<int>(0) < 3

select e.Field<string>(1);

// Enumerate the results of the LINQ query

foreach (string str in result)

Trang 22

Define and annotate types with the Table and Column annotations and use

System.Data.Linq.DataContext to access the data in your database

How It Works

LINQ includes support for entity classes, which map the schema from your database into NET types

You create an entity type by defining a partial class with members representing columns in a given

database table and apply annotations from the System.Data.Linq.Mapping namespace to give the NET

Framework details of how to map from the table to instances of your entity type

■ Tip Visual Studio can automatically create types for you Select the Add a New Item option for your project, and then select LINQ to SQL Classes to start a wizard that will generate the source files you require

The first annotation to apply is Table, which creates the relationship between the partial class you

have defined and the table in the database—this annotation takes one argument, which is, not

surprisingly, the name of the table in question You must then define one member for each column in

the table (ensuring that the member type matches the schema type for the database table), and apply the

Column annotation For the Region table in the Northwind database, we would create a class like this:

To use the entity type, create an instance of System.Data.Linq.DataContext, passing in a

SqlConnection to your database as the constructor argument You then call the DataContext.GetTable<>

instance method using your entity class as the type annotation—for example:

Table<Region> regionstable = context.GetTable<Region>();

The result from the GetTable method is a strongly typed instance of System.Data.Linq.Table, which

you can use as the data source for a LINQ query In the clauses of the query, you can refer to the

Trang 23

The following example defines the type Region to represent rows in the Northwind Region table A

DataContext is created to access the data, and the Region table is used as the basis for a LINQ query,

returning an IEnumeration<Region> as the result

■ Note You must add the System.Data.Linq.dll assembly to your project in order to use the

System.Data.Linq and System.Data.Linq.Mapping namespaces

Trang 24

473

// Create a new SqlConnection object

using (SqlConnection con = new SqlConnection())

{

// Configure the SqlConnection object's connection string

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI";

// Open the database connection

con.Open();

// Create the data context

DataContext context = new DataContext(con);

// Get the table we are interested in

Table<Region> regionstable = context.GetTable<Region>();

IEnumerable<Region> result = from e in regionstable

Trang 25

474

How It Works

The default result of a LINQ query on a DataSet is an IEnumerable<DataRow>, and LINQ provides

extension methods that operate on this result type to allow you to compare results

■ Tip See Chapter 16 for more information about LINQ extension methods, recipes for using them, and creating custom extension methods that you can apply to your own data types

The three extension methods are Union, Intersect, and Except In all three cases, you call the

extension method on one result and supply another as the method argument—for example:

IEnumerable<DataRow> result1 = LINQ query on a DataSet

IEnumerable<DataRow> result2 = LINQ query on a DataSet

IEnumerable<DataRow> union = result1.Union(result2)

The Union method combines the contents of the two IEnumerable<DataRow> instances The

Intersect method returns just those rows that exist in both enumerations The Except method returns

all of the rows in the first enumeration except those that also exist in the second enumeration

The result of these methods is another IEnumerable<DataRow>, meaning that you can use the result

to enumerate the data rows or as the basis for a further LINQ query, and you can use the same extension

methods to compare the result against another IEnumerable<DataRow>

// Create a new SqlConnection object

using (SqlConnection con = new SqlConnection())

{

Trang 26

475

// Configure the SqlConnection object's connection string

con.ConnectionString = @"Data Source = \sqlexpress;" +

"Database = Northwind; Integrated Security=SSPI";

// Open the database connection

con.Open();

// Create the query string

string query = "SELECT * from Region";

// Create the data set

DataSet dataset = new DataSet();

// Create the SQL data adapter

SqlDataAdapter adapter = new SqlDataAdapter(query, con);

// Create the command builder so we can do modifications

SqlCommandBuilder commbuilder = new SqlCommandBuilder(adapter);

// Populate the data set from the database

adapter.Fill(dataset);

// Obtain the data table

DataTable table = dataset.Tables[0];

// Perform the first LINQ query

IEnumerable<DataRow> result1 = from e in table.AsEnumerable()

where e.Field<int>(0) < 3

select e;

// Enumerate the results of the first LINQ query

Console.WriteLine("Results from first LINQ query");

foreach (DataRow row in result1)

{

Console.WriteLine("ID: {0} Name: {1}",

row.Field<int>(0), row.Field<string>(1));

}

// Perform the first LINQ query

IEnumerable<DataRow> result2 = from e in table.AsEnumerable()

let name = e.Field<string>(1)

where name.StartsWith("North")

|| name.StartsWith("East")

select e;

Trang 27

476

// Enumerate the results of the first LINQ query

Console.WriteLine("\nResults from second LINQ query"); foreach (DataRow row in result2)

{

Console.WriteLine("ID: {0} Name: {1}",

row.Field<int>(0), row.Field<string>(1));

}

IEnumerable<DataRow> union = result1.Union(result2);

// Enumerate the results

Console.WriteLine("\nResults from union");

foreach (DataRow row in union)

Console.WriteLine("\nResults from intersect");

foreach (DataRow row in intersect)

Console.WriteLine("\nResults from except");

foreach (DataRow row in except)

Trang 28

477

Running the sample program gives the following results:

Results from first LINQ query

ID: 1 Name: Eastern

ID: 2 Name: Western

Results from second LINQ query

ID: 1 Name: Eastern

ID: 3 Name: Northern

Results from union

ID: 1 Name: Eastern

ID: 2 Name: Western

ID: 3 Name: Northern

Results from intersect

ID: 1 Name: Eastern

Results from except

ID: 2 Name: Western

Main method complete Press Enter

Trang 30

■ ■ ■

479

Networking

The Microsoft NET Framework includes a full set of classes for network programming These classes

support low-level network programming tasks like querying the state of network interfaces and based programming with Transmission Control Protocol/Internet Protocol (TCP/IP) to higher-level

socket-tasks like downloading files and HTML pages from the Web over Hypertext Transfer Protocol (HTTP)

You can even build fully distributed applications using distributed objects or service-oriented

approaches

Included in the release of NET Framework 3.0 was Windows Communication Foundation (WCF), a unified programming model for building service-oriented applications Although earlier technologies

are still available, WCF is generally seen as a replacement for technologies like NET Remoting and

ASP.NET Web Services, and also provides a flexible unified interface through which to access many other types of distributed communications, like message queues

The recipes in this chapter describe how to do the following:

• Obtain configuration and network statistic information about the network

interfaces on a computer as well as detect when network configuration changes

occur (recipes 10-1 and 10-2)

• Download files from File Transfer Protocol (FTP) and HTTP servers (recipes 10-3,

10-4, and 10-6)

• Respond to HTTP requests from within your application (recipe 10-5)

• Send e-mail messages with attachments using Simple Mail Transfer Protocol

(SMTP) (recipe 10-7)

• Use the Domain Name System (DNS) to resolve a host name into an Internet

Protocol (IP) address (recipe 10-8)

• Ping an IP address to determine whether it is accessible and calculate round-trip

communication speeds by sending it an Internet Control Message Protocol

(ICMP) Echo request (recipe 10-9)

• Communicate between programs through the direct use of TCP in both

synchronous and asynchronous communication models (recipes 10-10 and 10-11)

• Communicate using User Datagram Protocol (UDP) datagrams where

connection-oriented and reliable TCP represents unnecessary overhead (recipe

10-12)

Trang 31

480

• Create a SOAP-based web service (recipe 10-13)

• Generate a WCF service proxy dynamically (recipe 10-14)

• Parse the contents of an Atom or RSS feed (recipe 10-15)

• Manipulate uniform resource locators (URIs) (recipe 10-16)

■ Tip A number of the recipes in this chapter include a client and a server component that must both be running for the recipe to work correctly Where this is the case, the client and server code are contained in separate projects To run these recipes from within Visual Studio, set the server project as the startup project and run it normally Once the server is running, right-click the client project in Solution Explorer, click Debug on the context menu, and then click “Start new instance.”

10-1 Obtain Information About the Local Network Interface Problem

You need to obtain information about the network adapters and network configuration of the local machine

Solution

Call the static method GetAllNetworkInterfaces of the System.Net.NetworkInformation

NetworkInterface class to get an array of objects derived from the abstract class NetworkInterface

Each object represents a network interface available on the local machine Use the members of each

NetworkInterface object to retrieve configuration information and network statistics for that interface

How It Works

The System.Net.NetworkInformation namespace provides easy access to information about network

configuration and statistics The primary means of retrieving network information are the properties

and methods of the NetworkInterface class You do not instantiate NetworkInterface objects directly Instead, you call the static method NetworkInterface.GetAllNetworkInterfaces, which returns an array

of NetworkInterface objects Each object represents a single network interface on the local machine You can then obtain network information and statistics about the interface using the NetworkInterface

members described in Table 10-1

Trang 32

481

■ Tip The System.Net.NetworkInformation.IPGlobalProperties class also provides access to useful

information about the network configuration of the local computer

Table 10-1 Members of the NetworkInterface Class

Member Description

Properties

Description Gets a string that provides a general description of the interface

Id Gets a string that contains the identifier of the interface

IsReceiveOnly Gets a bool indicating whether the interface can only receive or can both send

and receive data

Name Gets a string containing the name of the interface

NetworkInterfaceType Gets a value from the System.Net.NetworkInformation.NetworkInterfaceType

enumeration that identifies the type of interface Common values include

Ethernet, FastEthernetT, and Loopback

OperationalStatus Gets a value from the System.Net.NetworkInformation.OperationalStatus

enumeration that identifies the status of the interface Common values include

Down and Up

Speed Gets a long that identifies the speed (in bits per second) of the interface as

reported by the adapter, not based on dynamic calculation

SupportsMulticast Gets a bool indicating whether the interface is enabled to receive multicast

packets

Methods

GetIPProperties Returns a System.Net.NetworkInformation.IPInterfaceProperties object that

provides access to the TCP/IP configuration information for the interface

Properties of the IPInterfaceProperties object provide access to WINS, DNS,

gateway, and IP address configuration

Trang 33

482

Member Description

GetIPv4Statistics Returns a System.Net.NetworkInformation.IPv4InterfaceStatistics object

that provides access to the TCP/IP v4 statistics for the interface The properties

of the IPv4InterfaceStatistics object provide access to information about

bytes sent and received, packets sent and received, discarded packets, and packets with errors

GetPhysicalAddress Returns a System.Net.NetworkInformation.PhysicalAddress object that

provides access to the physical address of the interface You can obtain the

physical address as a byte array using the method

PhysicalAddress.GetAddressBytes or as a string using PhysicalAddress.ToString

Supports Returns a bool indicating whether the interface supports a specified protocol

You specify the protocol using a value from the

System.Net.NetworkInformation.NetworkInterfaceComponent enumeration

Possible values include IPv4 and IPv6

The NetworkInterface class also provides two other static members that you will find useful:

The static property LoopbackInterfaceIndex returns an int identifying the index

of the loopback interface within the NetworkInterface array returned by

GetAllNetworkInterfaces

The static method GetIsNetworkAvailable returns a bool indicating whether any

network connection is available—that is, has an OperationalStatus value of Up

The Code

The following example uses the members of the NetworkInterface class to display information about all

the network interfaces on the local machine:

Trang 34

483

// Iterate through the interfaces and display information

foreach (NetworkInterface ni in interfaces)

{

// Report basic interface information

Console.WriteLine("Interface Name: {0}", ni.Name);

Console.WriteLine(" Description: {0}", ni.Description);

Console.WriteLine(" ID: {0}", ni.Id);

Console.WriteLine(" Type: {0}", ni.NetworkInterfaceType);

Console.WriteLine(" Speed: {0}", ni.Speed);

Console.WriteLine(" Status: {0}", ni.OperationalStatus);

// Report physical address

Console.WriteLine(" Physical Address: {0}",

ni.GetPhysicalAddress().ToString());

// Report network statistics for the interface

Console.WriteLine(" Bytes Sent: {0}",

Trang 35

Add handlers to the static NetworkAddressChanged and NetworkAvailabilityChanged events implemented

by the System.Net.NetworkInformation.NetworkChange class

How It Works

The NetworkChange class provides an easy-to-use mechanism that allows applications to be aware of

changes to network addresses and general network availability This allows your applications to adapt dynamically to the availability and configuration of the network

The NetworkAvailabilityChanged event fires when a change occurs to general network availability

An instance of the NetworkAvailabilityChangedEventHandler delegate is needed to handle this event and

is passed a NetworkAvailabilityEventArgs object when the event fires The

NetworkAvailabilityEventArgs.IsAvailable property returns a bool indicating whether the network is

available or unavailable following the change

The NetworkAddressChanged event fires when the IP address of a network interface changes

An instance of the NetworkAddressChangedEventHandler delegate is required to handle these events

No event-specific arguments are passed to the event handler, which must call NetworkInterface

GetAllNetworkInterfaces (discussed in recipe 10-1) to determine what has changed and to take

appropriate action

■ Note The NetworkAddressChanged and NetworkAvailabilityChanged events work on Windows 2000 and later operating systems

The Code

The following example demonstrates how to use handlers that catch NetworkAddressChanged and

NetworkAvailabilityChanged events and then display status information to the console To observe how

the code handles changing network conditions, unplug your network cable while the example is

running, wait a few seconds, and then plug the cable back in

Trang 36

// Declare a method to handle NetworkAvailabilityChanged events

private static void NetworkAvailabilityChanged(

object sender, NetworkAvailabilityEventArgs e)

// Declare a method to handle NetworkAdressChanged events

private static void NetworkAddressChanged(object sender, EventArgs e)

Trang 37

The NET Framework provides several mechanisms for transferring data over the Internet One of the

easiest approaches is to use the System.Net.WebClient class WebClient provides many high-level

methods that simplify the transfer of data by specifying the source as a URI; Table 10-2 summarizes

them The URI can specify that a file (file://), FTP (ftp://), or HTTP (http:// or https://) scheme be

used to download the resource

Table 10-2 Data Download Methods of the WebClient Class

Method Description

OpenRead Returns a System.IO.Stream that provides access to the data from a specified

URI

OpenReadAsync Same as OpenRead, but performs the data transfer using a thread-pool thread so

that the calling thread does not block Add an event handler to the

OpenReadCompleted event to receive notification that the operation has

completed

DownloadData Returns a byte array that contains the data from a specified URI

DownloadDataAsync Same as DownloadData, but performs the data transfer using a thread-pool thread

so that the calling thread does not block Add an event handler to the

DownloadDataCompleted event to receive notification that the operation has

completed

Trang 38

487

DownloadFile Downloads data from a specified URI and saves it to a specified local file

DownloadFileAsync Same as DownloadFile, but performs the data transfer using a thread-pool thread

so that the calling thread does not block Add an event handler to the

DownloadFileCompleted event to receive notification that the operation has

completed

DownloadString Returns a string that contains the data from a specified URI

DownloadStringAsync Same as DownloadString, but performs the data transfer using a thread-pool

thread so that the calling thread does not block Add an event handler to the

DownloadStringCompleted event to receive notification that the operation has

completed

The asynchronous download allows you to download data as a background task using a thread from the thread pool (discussed in recipe 4-1) When the download is finished or fails, the thread calls the

appropriate OnXXX virtual methods that raise the corresponding event on the WebClient object, which

you can handle using a method that matches the signature of the System.ComponentModel

AsyncCompletedEventHandler delegate if you don’t want to derive a type from WebClient and override the

virtual method However, the WebClient object can handle only a single concurrent asynchronous

download, making a WebClient object suitable for the background download of large single sets of data but not for the download of many files concurrently (You could, of course, create multiple WebClient

objects to handle multiple downloads.) You can cancel the outstanding asynchronous download using

the method CancelAsync

■ Tip The WebClient class derives from System.ComponentModel.Component, so you can add it to the Visual

Studio Form Designer Toolbox in order to allow you to easily set the properties or define the event handlers in a

Windows Forms–based application

The Code

The following example downloads a specified resource from a URI as a string and, since it is an HTML

page, parses it for any fully qualified URLs that refer to GIF files It then downloads each of these files to the local hard drive

Trang 39

488

private static void Main()

{

// Specify the URI of the resource to parse

string srcUriString = "http://www.apress.com";

Uri srcUri = new Uri(srcUriString);

// Create a WebClient to perform the download

WebClient client = new WebClient();

Console.WriteLine("Downloading {0}", srcUri);

// Perform the download getting the resource as a string

string str = client.DownloadString(srcUri);

// Use a regular expression to extract all HTML <img>

// elements and extract the path to any that reference

// files with a gif, jpg, or jpeg extension

MatchCollection matches = Regex.Matches(str,

"<img.*?src\\s*=\\s*[\"'](?<url>.*?\\.(gif|jpg|jpeg)).*?>", RegexOptions.Singleline | RegexOptions.IgnoreCase);

// Try to download each referenced image file

foreach(Match match in matches)

Uri imgUri = null;

// Determine the source URI

Trang 40

■ Note The regular expression used in the example is simple and is not designed to cater to all possible URL

structures Recipes 2-5 and 2-6 discuss regular expressions

Notes

You may also want to upload data to resources specified as a URI, although this technique is not as

commonly used The WebClient class also provides methods for performing uploads that are equivalent

to the download methods discussed previously:

OpenWrite

OpenWriteAsync

UploadData

UploadDataAsync

Ngày đăng: 18/06/2014, 16:20

TỪ KHÓA LIÊN QUAN