The class in Listing 20.29 contains a method called GetDynamicSort that returns a dynamic lambda expression that can be used with either the OrderBy or OrderByDescendingmethod.. using Sy
Trang 1You can build LINQ to SQL query expressions dynamically by taking advantage of the
System.Linq.Expressions.Expression class This class contains all the methods for
build-ing query expressions dynamically Here is a (very partial) list of methods supported by
this class:
Add()—Creates an expression that represents addition.
And()—Creates an expression that represents a logical AND.
Condition()—Creates an expression that represents a condition.
Constant()—Creates an expression that represents a constant value.
Convert()—Creates an expression that represents a conversion from one type to
another
Divide()—Creates an expression that represents division.
Equal()—Creates an expression that represents whether two expressions are equal.
Field()—Creates an expression that represents a field.
Lambda()—Creates a lambda expression.
Multiply()—Creates an expression that represents multiplication.
Or()—Creates an expression that represents a logical OR.
Parameter()—Creates an expression that represents a function parameter.
Property()—Creates an expression that represents accessing a property.
PropertyOrField()—Creates an expression that represents accessing a property or
field
Subtract()—Creates an expression that represents subtraction.
Again, this is not a complete list of methods supported by the Expression class However,
it should give you some idea of how you can go about building expressions
Let’s discuss a real-world situation in which you need dynamic LINQ to SQL expressions:
sorting If you want to enable sorting when using a GridView control with LINQ to SQL,
you have a choice You can create a switch (SELECT CASE) block to sort by every possible
column that a user can click, or you can create a dynamic LINQ to SQL expression
The class in Listing 20.29 contains a method called GetDynamicSort() that returns a
dynamic lambda expression that can be used with either the OrderBy() or
OrderByDescending()method
LISTING 20.29 Standard\App_Code\Movie.cs
using System;
using System.Web;
using System.Collections.Generic;
using System.Linq;
Trang 2using System.Linq.Expressions;
using System.Data.Linq;
using System.Reflection;
public partial class Movie
{
public static IEnumerable<Movie> Select(string orderBy)
{
string orderByColumn = “Id”;
string orderByDirection = “asc”;
if (!String.IsNullOrEmpty(orderBy))
ParseOrderBy(orderBy, ref orderByColumn, ref orderByDirection);
MyDatabaseDataContext db = new MyDatabaseDataContext();
if (orderByDirection == “asc”)
return db.Movies.OrderBy(GetDynamicSort(orderByColumn));
else
return db.Movies.OrderByDescending(GetDynamicSort(orderByColumn));
}
public static void ParseOrderBy
(
string orderBy,
ref string orderByColumn,
ref string orderByDirection
)
{
string[] orderByParts = orderBy.Split(‘ ‘);
orderByColumn = orderByParts[0];
if (orderByParts.Length > 1)
orderByDirection = orderByParts[1].ToLower();
}
private static Expression<Func<Movie, string>> GetDynamicSort
(
string orderByColumn
)
{
// Create expression to represent Movie parameter into lambda expression
ParameterExpression pMovie = Expression.Parameter(typeof(Movie), “m”);
Trang 3// Create expression to access value of order by column
PropertyInfo propInfo = typeof(Movie).GetProperty(orderByColumn);
MemberExpression m = Expression.MakeMemberAccess(pMovie, propInfo);
// Box it
UnaryExpression b = Expression.TypeAs(m, typeof(object));
// Convert to string
MethodInfo convertMethod = typeof(Convert).GetMethod(“ToString”,
new Type[] { typeof(object) });
MethodCallExpression c = Expression.Call(null, convertMethod, b);
// Return lambda
return Expression.Lambda<Func<Movie, string>>(c, pMovie);
}
}
The GetDynamicSort() method builds a lambda expression dynamically and creates an
expression that looks like this:
m => Convert.ToString(m.Id As Object)
When the LINQ to SQL query gets translated to SQL, the following SQL command is
executed:
SELECT [t0].[Id], [t0].[CategoryId], [t0].[Title],
[t0].[Director], [t0].[DateReleased], [t0].[InTheaters],
[t0].[BoxOfficeTotals], [t0].[Description], [t0].[Version]
FROM [dbo].[Movie] AS [t0]
ORDER BY CONVERT(NVarChar(MAX),[t0].[Title])
You can use the class in Listing 20.29 with the ASP.NET page in Listing 20.30 When you
click a header column in GridView, it is sorted by the column
LISTING 20.30 Standard\ShowDynamicSort.aspx
<%@ Page Language=”C#” trace=”true” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Dynamic Sort</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
Trang 4<asp:GridView
id=”grdMovies”
DataSourceId=”srcMovies”
AllowSorting=”true”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie”
SelectMethod=”Select”
SortParameterName=”orderBy”
Runat=”server” />
</div>
</form>
</body>
</html>
The GridView control has its AllowSorting attribute set to the value true and the
ObjectDataSource control has its SortParameterName attribute set to the value orderBy
The page is set up to enable data source paging
NOTE
The GetDynamicSort() method described in this section does a sort after converting
the values of a column to strings For nonstring data types such as dates and integers,
doing string sorts produces the wrong results For example, after sorting, the id column
is ordered as 10, 2, 22, 29, 3, 30, 31, and so on
In the final part of this chapter, you learn how to create a custom entity base class that
implements a more sophisticated version of a dynamic sort that sorts different column
types correctly
Debugging LINQ to SQL
For the sake of performance, you had better know what is going on beneath the covers
when you execute LINQ to SQL queries In particular, it is useful to know how your LINQ
to SQL queries get translated into SQL and when your LINQ to SQL queries execute In
this section, I describe three methods of debugging LINQ to SQL
Trang 5Using the LINQ to SQL Debug Visualizer
The LINQ to SQL Debug Visualizer is a useful tool for viewing how a LINQ to SQL query
translates into SQL The LINQ to SQL Debug Visualizer is not included with NET
Framework You need to download it from the following address: http://www.scottgu.com/
blogposts/linqquery/SqlServerQueryVisualizer.zip
After you download the LINQ to SQL Visualizer, you can use it like other Visualizers in
Visual Web Developer and Visual Studio by compiling it and placing the resulting DLL in
the My Documents\Visual Studio 2010\Visualizers folder Then, If you set a breakpoint
after a LINQ to SQL query and hover your mouse over the query, you can click the
magni-fying glass to see the full SQL command into which the query gets translated (see Figure
20.1) You also have the option of executing the SQL query directly from the Visualizer
FIGURE 20.1 Using the LINQ to SQL Debug Visualizer
Logging LINQ to SQL Queries
My favorite method of debugging LINQ to SQL queries is to log all the DataContext
output to ASP.NET trace That way, I can see all the LINQ to SQL queries that execute at
the bottom of each of my ASP.NET pages (see Figure 20.2)
The DataContext class includes a Log property You can assign a TextWriter to the Log
property, and DataContext writes to this TextWriter whenever it executes a query
Unfortunately, the NET Framework does not include a TextWriter that writes to ASP.NET
Trace Fortunately, it is not that difficult to write one, and I’ve included the code for a
Trace TextWriter in Listing 20.31
Trang 6LISTING 20.31 Standard\TraceWriter.cs
using System;
using System.Text;
using System.Web;
using System.IO;
using System.Globalization;
public class TraceWriter : TextWriter
{
public override void Write(string value)
{
HttpContext.Current.Trace.Warn(value);
}
public override void Write(char[] buffer, int index, int count)
{
HttpContext.Current.Trace.Warn(“Linq”, new string(buffer, index, count));
}
public override Encoding Encoding
{
FIGURE 20.2 Logging LINQ to Trace
Trang 7get { return Encoding.Unicode; }
}
public TraceWriter()
: base(CultureInfo.CurrentCulture)
{
}
}
After you drop the class in Listing 20.31 in your App_Code folder, you can set the
DataContext class to write to the TraceWriter like this:
MyDatabaseDataContext db = new MyDatabaseDataContext();
db.Log = new TraceWriter();
grd.DataSource = db.Movies.Where(m=>m.Id==2);
grd.DataBind();
After you set up the TraceWriter, you can enable Trace (by adding the Trace=”true” attribute
to the <%@ Page %> directive) on any page that uses a LINQ query and view the output
NOTE
The LINQ entity base class we create in the last part of this chapter automatically logs
all output to the TraceWriter
Using the GetCommand Method
Finally, you can use the DataContext.GetCommand() method to get the ADO.NET
command object that executes when a LINQ to SQL query executes After you grab the
command object, you can examine its parameters or its command text
The following code assigns the command text of a command associated with a LINQ to
SQL query to a Label control:
MyDatabaseDataContext db = new MyDatabaseDataContext();
var query = db.Movies.Where(m=>m.Id==2);
lblQuery.Text = db.GetCommand(query).CommandText;
The following SELECT command is displayed in the Label control:
SELECT [t0].[Id], [t0].[CategoryId], [t0].[Title], [t0].[Director],
[t0].[DateReleased], [t0].[InTheaters], [t0].[BoxOfficeTotals],
[t0].[Description],
[t0].[Version] FROM [dbo].[Movie] AS [t0] WHERE [t0].[Id] = @p0
Trang 8Creating a Custom LINQ Entity Base Class
In this final part of this chapter, we build a custom LINQ to SQL base class Our base class
contains standard methods for selecting records, inserting records, updating records, and
deleting records It also supports paging and caching Finally, our base class contains
methods for performing validation
The files for the custom classes can be found on the website that accompanies this book
in the folder EntityBaseClasses This folder contains the following files:
EntityBase—A custom base class for LINQ to SQL entities.
EntityDataSource—A custom data source control derived from the
ObjectDataSource control for representing LINQ to SQL entities
EntityValidator—A custom validation control.
EntityCallOutValidator—A custom validation control that displays a call-out
vali-dation error message
ValidationError—A class that represents a validation error.
ValidationErrorCollection—A collection of validation errors.
ValidationException—An exception thrown when there is a validation error.
TraceWriter—A class for logging LINQ to SQL queries to ASP.NET trace.
The motivation for writing these classes was to make standard database operations easier
when using LINQ to SQL I discovered that I was writing the exact same queries and
commands over and over again whenever I created a new entity Writing a standard base
class made my life easier because it freed me from writing the same repetitive code
Using the Entity Base Class
Follow these steps to use the custom entity base classes:
1 Create a new website
2 Add an App_Code folder to your website, and copy the EntityBaseClasses folder to
the App_Code folder
3 Create one or more LINQ to SQL entities with the help of the LINQ to SQL Designer
4 Add a connection string named con to your database in the web.config file
5 Create a separate partial class for each LINQ to SQL entity and derive the class from
the EntityBase class
6 Create an empty Validate() method for each entity class
For example, imagine that you have used the LINQ to SQL Designer to create an entity
named Movie You created the Movie entity by dragging the Movie database table from the
Database Explorer (Server Explorer) window onto the LINQ to SQL Designer surface At
this point, you are ready to inherit your new Movie entity from the EntityBase class
Trang 9Listing 20.32 contains the file that you add to your website to create a Movie entity that
inherits from the EntityBase class
LISTING 20.32 ShowEntityBase\App_Code\Movie.cs
using System;
public partial class Movie : EntityBase<Movie>
{
protected override void Validate()
{
}
}
Now that you have derived the Movie entity from EntityBase, the Movie class inherits
methods for selecting, inserting, updating, and deleting records
Performing Standard Data-Access Operations with the EntityBase
Class
Any entity that you inherit from the EntityBase class inherits the following methods
automatically:
Select()—Selects all entities.
Select(string orderBy)—Selects all entities in a certain order.
SelectCached()—Selects all entities from the cache.
SelectCached(string orderBy)—Selects all entities from the cache in a certain
order
Select(int startRowIndex, int maximumRows)—Selects a page of entities.
Select(int startRowIndex, int maximumRows, orderBy)—Selects a page of entities
in a certain order
SelectCount()—Returns a count of entities.
SelectCount(string orderBy)—Returns a count of entities.
SelectCountCached()—Returns a count of entities from the cache.
Get(int? Id)—Gets a single entity using the entity’s identity value.
Save(T oldEntity, T newEntity)—Either performs an insert or update depending
on whether the identity value is 0
Insert(T entityToInsert)—Inserts a new entity.
Update(T oldEntity, T newEntity)—Updates an existing entity.
Trang 10Two of these methods—Get() and Save()—require that the database table an entity
repre-sents include an identity column The other methods do not make this assumption
The page in Listing 20.33 illustrates how you can use these methods
LISTING 20.33 ShowEntityBase\SelectPagedSortedMovies.aspx
<%@ Page Language=”C#” Trace=”true” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head id=”Head1” runat=”server”>
<title>Select Paged Sorted Movies</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceId=”srcMovies”
AllowPaging=”true”
AllowSorting=”true”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie”
SelectMethod=”Select”
EnablePaging=”true”
SelectCountMethod=”SelectCountCached”
SortParameterName=”orderBy”
Runat=”Server” />
</div>
</form>
</body>
</html>
The page in Listing 20.33 contains a GridView control bound to an ObjectDataSource
control The ObjectDataSource control represents the Movie entity The ObjectDataSource
is configured to support data source paging and sorting You get both the Select() and
SelectCountCached() methods for free from the EntityBase class
The EntityBaseClasses folder also contains a control named EntityDataSource, which can
be used instead of the normal ObjectDataSource The EntityDataSource control inherits