LISTING 45.2 Your First LINQ to SQL Query public List GetProductsByIdint ProductId { AdventureWorks2008DataContext Context = new AdventureWorks2008DataContext; var query = from p in Cont
Trang 1FIGURE 45.3 Adding a LINQ to SQL classes file to your Windows Forms sample application
After your new .dbmlfile is created in your project, Visual Studio’s Object/Relational (O/R)
Designer opens up with its blank surface ready On this surface, you add C# data classes
that mirror the tables and relationships in your database This is the heart of what is
known as object/relational mapping (ORM): each class represents a database table Each
instance of that class represents a row in that table Each property on each instance
repre-sents either a column and its value, or a collection of related (via ORM) objects Changes
to these object instances (including updates, insertions, and deletions) once committed,
are reflected as changes to the underlying rows in your tables LINQ to SQL operates on
these ORM objects, which are defined in the code of your .dbmlfile
Using Visual Studio’s View menu, open Server Explorer Right-click its root node and select
Add Connection Fill out the form to set up a connection to your AdventureWorks2008
database When done, navigate to that new node in Server Explorer (it is named
[ServerName]\[InstanceName].AdventureWorks2008.[SchemaName]) (This new node
should have a tiny image of a plug on its icon.) Expand this node and then expand the
Tablesnode to view the tables in your database
Shift-click the following table nodes in Server Explorer and then drag them all to your O/R
Designer surface and release the mouse button (answer Yes to the ensuing warning dialog):
Product (Production)
ProductModel (Production)
ProductInventory (Production)
ProductReview (Production)
Your O/R Designer surface should now look something like the one in Figure 45.4
The best way to introduce any new technology is with sample code, so let’s jump right in
and write a LINQ to SQL query
Trang 2FIGURE 45.4 Viewing Visual Studio’s O/R Designer surface for the Windows Forms sample
application
Right-click your Windows Forms project in Solution Explorer and then click Add Class
Name your new class LINQExamples.csand then add the code from Listing 45.2 into its
body
LISTING 45.2 Your First LINQ to SQL Query
public List<Product> GetProductsById(int ProductId)
{
AdventureWorks2008DataContext Context =
new AdventureWorks2008DataContext();
var query =
from p in Context.Products
where p.ProductID == ProductId
select p;
return query.ToList();
}
Trang 3Going Deeper
LINQ requires an understanding of generics (introduced in NET 2.0), which provide a
means of working with classes in a type-independent manner That is, a generic class
provides methods and properties just like any other class; however, it also has a type
para-meter that enables users of that class to supply a type at runtime that the algorithms in
the class will then operate on In Listing 45.2, for example, the return type of
GetProductsByIduses the generic Framework class System.Collections.Generic.List<T>,
substitutingProductas a type parameter for T
When working with LINQ to SQL, you also use the new varkeyword, which indicates that
the named variable is implicitly typed This means that the compiler will infer the type of
the statement on the right by walking down the expression at compile time In many
cases, your LINQ statements end up being implicitly typed as
System.Linq.IQueryable<T> This generic class stores the abstract expression tree that will
be translated (at execution time) to T-SQL, information about the query provider (in this
case SQL Server), as well as the enumerable collection itself that provides access to the
data the query returns
IQueryable<T>itself derives from System.Collections.Generic.IEnumerable<T>.One
reason for this is that, under the hood, the compiler converts the query syntax used in
Listing 45.2 to actually use generic query operators defined as extension methods to
IEnumerable<T>, including Select,Where,OrderBy,Distinct, and so on (Extension
methods provide a means of adding methods to classes that are otherwise sealed, that is,
noninheritable) This means that the query in Listing 45.2 could also have been written as
var query = Context.Products.Where(p => p.ProductID == ProductId);
Because of their common return type (IEnumerable<T>), LINQ’s extension methods may
be chained together For example:
var query =
Context.Products.Where(p => p.ProductID == ProductId).OrderBy(p =>
p.ProductID).Distinct();
The parameter to the WhereandOrderBymethods shown here is built on another new
construct called a lambda expression, which is an anonymous function that can be cast to a
delegate (such as System.Func<T, TResult>, the delegate type required by the input
para-meter to the extension method Where) Lambda expressions take the form input
parame-ters => expression, where the lambda operator=>is read as “goes to.” (For more
information, refer to the MSDN article titled “Lambda Expressions.”) Put simply, these
expressions provide a compact syntax for writing anonymous functions that you will use
more often as you progress deeper in your knowledge of LINQ For now, let’s get back to
the code in Listing 45.2
First, in the method signature, you can see that GetProductsByIdtakes an integral
ProductIdas input and returns a generic ListofProductobjects What is a Product
Trang 4object? It’s a LINQ to SQL class instance that points (is object/relationally mapped) to a
particular row in the Production.Producttable
You can think of a Productobject as an “objectified” SQL Server row, insofar as its
primi-tively typed properties are akin to SQL Server column values It also has collection-backed
properties that point to rows stored in the tables to which it is related These specialized
LINQ collections are of type System.Data.Linq.EntitySet Let’s examine theclass
defini-tion of Productto see how this plays out
Using Solution Explorer, expand the AdventureWorks2008.dbmlfile to reveal
AdventureWorks2008.designer.cs This code file contains all the ORM logic needed to use
LINQ to SQL with your database Double-click this file and, using the drop-down at the
top left of your code editor window, select [YourProjectName].Product.
Above your class’s name, notice the Tableattribute (attributes are NET classes used to
decorate other classes with information queryable at runtime) Its Nameproperty reveals
that the class it decorates is to be mapped to rows in Production.Product As you scroll
down, examine this class a bit more Notice how its primitive properties map nicely to the
columns of your table Notice also how each property has its own Columnattribute,
signi-fying the specific SQL Server column to which it is mapped
As we mentioned earlier, the Productclass also has collection-backed properties, such as
ProductInventoriesandProductReviews These properties represent the tables related
(via primary and foreign keys) to Product By using properties in your NET code, you can
navigate from a parent object in Productto a child object in ProductInventories, just like
you would when writing T-SQL joins Not surprisingly, each Associationattribute found
on your collection-backed properties denotes the direction of the navigational path from
parent to child
The glue that holds all this together is theSystem.Linq.DataContextclass, represented in
Listing 45.2 by yourContextobject (which is of typeAdventureWorks2008DataContext) Put
simply,DataContextis the ORM bridge or conduit from NET to SQL Server and back
At the top of your designer file, you can see that the DBML class inherits from
System.Data.Linq.DataContext Notice its mapping attribute, Database, which indicates
that it is mapped to AdventureWorks2008 As the SQL Server conduit, you use the
DataContextinstance to select your object-mapped rows Through it, you commit inserts,
updates, and deletes to the underlying tables, by adding, changing properties of, and
removing objects from its System.Data.Linq.Tablecollection’s properties These
collec-tions represent the tables you added to your O/R Designer’s surface And this is the real
power of LINQ to SQL: no longer is it necessary to waste hours writing boilerplate T-SQL
to perform simple database operations; you can get it all done with pure NET code
Let’s look at a slightly more complex LINQ query that leverages the power of key-based
rela-tionships to select related database objects In Listing 45.3, you add a new method to the
sample class that gets aListofProductReviewobjects for a givenProduct
Trang 5LISTING 45.3 Using Database Relationships to Select Related Objects with LINQ to SQL
public List<ProductReview>
GetProductReviewsByProduct(Product MyProduct)
{
AdventureWorks2008DataContext Context =
new AdventureWorks2008DataContext();
var query =
from p in Context.Products
join r in Context.ProductReviews
on p.ProductID equals r.ProductID
where p.ProductID == MyProduct.ProductId
select r;
return query.ToList();
}
Notice the joinsyntax introduced in this example As you can see, it’s a lot like T-SQL’s
INNER JOIN, and it performs the same basic function The whereclause in the example is
also just like T-SQL’s WHERE, except for the fact that you have to use C#’s ==operator
instead of T-SQL’s =) The one big difference to pay attention to is that with LINQ your
selectstatement comes last, and your fromclause comes first Remember that all the
tables you want to select from are properties (System.Data.Linq.Tableobjects) of the
DataContextobject Very simple, very powerful
Uncovering LINQ to SQL with Linqpad
You may be curious about the T-SQL that LINQ is generating for this NET expression To
reveal this mystery, you can use two tools: SQL Server Profiler and Linqpad To use
Profiler, launch the application from the Windows Start menu, create a new trace using
the File menu (select New Trace), and connect to your database of choice On the ensuing
Trace Properties dialog, select the trace template called T-SQL_SPs, start the trace, and then
run the LINQ statement found in Listing 45.3 The T-SQL that LINQ generates is revealed
in the TextDatacolumn
Alternatively, you can download and use the amazing Linqpad from http://www.linqpad
net Visit the site and download Linqpad.exe Save it to a folder that you will remember
As of this writing (version 1.35), Linqpad does not have or need an installation program;
the download itself is the entire self-contained application
After you download Linqpad, start it Click on the Add Connection link at top left of the
application’s main window; then add a connection to your local server Next, select the
AdventureWorks2008database using the drop-down at the top left In the query window,
Trang 6type the following LINQ statement (Hint: This is the same as shown in Listing 45.3, minus
the method signature, input parameter, and Contextobject):
from p in Products
join r in ProductReviews
on p.ProductID equals r.ProductID
select r
Click the green arrow button or press F5 Your query results show up in a visually friendly
tabular format in the results area below the query window Notice just above the results
area there is a button bar with four buttons: Results, Δ; (lambda expression), SQL, and IL.
You’ve seen what shows up with the default setting, Results.Δ; reveals the underlying
anonymous functions upon which LINQ relies IL shows you the CLR Intermediate
Language code that your LINQ expression generates Finally, SQL shows the resultant
T-SQL, of main concern to you now Select SQL and press F5 again Your results should
match the code shown in Figure 45.5 You can also use the Analyze SQL menu option
(above the results area) to jump into SSMS to run your query, or you can even run the
T-SQL itself via Linqpad
Next, let’s examine an insert query using LINQ to SQL, as shown in Listing 45.4
FIGURE 45.5 Viewing the T-SQL Generated by a LINQ Query with Linqpad
Trang 7LISTING 45.4 An Insert Query Using LINQ to SQL
public void AddProductReview(
int ForProductId,
string Comments,
string Email,
int Rating,
string Reviewer
)
{
AdventureWorks2008DataContext Context =
new AdventureWorks2008DataContext();
ProductReview NewReview =
new ProductReview()
{
Comments = Comments,
EmailAddress = Email,
ModifiedDate = DateTime.Now,
ProductID = ForProductId,
Rating = Rating,
ReviewDate = DateTime.Now,
ReviewerName = Reviewer
};
Context.ProductReviews.InsertOnSubmit(NewReview);
Context.SubmitChanges();
//Check the new review ID:
if (NewReview.ProductReviewID > 0)
{
Debug.WriteLine(
string.Format(
“Success! Added new ProductReview with ID#{0}”, NewReview.ProductReviewID
)
);
}
}
Let’s go over this code in detail First, you can see that the input parameters to the new
AddProductReviewmethod include a ProductReviewIdas well as most of the properties
that make up a row in Production.ProductReview Next, using C#’s new newsyntax, you
instantiate a ProductReviewobject (NewReview) representing a row to be added to
Trang 8Production.ProductReview To make the insertion happen, you again rely on the
DataContextobject (Context) The syntax Context.ProductReviewsindicates that the
target table is Production.ProductReview
When you callInsertOnSubmitwith yourNewReviewobject as its parameter, your new
review is added to the table when you callSubmitChanges(one line further down in the
example) After that call, you can check your object to see if itsProductReviewID
prop-erty was magically populated due to the fact that the row was created in the database
(becauseProductReviewIDis bound to the table’s primary key, which is an
auto-incre-mental identity column) LINQ is great in this way because it keeps the contents of your
objects and data tables in sync
The next listing, Listing 45.5, illustrates how to perform a delete using LINQ to SQL
LISTING 45.5 Deleting Rows Using LINQ to SQL
public void DeleteProductReview(int ProductReviewId)
{
AdventureWorks2008DataContext Context =
new AdventureWorks2008DataContext();
ProductReview Review
= (from m in Context.ProductReviews
where m.ProductReviewID == ProductReviewId
select m).FirstOrDefault();
if (Review != null)
{
using (TransactionScope Tran = new TransactionScope())
{
Context.ProductReviews.DeleteOnSubmit(Review);
Context.SubmitChanges();
}
}
}
To run Listing 45.5, you need to add a reference to System.Transactionsand then add a
usingstatement for that namespace This addition to the code illustrates one way to use
transactions with LINQ and also preserves the integrity of your AdventureWorks2008data
Going over the example, the DeleteProductReviewmethod takes a ProductReviewIDvalue
as input It then looks up that record using a LINQ query, just as you would in T-SQL If
the record was found (that is, if the query returns a non-null value), you then create a new
transaction, in which you delete the record by calling DeleteOnSubmit Note that because
you do not call the Completemethod of the Tranobject, the transaction is implicitly
rolled back
Trang 9Listing 45.6 rounds out our LINQ examples by showing you how to update a set of rows
LISTING 45.6 Updating Rows Using LINQ to SQL
public void UpdateProductInventories(int Threshold, short NewQty)
{
AdventureWorks2008DataContext Context =
new AdventureWorks2008DataContext();
List<ProductInventory> InventoryItems
= (from m in Context.ProductInventories
where m.Quantity < Threshold
select m).ToList();
if (InventoryItems.Count > 0)
{
using (TransactionScope Tran = new TransactionScope())
{
foreach (ProductInventory Item in InventoryItems)
{
Item.Quantity = NewQty;
}
Context.SubmitChanges();
}
}
}
In this listing, the query operates on a range of rows, rather than just one Using LINQ’s
ORM magic, you select rows from Production.ProductInventoryas a Listof
ProductInventoryby matching your Thresholdcriterion against the current Quantity
value of objects in DataContext.ProductInventories (Notice how LINQ even performs
grammatically correct pluralization of table names.) You iterate through each returned
object (again, within the scope of a transaction so as to keep AdventureWorks2008intact)
and then update the Quantityfor each Then you submit your changes Very simple, very
powerful, very much a timesaver
Although this LINQ to SQL primer covers the essentials, we highly recommend you dive
deeper into the NET namespaces you’ve seen to uncover all the possibilities LINQ to SQL
has to offer
Trang 10Using ADO.NET Data Services
ADO.NET Data Services (ADODS) is a platform for providing SQL Server data to websites,
RIAs (such as Silverlight and Flash applications), and other Internet clients over standard
HTTP using modern web development conventions
Getting Set Up
To start using ADODS, you first need to download and install Visual Studio 2008 Service
Pack 1, as well as the NET Framework 3.5 Service Pack 1 These updates include support
for the Microsoft Entity Framework (EF), ADODS core, and ADODS NET client library A
second configuration step, required for viewing ADODS XML data in the examples that
follow, is an Internet Explorer 8 settings change: using the Tools menu, click Internet
Options Click the Content tab and then click the Settings button in the Feeds and Web
Slices group On the Feeds and Web Slices dialog, uncheck the Turn on Feed Reading View
check box Click OK twice
Essentials
To work with data provided by ADODS services, you make HTTP requests, each of which
must include three key parts:
A uniform resource identifier (URI), which addresses the data in question
An HTTP verb (either GET,POST,MERGE,PUT, or DELETE), which indicates the type of
CRUD operation to be performed
An HTTPAcceptheader, which indicates the format of the data being sent or received
As of this writing, ADODS services provide data to clients in one of two formats:
Atom Publishing Protocol (AtomPub)—An XML format that acts as an
applica-tion-level protocol for working with web resources (This is the default ADODS
response format For more information on Atom, visit
http://en.wikipedia.org/wiki/Atom_(standard).)
JavaScript Object Notation (JSON)—A text-based format for representing
serial-ized JavaScript objects Many popular Asynchronous JavaScript and XML (AJAX)
client libraries (including JQuery, Prototype, and YUI) include core support for
work-ing with JSON
ADODS services rely on EF to provide an abstract mapping layer between a physical data
model and CLR object model EF is a general-purpose ORM (similar to a LINQ to SQL class)
that works with a number of data providers, including SQL Server, Oracle, DB2, and MySQL
The first step in working with ADODS is to create an EF Entity Data Model (EDM) that
includes the objects you want to expose from theAdventureWorks2008database to the Web