Let’s use LINQ to SQL to retrieve all customers from the Northwind Customerstable.
1. Rename the Chapter18project in the Chapter18solution to LinqToSql, then rename Program.csto LinqToSql.cs. Replace the code in LinqToSql.cswith the code in List- ing 18-1.
Listing 18-1.LinqToSql.cs using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Xml.XLinq;
using System.Data.DLinq;
namespace Chapter18 {
[Table]
public class Customers {
[Column(Id=true)]
public string customerId;
[Column]
public string companyName;
[Column]
public string city;
[Column]
public string country;
}
class LinqToSql {
static void Main(string[] args) {
// connection string string connString = @"
server = .\sqlexpress;
integrated security = true;
database = northwind
";
C H A P T E R 1 8 ■ I N T R O D U C I N G L I N Q 467
// create data context
DataContext db = new DataContext(connString);
// create typed table
Table<Customers> customers = db.GetTable<Customers>();
// query database var custs =
from c in customers select
c
;
// display customers foreach (var c in custs)
Console.WriteLine(
"{0} {1} {2} {3}", c.customerId, c.companyName, c.city, c.country );
} } }
2. Run the program with Ctrl+F5 and you should see results as in Figure 18-11 (which displays the last ten rows).
C H A P T E R 1 8 ■ I N T R O D U C I N G L I N Q 468
Figure 18-11.Retrieving customer data with LINQ to SQL
How It Works
You defined an entity class,Customers: [Table]
public class Customers {
[Column(Id=true)]
public string customerId;
[Column]
public string companyName;
[Column]
public string country;
}
Entity classes provide objects in which LINQ stores data from data sources. They’re like any other C# class, but LINQ defines attributes that tell it how to use the class.
The [Table]attribute marks the class as an entity class and has an optional Name property that can be used to give the name of a table, which defaults to the class name.
That’s why you named the class Customersrather than Customer. A more typical approach would be
[Table(Name="Customers")]
public class Customer
and then you’d have to change the typed table definition to Table<Customer> customers = db.GetTable<Customer>();
to be consistent.
The [Column]attribute marks a field as one that will hold data from a table. You can declare fields in an entity class that don’t map to table columns, and LINQ will just ignore them, but those decorated with the [Column]attribute must be of types compatible with the table columns they map to. The [Column]attribute also has an optional Nameproperty that can be used to specify a specific column and defaults the column name to the field name. (Note that since SQL Server table and column names aren’t case sensitive, the default names do not have to be identical in case to the names used in the database.)
You used the Idproperty in the [Column]attribute for the first field:
[Column(Id=true)]
public string customerId;
because CustomerIDis the primary key of the Customerstable. LINQ will deduce whatever it can about a column, but you need to tell it explicitly that a column is part of a primary key.
C H A P T E R 1 8 ■ I N T R O D U C I N G L I N Q 469
Note that you made only four of the eleven columns in the Customerstable available to LINQ.
■ Tip SQLMetal is a tool that comes with LINQ that can generate entity class declarations from a SQL Server database. It’s described in section 6.3 of the DLinq Overview for CSharp Developers.docfile, which comes with LINQ and is the basic documentation for LINQ to SQL.
You created a data context: // create data context
DataContext db = new DataContext(connString);
A data context does what an ADO.NET connection does, but it also does things that a data provider handles. It not only manages the connection to a data source, but also translates LINQ requests (expressed in SQO) into SQL, passes the SQL to the database server, and creates objects from the result set.
You created a typed table:
// create typed table
Table<Customers> customers = db.GetTable<Customers>();
A typed table is a collection (of type System.Data.Dlinq.Table<T>) whose elements are of a specific type. The GetTablemethod of the data context specifies the data context to access and where to put the results. Here, you got all the rows (but only four columns) from the Customerstable and the data context created an object for each row in the customerstyped table.
■ Tip Data contexts can be strongly typed, and that’s the recommended way to use them, but we inten- tionally didn’t do that so we’d have to explicitly create a typed table and show you how to load it. See DLinq Overview for CSharp Developers.doc, section 2.2, to learn how to define a strongly typed data context.
You declared a C# 3.0 implicitly typed local variable,custs, of type var: // query database
var custs =
C H A P T E R 1 8 ■ I N T R O D U C I N G L I N Q 470
An implicitly typed local variable is just what its name implies. When C# sees the var type, it infers the type of the local variable based on the type of the expression in the initializerto the right of the =sign.
You initialized the local variable with a query expression:
from c in customers select
c
;
A query expression is composed of a fromclause and a query body. We’ve used the simplest form of the fromclause and query body here. This fromclause declares an itera- tion variable, c, to be used to iterate over the result of the expression, customers—that is, over the typed table we earlier created and loaded. A query body must include a selector groupbyclause, which may be preceded by a whereor an orderbyclause.
Your selectclause was the most primitive possible:
select c
and, like a SQL SELECT *, gets all columns, so the variable custsis implicitly typed to han- dle a collection of objects that contain all the fields in the Customersclass.
■ Note As we mentioned earlier in “What Is LINQ?” C# translates query expressions into calls to SQO methods. In fact, C# can also translate parts of query expressions into extension methods, which enable you to code custom methods for use with LINQ objects. This is far beyond the scope of this book, but it’s a functional programming feature that you can use to great advantage.
Finally, you looped through the custscollection and displayed each customer. Except for the use of the vartype in the foreachstatement, this was just C# 2.0.
// display customers foreach (var c in custs)
Console.WriteLine(
"{0} {1} {2}", c.customerId, c.companyName, c.country );
C H A P T E R 1 8 ■ I N T R O D U C I N G L I N Q 471
Despite the new C# features and terminology, what you did should feel familiar.
Once you get the hang of it, it’s an appealing alternative for coding queries. You basically coded a query expression instead of SQL to populate a collection that you could iterate through with a foreachstatement. However, you provided a connection string, but didn’t explicitly open or close a connection. Further, no command, data reader, or indexer was required. You didn’t even need the System.Dataor System.Data.SqlClientnamespaces to access SQL Server.
Pretty cool.