LISTING 20.1 LanguageChanges\App_Code\AutomaticProperties.cs public class AutomaticProperties { // Automatic Properties public int Id { get; set; } public string Description { get; set;
Trang 1CHAPTER 19 Building Data Access Components with ADO.NET
Runat=”server” />
<hr />
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”RandomDataLayer”
SelectMethod=”GetRandomMovies”
Runat=”server” />
</div>
</form>
</body>
</html>
Summary
This chapter provided you with an overview of ADO.NET It described how you can use
ADO.NET to represent database data with both a connected and disconnected model of
data access
In the first part, you learned how to use the Connection, Command, and DataReader objects
to connect to a database, execute commands, and represent the results of a database
query You learned how to retrieve provider statistics such as command execution times
You also learned how to represent stored procedures with the Command object Finally, you
learned how to work with multiple active resultsets (MARS)
In the second part, you learned how to work with the DataAdapter, DataTable, DataView,
and DataSet objects You learned how you can perform batch updates with the
DataAdapter object You also learned how to use the DataTable object to represent and
edit database rows
Next, you learned how to improve the data access performance of your ASP.NET pages by
executing asynchronous database commands within asynchronous ASP.NET pages
Finally, you got a chance to tackle the advanced topic of building database objects with
.NET Framework You learned how you can use NET Framework to build both
user-defined types and stored procedures For example, you learned how to insert and select a
custom class from a database table by creating a user-defined type with NET Framework
Trang 2Data Access with
LINQ to SQL
New C# and VB.NET Language Features
Creating LINQ to SQL Entities
Performing Standard Database Commands with LINQ to SQL
Creating a Custom LINQ Entity Base Class
Summary
A vast chasm separates the way developers work with
transient application data and the way developers work
with persistent database data In our applications, we work
with objects and properties (created with either C# or
VB.NET) In most databases, on the other hand, we work
with tables and columns
This is true even though our applications and our databases
represent the very same data For example, you might have
both a class and a database table named Product that
repre-sents a list of products you sell through your website
However, the languages we use to interact with these
enti-ties are different The C# and VB.NET languages are
differ-ent from the SQL language Larger companies typically have
different developers who specialize in C# or VB.NET, on the
one hand, or SQL, on the other hand
A huge amount of developer time is spent performing
brain-dead, tedious translations between the object and
relational universes I cringe when I think of the number of
hours I’ve spent declaring classes that contain a one-to-one
mapping between properties and database columns This is
time I could have devoted to going to the park with my
children, seeing a movie, walking my dog, and so on
LINQ to SQL promises to finally enable us to put SQL to a
well-deserved death Or more accurately, it promises to
make SQL a subterranean language that we never need to
interact with again (SQL is plumbing, and I am not a
plumber.) This is a good thing Death to SQL!
This is a hard chapter LINQ to SQL is not easy to
under-stand because it relies on several mind-bending features
Trang 3CHAPTER 20 Data Access with LINQ to SQL
introduced into C#, VB.NET, and NET Framework So please, have patience Take a deep
breath Everything will make sense in the end
This chapter is divided into four parts In the first part, I discuss the features introduced in
C#, VB.NET, and NET Framework that support LINQ Next, you learn how to represent
database tables with LINQ to SQL entities In the following part, I explain how to perform
standard SQL commands—such as SELECT, INSERT, UPDATE, and DELETE—with LINQ to
SQL In the final part of this chapter, I demonstrate how you can create a custom entity
base class (and integrate form validation into your LINQ entities)
New C# and VB.NET Language Features
To get LINQ to SQL to work, Microsoft had to introduce several new language features to
both C# and VB.NET Many of these features make C# and VB.NET behave more like a
dynamic language (think JavaScript) Although the primary motivation for introducing
these new features was to support LINQ, the new features are also interesting in their own
right
NOTE
LINQ was introduced in ASP.NET 3.5 These language features will not work on websites
targeting NET Framework 1.1, 2.0, or 3.0
Understanding Automatic Properties
The first of these language features we explore is automatic properties, which unfortunately
is supported only by C# and not VB.NET Automatic properties provide you with a
short-hand method for defining a new property For example, Listing 20.1 contains a class
named Product that contains Id, Description, and Price properties
LISTING 20.1 LanguageChanges\App_Code\AutomaticProperties.cs
public class AutomaticProperties
{
// Automatic Properties
public int Id { get; set; }
public string Description { get; set; }
// Normal Property
Trang 4public decimal Price
{
get { return _Price; }
set { _Price = value; }
}
}
The first two properties, Id and Description, unlike the last property, Price, do not
include getters or setters The C# compiler creates the getters and setters—and the secret,
private, backing fields—for you automatically
You can’t add any logic to the getters and setters for an automatic property You also can’t
create read-only automatic properties
Why are automatic properties relevant to LINQ to SQL? When working with LINQ to SQL,
you often use classes to represent nothing more than the list of columns you want to
retrieve from the database (the shape of the data) like the select list in a SQL query In
those cases, you just want to do the minimum amount of work possible to create a list of
properties, and automatic properties enable you to do this
NOTE
You can quickly add an automatic property to a class or page when using Visual Web
Developer/Visual Studio by typing prop and pressing the Tab key twice
Understanding Initializers
You can use initializers to reduce the amount of work it takes to create a new instance of a
class For example, assume that you have a class that looks like Listing 20.2 (in C#) or like
Listing 20.3 (in VB.NET)
LISTING 20.2 LanguageChanges\App_Code\Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Trang 5CHAPTER 20 Data Access with LINQ to SQL
LISTING 20.3 LanguageChanges\App_Code\Product.vb
Public Class Product
Private _Id As Integer
Public Property Id() As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
_Id = value
End Set
End Property
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Price As Decimal
Public Property Price() As Decimal
Get
Return _Price
End Get
Set(ByVal value As Decimal)
_Price = value
End Set
End Property
End Class
The Product class has three public properties (declared by taking advantage of automatic
properties in the case of C#—sorry VB.NET)
Now, let’s say you want to create an instance of the Product class Here’s how you would
Trang 6Product product1 = new Product();
product1.Id = 1;
product1.Name = “Laptop Computer”;
product1.Price = 800.00m;
And here is how you would do it in NET Framework 2 (with VB.NET):
Dim product1 As New Product()
product1.Id = 1
product1.Name = “Laptop Computer”
product1.Price = 800.0
It takes four lines of code to initialize this trivial little Product class That’s too much
work By taking advantage of initializers, you can do everything in a single line of code
Here’s how you use initializers in C#:
Product product2 = new Product {Id=1, Name=”Laptop Computer”, Price=800.00m};
Here’s how you use initializers in VB.NET:
Dim product2 As New Product() With {.Id = 1, Name = “Laptop Computer”,
➥.Price = 800.0}
Now, clearly, you could do something similar by declaring a constructor on the Product
class that accepts Id, Name, and Price parameters However, then your class would become
more bloated with code because you would need to assign the constructor parameter
values to the class properties Initializers are useful because, by taking advantage of this
feature, you can declare agile, little classes and initialize these svelte classes with a
minimum of code
Understanding Type Inference
Here’s a feature that makes C# and VB.NET look much more like a dynamic language such
as JavaScript: local variable type inference When you take advantage of type inference,
you allow the C# or VB.NET compiler to determine the type of a variable at compile time
Here’s an example of how you use type inference with C#:
var message = “Hello World!”;
And here is how you would use type inference with VB.NET:
Dim message = “Hello World!”
The message variable is declared without specifying a type The C# and VB.NET compilers
can infer the type of the variable (it’s a String) from the value you use to initialize the
variable No performance impact results from using type inference (The variable is not
late bound.) The compiler does all the work of figuring out the data type at compile time
Trang 7CHAPTER 20 Data Access with LINQ to SQL
A new keyword was introduced in ASP.NET 3.5 to support type inference: the var
keyword You declare a variable as type var when you want the compiler to figure out the
variable’s data type all by itself You can take advantage of type inference only when you
provide a local variable with an initial value For example, this won’t work (C#):
var message;
message = “Hello World!”;
The C# compiler will refuse to compile this code because the message variable is not
initialized when it is declared
The following code will work in VB.NET (but it won’t do what you want):
Dim message
message = “Hello World!”
In this case, VB.NET will treat the message variable as type Object At runtime, it will cast
the value of the variable to a string when you assign the string to the variable This is not
good from a performance perspective
NOTE
VB.NET includes an Option Infer option that must be enabled for the implicit typing
fea-ture to work You can enable it for a particular class file by adding the line Option
Infer On at the top of a code file
The relevance of type inference to LINQ to SQL will be apparent after you read the next
section In many circumstances when using LINQ to SQL, you won’t actually know the
name of the type of a variable, so you have to let the compiler infer the type
Understanding Anonymous Types
Anonymous types is another idea that might be familiar to you from dynamic languages
Anonymous types are useful when you need a transient, fleeting type and you don’t want
to do the work to create a class
Here’s an example of creating an anonymous type in C#:
var customer = new {FirstName = “Stephen”, LastName = “Walther”};
Here’s how you would create the same anonymous type in VB.NET:
Dim customer = New With {.FirstName = “Stephen”, LastName = “Walther”}
Trang 8The customer variable is used without specifying a type, which looks very much like
JavaScript or VBScript However, you need to understand that customer does have a type;
you just don’t know its name: It’s anonymous
In a single line of code, we’ve managed to both create a new class and initialize its
proper-ties The terseness brings tears to my eyes
Anonymous types are useful when working with LINQ to SQL because you’ll discover that
you often need to create new types on-the-fly For example, you might want to return a
class that represents a limited set of database columns when performing a particular query
You need to create a transient class that represents the columns
Understanding Generics
Generics are not a new feature; however, they are such an important aspect of LINQ to
SQL that it is worth using a little space to review this feature
NOTE
To use generics, you need to import the System.Collections.Generic namespace
I most often use generics by taking advantage of generic collections For example, if you
want to represent a list of strings, you can declare a list of strings like this (in C#):
List<string> stuffToBuy = new List<string>();
stuffToBuy.Add(“socks”);
stuffToBuy.Add(“beer”);
stuffToBuy.Add(“cigars”);
Here’s how you would declare the list of strings in VB.NET:
Dim stuffToBuy As New List(Of String)
stuffToBuy.Add(“socks”)
stuffToBuy.Add(“beer”)
stuffToBuy.Add(“cigars”)
And, by taking advantage of collection initializers, you can now declare a strongly typed
list of strings in a single line like this (in C#):
List<string> stuffToBuy2 = new List<string> {“socks”, “beer”, “cigars”};
NOTE
Unfortunately, VB.NET does not support collection intializers or array initializers
Trang 9CHAPTER 20 Data Access with LINQ to SQL
The List class is an example of a generic because you specify the type of object that the
class will contain when you declare the List class In C#, you specify the type in between
the alligator mouths (< >), and in VB.NET you use the Of keyword In the preceding
examples, we created a List class that contains strings Alternatively, we could have
created a List class that contains integers or a custom type such as products or customers
represented by a Product or Customer class
A generic collection such as a List is superior to a nongeneric collection such as an
ArrayList because a generic is strongly typed An ArrayList stores everything as an object A
generic stores everything as a particular type When you pull an item out of an ArrayList,
you must cast it to a particular type before you use it An item pulled from a generic, on
the other hand, does not need to be cast to a type
Generics are not limited solely to collections You can create generic methods, generic
classes, and generic interfaces For example, when working with ADO.NET classes, I like to
convert my data readers into strongly typed List collections The method
GetListFromCommand(), shown in Listing 20.4, takes a command object, executes it, and
generates a typed List automatically
LISTING 20.4 LanguageChanges\App_Code\GenericMethods.cs
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
public class GenericMethods
{
public static List<T> GetListFromCommand<T>(SqlCommand command)
where T: ICreatable, new()
{
List<T> results = new List<T>();
using (command.Connection)
{
command.Connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
T newThing = new T();
newThing.Create(reader);
results.Add( newThing );
}
}
return results;
}
Trang 10public interface ICreatable
{
void Create(SqlDataReader reader);
}
The GetListFromCommand() method in Listing 20.4 accepts a SqlCommand object and
returns a generic List<T> The generic type is constrained by the where clause The generic
constraint restricts the types that can be used for T to types that implement the
ICreatable interface and types that can be instantiated with new
The ICreatable interface is also defined in Listing 20.4 The interface requires a class to
implement a single method named Create()
Now that we have created a generic method for converting data readers into strongly
typed lists, we can use it with any class that implements the ICreatable interface, such as
the Movie class in Listing 20.5
LISTING 20.5 Movie.cs
using System;
using System.Data.SqlClient;
public class Movie : ICreatable
{
public int Id { get; set; }
public string Title { get; set; }
public void Create(SqlDataReader reader)
{
Id = (int)reader[“Id”];
Title = (string)reader[“Title”];
}
}
You can call the generic GetListFromCommand() method with the Movie type like this (the
page named ShowGenericMethods.aspx on the book’s website uses this code):
string conString = WebConfigurationManager.ConnectionStrings[“con”]
➥ConnectionString;
SqlConnection con = new SqlConnection(conString);
SqlCommand cmd = new SqlCommand(“SELECT Id, Title FROM Movie”, con);
List<Movie> movies = GenericMethods.GetListFromCommand<Movie>(cmd);