Updating with LINQ to SQL You can update a LINQ to SQL entity and the underlying database table by modifying the entity’s properties and calling the DataContext’s SubmitChanges method, l
Trang 1The page in Listing 20.23 contains a FormView control and a GridView control You can
use FormView to insert new movie records into the database The FormView control is
bound to an ObjectDataSource control that represents the Movie class
LISTING 20.23 Standard\InsertMovie.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>Insert Movie</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:FormView
id=”frmMovie”
DataSourceID=”srcMovies”
DefaultMode=”Insert”
Runat=”Server”>
<InsertItemTemplate>
<asp:Label
id=”lblTitle”
Text=”Title:”
AssociatedControlID=”txtTitle”
Runat=”server” />
<br />
<asp:TextBox
id=”txtTitle”
Text=’<%# Bind(“Title”) %>’
Runat=”server” />
<br /><br />
<asp:Label
id=”lblCategory”
Text=”Category:”
AssociatedControlID=”ddlCategory”
Runat=”server” />
<br />
<asp:DropDownList
id=”ddlCategory”
DataSourceId=”srcMovieCategories”
Trang 2SelectedValue=’<%# Bind(“CategoryId”) %>’
DataTextField=”Name”
DataValueField=”Id”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovieCategories”
TypeName=”MovieCategory”
SelectMethod=”Select”
Runat=”Server” />
<br /><br / >
<asp:Label
id=”lblDirector”
Text=”Director:”
AssociatedControlID=”txtDirector”
Runat=”server” />
<br />
<asp:TextBox
id=”txtDirector”
Text=’<%# Bind(“Director”) %>’
Runat=”server” />
<br /><br />
<asp:Label
id=”lblDescription”
Text=”Description:”
AssociatedControlID=”txtDescription”
Runat=”server” />
<br />
<asp:TextBox
id=”txtDescription”
Text=’<%# Bind(“Description”) %>’
TextMode=”MultiLine”
Columns=”60”
Rows=”3”
Runat=”server” />
<br /><br />
<asp:Label
id=”lblDateReleased”
Text=”Date Released:”
AssociatedControlID=”txtDateReleased”
Runat=”server” />
Trang 3<br />
<asp:TextBox
id=”txtDateReleased”
Text=’<%# Bind(“DateReleased”) %>’
Runat=”server” />
<br /><br />
<asp:Button
id=”btnInsert”
Text=”Insert”
CommandName=”Insert”
Runat=”server” />
</InsertItemTemplate>
</asp:FormView>
<hr />
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie”
DataObjectTypeName=”Movie”
SelectMethod=”Select”
InsertMethod=”Insert”
Runat=”server” />
</div>
</form>
</body>
</html>
The ObjectDataSource control in Listing 20.23 includes a DataObjectTypeName attribute
that is set to the value Movie ObjectDataSource instantiates a new Movie object
automati-cally when calling the Movie.Insert() method
Updating with LINQ to SQL
You can update a LINQ to SQL entity and the underlying database table by modifying the
entity’s properties and calling the DataContext’s SubmitChanges() method, like this:
Trang 4MyDatabaseDataContext db = new MyDatabaseDataContext();
Movie movieToUpdate = db.Movies.Single(m=>m.Id==1);
movieToUpdate.Title = “King Kong II”;
movieToUpdate.Director = “George Lucas”;
db.SubmitChanges();
This code first grabs the movie that has an Id value of 1 Next, the movie Title and
Director properties are modified Finally, these changes are submitted to the database by
calling the SubmitChanges() method
This code works, but it is not the best code to use when building an ASP.NET page Typically,
when performing an update in ASP.NET, you already have information about the entity in
view state You don’t want or need to grab the entity from the database to modify it
For example, if you use a FormView control to update the database, the FormView control
will do a select automatically and store the entity information in view state You don’t
want to grab the entity information a second time after the user clicks the Insert button
Instead, what you want to do is reattach the entity back into the DataContext from view
state You already have the entity; you just want to make the DataContext aware of the
entity again
This approach is illustrated in the class in Listing 20.24
LISTING 20.24 Standard\App_Code\Movie.cs
using System;
using System.Web;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
public partial class Movie
{
public static void Update(Movie oldMovie, Movie newMovie)
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
db.Movies.Attach(oldMovie);
oldMovie.Title = newMovie.Title;
oldMovie.Director = newMovie.Director;
db.SubmitChanges();
}
public static IEnumerable<Movie> Select()
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
Trang 5return db.Movies;
}
}
The Update() method in Listing 20.24 receives both the original and new version of the
Movie entity First, the old version of the Movie entity is attached to the DataContext
Next, the old entity is updated with changes from the new entity Finally,
SubmitChanges() is called to perform the SQL UPDATE command against the database
You can use the page in Listing 20.25 with the Update() method
LISTING 20.25 Standard\UpdateMovie.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>Update Movie</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
DataKeyNames=”Id”
AutoGenerateEditButton=”true”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie”
DataObjectTypeName=”Movie”
SelectMethod=”Select”
UpdateMethod=”Update”
ConflictDetection=”CompareAllValues”
OldValuesParameterFormatString=”oldMovie”
Runat=”server” />
</div>
</form>
</body>
</html>
Trang 6The ObjectDataSource control has both its ConflictDetection and
OldValuesParameterFormatString attributes set The ConflictDetection attribute is set to
the value CompareAllValues This value causes the ObjectDataSource to store the original
movie property values in view state The OldValuesParameterFormatString attribute
determines the name of the parameter that represents the old Movie entity
When you update a movie by using the page in Listing 20.25, the following SQL
command is sent to the database:
UPDATE [dbo].[Movie]
SET [Title] = @p7
WHERE ([Id] = @p0) AND ([CategoryId] = @p1)
AND ([Title] = @p2) AND ([Director] = @p3)
AND ([DateReleased] = @p4) AND (NOT ([InTheaters] = 1))
AND ([BoxOfficeTotals] = @p5) AND ([Description] = @p6)
LINQ to SQL compares all the new column values against the old column values This is
done to prevent concurrency conflicts If someone else makes a change to a record before
you have a chance to submit your changes, the record won’t be updated with your
changes However, all this comparing of column values seems wasteful and silly
If you don’t want LINQ to SQL to compare all the column values when it does an update,
you need to add a version property to your entity The easiest way to do this is to add a
timestamp column to the database table that corresponds to the entity (and re-create the
entity in the LINQ to SQL Designer so that it has the new timestamp column property)
So, our modified Movie table looks like this:
Movie
Column Name Data Type Is Identity?
Title NVarchar(100) FALSE
DateReleased DateTime FALSE
BoxOfficeTotals Money FALSE
You also need to ensure that the Version property gets saved into view state You can do
this by adding the Version property to a DataBound control’s DataKeyNames property This
approach is illustrated by the page in Listing 20.26
Trang 7LISTING 20.26 Standard\UpdateMovieVersion.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>Update Movie Version</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
DataKeyNames=”Id,Version”
AutoGenerateEditButton=”true”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie”
DataObjectTypeName=”Movie”
SelectMethod=”Select”
UpdateMethod=”Update”
ConflictDetection=”CompareAllValues”
OldValuesParameterFormatString=”oldMovie”
Runat=”server” />
</div>
</form>
</body>
</html>
Both the Id and Version properties are assigned to the GridView control’s DataKeyNames
attribute
After you make these changes, the update SQL command looks like this:
UPDATE [dbo].[Movie]
SET [Title] = @p2
WHERE ([Id] = @p0) AND ([Version] = @p1)
Trang 8Deleting with LINQ to SQL
You can delete an entity with LINQ to SQL by using code like the following:
MyDatabaseDataContext db = new MyDatabaseDataContext();
Movie movieToDelete = db.Movies.Single(m=>m.Id==1);
db.Movies.Remove( movieToDelete );
db.SubmitChanges();
This code starts by retrieving the record with an Id of 1 from the Movie database table
Next, the Movie entity is removed from the Movies collection Finally, this change is
submitted to the database and the following SQL command executes:
DELETE FROM [dbo].[Movie]
WHERE ([Id] = @p0) AND ([Version] = @p1)
NOTE
I’m assuming in this section that you have added a Version property to your Movie
database table If not, see the previous section, because you should add a Version
property when deleting for the same reasons you should add a Version property
when updating
It seems silly to retrieve a record from the database just so that you can delete it—and it is
silly What you need to do is reattach the Movie entity so that you can delete it Thus, you
can avoid making two calls to the database
The modified Movie class in Listing 20.27 includes a Delete() method that removes a
movie without retrieving it first
LISTING 20.27 Standard\App_Code\Movie.cs
using System;
using System.Web;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
public partial class Movie
{
public static void Delete(Movie movieToDelete)
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
db.Movies.Attach(movieToDelete);
db.Movies.DeleteOnSubmit(movieToDelete);
db.SubmitChanges();
Trang 9}
public static IEnumerable<Movie> Select()
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
return db.Movies;
}
}
You can use the class in Listing 20.27 with the ASP.NET page in Listing 20.28
LISTING 20.28 Standard\DeleteMovie.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>Delete Movie</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
DataKeyNames=”Id,Version”
AutoGenerateDeleteButton=”true”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie”
DataObjectTypeName=”Movie”
SelectMethod=”Select”
DeleteMethod=”Delete”
ConflictDetection=”CompareAllValues”
OldValuesParameterFormatString=”oldMovie”
Runat=”server” />
</div>
</form>
</body>
</html>
Trang 10The ObjectDataSource control in Listing 20.28 has both its ConflictDetection and
OldValuesParameterFormatString attributes set The ObjectDataSource remembers a
Movie entity across postbacks It passes the original Movie entity to the Delete() method
so that the entity and be reattached and deleted
Dynamic Queries
One concern that everyone has when they start working with LINQ to SQL is the problem of
representing dynamic queries When you create a query by using ADO.NET and SQL, you
can dynamically modify the SQL query simply by modifying the string that represents the
SQL command When working with LINQ to SQL, on the other hand, you can’t do this
because you are not working with strings
In this section, we explore two methods of executing dynamic queries You learn how to
pass normal SQL commands while using LINQ to SQL You also learn how to dynamically
build LINQ to SQL query expressions
Executing Dynamic SQL Statements
If you simply want to execute a SQL statement or query, and you don’t want to use
ADO.NET directly, you can take advantage of the DataContext ExecuteCommand() and
ExecuteQuery() methods The ExecuteCommand() method executes a SQL command
against a database The ExecuteQuery() method executes a SQL query against a database
and returns the results as entities
The following code illustrates how to use both of these methods:
MyDatabaseDataContext db = new MyDatabaseDataContext();
db.ExecuteCommand(“INSERT Movie (Title,Director,CategoryId)
VALUES (@p0,@p1,
var query = db.ExecuteQuery(typeof(Movie), “SELECT * FROM Movie
WHERE CategoryId=@p0”, new object[]{2});
Here, the ExecuteCommand() method is used to insert a new record into the Movie
data-base table The ExecuteQuery() method is used to grab all the records from the Movie
table where the CategoryId column has the value 2
You indicate parameters by using parameter names like @p0, @p1, @p2, and so on You do
not use named parameters like you would in the case of an ADO.NET command
Parameters are identified by their ordinal position
Building Query Expressions Dynamically
Resorting to executing SQL statements against the database feels like a type of cheating
The whole point of LINQ to SQL is to get away from working with SQL directly What we
actually want to do is build LINQ to SQL queries dynamically in the same way we can
build a SQL command dynamically