Chapter 20Using LINQ to SQL After completing this chapter, you will be able to: ■ ■ Build LINQ queries that use the LINQ to SQL provider ■ ■ Understand how LINQ to SQL prepares queries f
Trang 1Chapter 20
Using LINQ to SQL
After completing this chapter, you will be able to:
■
■ Build LINQ queries that use the LINQ to SQL provider
■
■ Understand how LINQ to SQL prepares queries for processing
■
■ Determine when to use LINQ to SQL over LINQ to Entities
LINQ is an extensible system, enabling a consistent querying experience against different data platforms Sometimes these different systems overlap, providing access to the same target platform through different providers SQL Server is one such database platform LINQ
to DataSet and LINQ to Entities both allow you to build LINQ queries that interact with data sourced from SQL Server, either directly (LINQ to Entities) or indirectly (LINQ to DataSet) LINQ to SQL, also included as a native LINQ provider within the NET Framework, provides a third option for processing SQL Server data
This chapter focuses on the LINQ to SQL provider and the benefits it supplies to your data-focused application In many ways, LINQ to SQL feels like LINQ to Entities, especially when using its class-generation feature However, LINQ to SQL was built specifically to interact with SQL Server database tables, and its queries reflect that closer relationship
Note In October 2008, soon after the announcement of its plans for Visual Studio 10 and the related NET Framework 4.0 release, Microsoft provided guidance on the future of its LINQ to SQL provider This “Update on LINQ to SQL and LINQ to Entities Roadmap” blog entry posted
by the ADO.NET team
(http://blogs.msdn.com/b/adonet/archive/2008/10/29/update-on-linq-to-sql-and-linq-to-entities-roadmap.aspx) indicated that the Entity Framework would be the
“rec-ommended data access solution for LINQ to relational scenarios.” The posting also included a commitment to evolve the LINQ to SQL product based on customer feedback.
Note The exercises in this chapter all use the same sample project, a tool that makes queries using LINQ to SQL Although you will be able to run the application after each exercise, the ex-pected results for the full application might not appear until you complete all exercises in the chapter.
Trang 2LINQ to SQL is a LINQ provider that targets SQL Server databases Its simple configuration needs and its focus on the logical organization of the underlying tables make it a useful tool for applications that need easy access to a specific SQL Server database
Comparing LINQ to SQL with LINQ to Entities
The LINQ to SQL provider first appeared with the initial release of LINQ, part of Visual Studio
2008 and the NET Framework 3.5 It preceded the release of LINQ to Entities, which was delivered as part of the NET Framework 3.5 SP1 The two platforms share many similarities, including the following:
■
■ Modeling of data entities using an XML-based schema language
■
■ A Visual Studio–hosted visual designer that simplifies XML model creation
■
■ Generation of a language-specific class layer from the model
■
■ Support for database updates using custom stored procedures
■
■ Delayed query processing through LINQ-constructed SQL statements
Despite these similarities, the two systems diverge significantly in many respects These dif-ferences often help determine which of the two solutions is best for a given application The two systems differ in four key areas:
■
■ Platform support The Entity Framework and its LINQ to Entities extension include
support for a wide range of database platforms In contrast, LINQ to SQL communicates only with SQL Server 2000 and later, including SQL Server Compact 3.5
Note The visual designer used to set up LINQ to SQL models does not work with SQL Server Compact 3.5.
■
■ Model focus LINQ to Entities queries focus on the Entity Framework conceptual
model, which is an abstraction of the underlying logical database model This model can differ significantly from the organization presented within the database In LINQ to SQL, the model closely reflects the database tables that support it
■
■ Overhead LINQ to SQL is extremely lightweight compared with its Entity Framework
counterpart EF’s three model layers allow for tremendous flexibility, but such design comes at a performance and memory overhead cost
Trang 3Using LINQ to SQL, especially when building models with its visual designer, is straightforward and often much quicker than setting up a LINQ to Entities environment Microsoft’s official encouragement to use the Entity Framework option may help guide your decision, but for applications that have simple needs and access to SQL Server data, LINQ to SQL may be the best query platform
Understanding the Components of LINQ to SQL
The focus of an Entity Framework model is the XML-based definition of the three different layers: conceptual, storage, and mapping While LINQ to SQL can use an XML definition as
the basis for a specific data implementation, the focus of each table definition is the entity
class, a standard NET class decorated with attributes from the System.Data.Linq.Mapping
namespace
C#
[Table(Name="UnitOfMeasure")]
public class UnitOfMeasure
{
// - As defined in the database:
// ID bigint
// ShortName varchar(15)
// FullName varchar(50)
[Column(IsPrimaryKey = true)] public long ID;
[Column] public string ShortName;
[Column] public string FullName;
}
Visual Basic
<Table(Name:="UnitOfMeasure")>
Public Class UnitOfMeasure
' - As defined in the database:
' ID bigint
' ShortName varchar(15)
' FullName varchar(50)
<Column(IsPrimaryKey:=True)> Public ID As Long
<Column> Public ShortName As String
<Column> Public FullName As String
End Class
Trang 4Attributes, such as the TableAttribute and ColumnAttribute shown in this code block, inform
LINQ to SQL how to map class members to tables and columns in the database Additional attributes identify storage-level data types, intertable relationships, stored procedure defini-tions, and other key items that let the application communicate cleanly with the external data source
The System.Data.Linq.DataContext class binds these class definitions with actual data
and acts much like the ObjectContext class in the Entity Framework Derived versions of
DataContext include instances of the decorated classes, forming a class-style representation
of the actual database
C#
public class SalesOrderLink : DataContext
{
// - Constructor establishes database connection.
public SalesOrder(string connectionString):
base(connectionString) {}
// - Table definitions to link with database.
public Table<Customer> Customers;
public Table<OrderEntry> Orders;
public Table<UnitOfMeasure> UnitsOfMeasure;
}
Visual Basic
Public Class SalesOrderLink
Inherits DataContext
' - Constructor establishes database connection.
Public Sub New(ByVal connectionString As String)
MyBase.New(connectionString)
End Sub
' - Table definitions to link with database.
Public Customers As Table(Of Customer)
Public Orders As Table(Of OrderEntry)
Public UnitsOfMeasure As Table(Of UnitOfMeasure)
End Class
After you have a defined context, using it with LINQ is a simple matter of creating an in-stance of the context and adding its exposed members to a query
Trang 5using (SalesOrderLink context = new SalesOrderLink(connectionString))
{
var results = from cu in context.Customers
orderby cu.FullName
select new { CustomerID = cu.ID, CustomerName = cu.FullName };
}
Visual Basic
Using context As New SalesOrderLink(connectionString)
Dim results = From cu In context.Customers
Order By cu.FullName
Select CustomerID = cu.ID, CustomerName = cu.FullName
End Using
Except for the replacement of an ObjectContext by a DataContext, this query is identical to
the first LINQ to Entities query included in Chapter 19, “Using LINQ to Entities.”
Like its Entity Framework complement, LINQ to SQL uses the clauses in the query (in either the standard LINQ form shown here or one built with extension methods) to craft SQL state-ments that retrieve, project, filter, and sort the data returned by the query Because of this, you are limited in the types of non-LINQ-To-SQL data that you can include in the query Only data content and functionality that can be represented easily in a SQL statement are candi-dates for inclusion in a LINQ to SQL query
Despite this limitation, LINQ to SQL does a pretty good job at converting ordinary NET
ele-ments into SQL equivalents Comparisons with null (C#) and Nothing (Visual Basic) translate into the expected IS NULL and IS NOT NULL forms Using the Visual Basic Like pattern-matching operator results in a similar LIKE comparison in the generated SQL Including the
AddDays method on a date value within a LINQ to SQL query properly converts the
expres-sion into one that uses the related DATEADD function in T-SQL For a complete list of all NET
features that LINQ to SQL can use in database queries, see the “Data Types and Functions (LINQ to SQL)” entry in the Visual Studio online help
Using the Object Relational Designer
Although you can handcraft your own entity classes, a better option for large databases (and
even small ones) is to use the Object Relational (O/R) Designer, a drag-and-drop visual
de-signer that generates LINQ to SQL classes based on a graphical database model To use the O/R Designer, add a new “LINQ to SQL Classes” item to your Visual Basic or C# project
Trang 6The Designer adds a dbml file to your project, which hosts the data model in XML form It
also adds two support files: (1) a dbml.layout file that stores some designer-specific informa-tion; and (2) a designer.vb or designer.cs file that holds the generated entity classes in either
Visual Basic or C# The Designer produces the designer file content as you make content-related changes to the visual design
You build your data model by dragging classes (entities) and associations (relationships) to the left pane of the designer surface These entity and relationship instances are either generic forms from the Visual Studio Toolbox or existing database elements from the Server Explorer (or Database Explorer in Visual Studio Express Editions) tool window, as shown in Figure 20-1 You can also include database-level stored procedures and custom functions in the model by dragging them to the Designer’s right-side pane
FIGURE 20-1 The Designer after dragging and dropping an existing database table.
Any changes you make to the model result in an immediate adjustment to the generated language-specific source code You should not customize these generated class files because any future changes to the model will overwrite your changes
Note You can generate both the XML model and the class layer from a source database with-out using the visual designer The Windows SDK installed with Visual Studio includes a program
named SqlMetal.exe This tool generates output similar to that of the visual designer, but it does
so through a command-line interface See the “SqlMetal.exe (Code Generation Tool)” entry in the Visual Studio online help for information on using this application.
Trang 7Building a LINQ to SQL Model
1 Open the “Chapter 20 CSharp” (C#) or “Chapter 20 VB” (Visual Basic) project from
the installed samples folder The project includes three Windows.Forms classes:
OrderViewer, StatesByYear, and Switchboard.
2 Add a new LINQ to SQL data model to the application through the Project | Add New
Item menu command When the Add New Item form appears, select LINQ to SQL
Classes from the list of templates and enter SalesOrder.dbml in the Name field Click
the Add button
3 Visual Studio displays a blank Object Relational Designer Open the Server Explorer
(or Database Explorer) tool window (use the View | Server Explorer or View | Database Explorer menu if the window is not already present in Visual Studio) to display the contents of the book’s sample database If the sample database is not already present
in the Data Connections tree, use the Connect To Database toolbar button within the Server Explorer to locate the database
4 Expand the sample database tree in the Server Explorer and drag the following items to the left half of the O/R Designer surface: Customer, OrderEntry, and StateRegion.
Trang 8As you drag each item, the Designer automatically creates associations between the entities based on foreign key references defined in the database schema Click each en-tity and association, and then view the Properties panel to review the different settings associated with each element
5 Drag the AdmittedInYear custom function from the Server Explorer tree to the right half
of the designer surface This adds a reference to a database-level function, allowing it
to be used in your LINQ to SQL queries
Trang 96 Save changes to the file and close it The Designer has already generated the class layer
for the database objects dragged to the design surface You can view the generated
content by opening SalesOrder.designer.cs (C#) or SalesOrder.designer.vb (Visual Basic,
although you may need to click the Show All Files button in the Solution Explorer to see the file)
Using Custom Database Functions in Queries
Although custom functions defined within a NET application cannot participate directly in
a LINQ to SQL query, these same queries can easily access functions written at the database level When dragged to the design surface (as was done in the example shown previously), these T-SQL functions become part of the context that also hosts the entity classes
LINQ to Entities includes a similar feature, but it requires you to create local NET stubs in your own code With LINQ to SQL, the functions are ready to use in your queries; simply ref-erence the function name as a member of the instantiated context, passing the appropriate parameters as defined within the database
C#
// - Assumes an AgedInvoices database function that
// accepts a customer ID and a number of days,
// returning a financial amount.
var result = from cu in context.Customers
orderby cu.FullName
select new { cu.ID, cu.FullName, context.AgedInvoices(cu.ID, 90) };
Visual Basic
' - Assumes an AgedInvoices database function that
' accepts a customer ID and a number of days,
' returning a financial amount.
Dim result = From cu In context.Customers
Select cu.ID, cu.FullName, context.AgedInvoices(cu.ID, 90)
Order By cu.FullName
You can also call these functions directly, as long as a valid context exists
C#
decimal pending = context.AgedInvoices(whichCustomer, 90);
Visual Basic
Dim pending As Decimal = context.AgedInvoices(whichCustomer, 90)
Trang 10Querying with LINQ to SQL: C#
Note This exercise parallels exercises found in Chapter 19 The project is nearly identical in func-tionality and purpose, but it uses LINQ to SQL instead of LINQ to Entities to process database content.
This exercise continues the previous exercise in this chapter.
1 Open the source code view for the General class Locate the GetConnectionString
func-tion; this is a routine that uses a SqlConnectionStringBuilder to create a valid connection
string to the sample database It currently includes the following statements:
sqlPortion.DataSource = @"(local)\SQLExpress";
sqlPortion.InitialCatalog = "StepSample";
sqlPortion.IntegratedSecurity = true;
Adjust these statements as needed to provide access to your own test database
2 Open the source code view for the StatesByYear form This form will access the
AdmittedInYear database function, which was dragged into the model in the prior
example
3 Locate the StatesByYear_Load event handler; this is a routine that loads the data onto
the form Add the following code as the body of the routine:
using (SalesOrderDataContext context = new
SalesOrderDataContext(GetConnectionString()))
{
var result = from st in context.StateRegions
where st.Admitted != null
orderby st.Admitted.Value.Year
select new { StateName = st.FullName,
AdmitYear = st.Admitted.Value.Year,
TotalAdmittedInYear = context.AdmittedInYear(st.Admitted) };
StateAdmittance.DataSource = result.ToList();
}
In addition to calling the custom function AdmittedInYear, this query also uses != null as
a condition, which will translate into the appropriate T-SQL comparison clause
4 Run the program When the Switchboard form appears, click States By Year When the
StatesByYear form appears, the results of the query will display in the form’s main grid.