ptg 974 CHAPTER 20 Data Access with LINQ to SQL The 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” From the Library of Wow! eBook ptg 975 Performing Standard Database Commands with LINQ to SQL SelectedValue=’<%# 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” /> 20 From the Library of Wow! eBook ptg 976 CHAPTER 20 Data Access with LINQ to SQL <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: From the Library of Wow! eBook ptg 977 Performing Standard Database Commands with LINQ to SQL MyDatabaseDataContext 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(); 20 From the Library of Wow! eBook ptg 978 CHAPTER 20 Data Access with LINQ to SQL return 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> From the Library of Wow! eBook ptg 979 Performing Standard Database Commands with LINQ to SQL The 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: 20 Movie Column Name Data Type Is Identity? Id Int TRUE Title NVarchar(100) FALSE Director NVarchar FALSE DateReleased DateTime FALSE BoxOfficeTotals Money FALSE Version TimeStamp 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. From the Library of Wow! eBook ptg 980 CHAPTER 20 Data Access with LINQ to SQL LISTING 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) From the Library of Wow! eBook ptg 981 Performing Standard Database Commands with LINQ to SQL Deleting 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(); 20 From the Library of Wow! eBook ptg 982 CHAPTER 20 Data Access with LINQ to SQL } 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> From the Library of Wow! eBook ptg 983 Performing Standard Database Commands with LINQ to SQL The 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. 20 From the Library of Wow! eBook . the ASP. NET page in Listing 20.28. LISTING 20.28 StandardDeleteMovie.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”>. ([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. TimeStamp 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