ptg 954 CHAPTER 20 Data Access with LINQ to SQL . Storage—Indicates a field where the value of the property is stored. . UpdateCheck—Indicates whether the property participates in optimistic concurrency comparisons. The Table attribute supports the following single property: . Name—Indicates the name of the database table that corresponds to the class. Some comments about these attributes are needed. First, you don’t need to specify a Name property when your property or class name corresponds to your database column or table name. If, on the other hand, your database table were named Movies and your class were named Movie, you would need to supply the Name property for the Table attribute to map the correct table to the class. Second, you always want to specify the primary key column by using the IsPrimaryKey property. For example, if you don’t specify a primary key column, you can’t do updates against your database using LINQ. Finally, even though we didn’t do this in our Movie class, you almost always want to include a timestamp column in your database table and indicate the timestamp column by using the IsVersion property. If you don’t do this, LINQ to SQL checks whether the values of all the properties match the values of all the columns before performing an update command to prevent concurrency conflicts. If you specify a version property, LINQ to SQL can check the value of this single property against the database rather than all the columns. Now that we’ve created an entity, we can start performing queries against the database using LINQ to SQL. For example, the page in Listing 20.12 contains a form that enables you to search for movies by a particular director. LISTING 20.12 Entities\SearchMovies.aspx <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Web.Configuration” %> <%@ Import Namespace=”System.Linq” %> <%@ Import Namespace=”System.Data.Linq” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> protected void btnSearch_Click(object sender, EventArgs e) { string conString = WebConfigurationManager. ConnectionStrings[“Movies”].ConnectionString; DataContext db = new DataContext(conString); var tMovie = db.GetTable<Movie>(); From the Library of Wow! eBook ptg 955 Creating LINQ to SQL Entities grdMovies.DataSource = tMovie.Where( m => m.Director.Contains( txtDirector.Text) ); grdMovies.DataBind(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml”> <head runat=”server”> <title>SearchMovies.aspx</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:Label id=”lblDirector” Text=”Director:” AssociatedControlID=”txtDirector” Runat=”server” /> <asp:TextBox id=”txtDirector” Runat=”server” /> <asp:Button id=”btnSearch” Text=”Search” OnClick=”btnSearch_Click” Runat=”Server” /> <br /><br /> <asp:GridView id=”grdMovies” Runat=”server” /> </div> </form> </body> </html> When you click the Search button, the btnSearch_Click() method executes the LINQ to SQL query. First, a DataContext is created by passing a database connection string to the class’s constructor. The DataContext is responsible for tracking all the LINQ to SQL entities and representing the database connection. 20 From the Library of Wow! eBook ptg 956 CHAPTER 20 Data Access with LINQ to SQL Next, a variable named tMovie is instantiated that represents a particular database table from the DataContext. Because we pass the Movie entity to the GetTable<T>() method() method>, the method returns a Table<T> object that represents the Movie database table. The Table<T> object implements the IQueryable interface and can, therefore, be queried with a LINQ to SQL query. Finally, the following LINQ to SQL query is executed: tMovie.Where( m => m.Director.Contains(txtDirector.Text) The lambda expression m => m.Director.Contains(txtDirector.Text) passed to the Where() method returns every movie record from the database in which the Director column contains the text entered into the TextBox control. We had to import two namespaces to use the LINQ to SQL query: System.Linq and System.Data.Linq. NOTE To keep things simple, I use the LINQ to SQL qu er y directly within the ASP.NET page in Listing 20.12. In real life, to avoid mixing user interface and Data Access layers, I would perform the LINQ to SQL query in a separate class and use an ObjectDataSource to represent the class. Building Entities with the LINQ to SQL Designer As an alternative to building entities by hand, you can use the LINQ to SQL Designer. You can simply drag database tables from the Database Explorer (Server Explorer) onto the Designer. The Designer generates the entity classes with the correct attributes automatically. Follow these steps to use the LINQ to SQL Designer: 1. Select Website, Add New Item to open the Add New Item dialog box. 2. Select the LINQ to SQL Classes template, give it the name MyDatabase, and click the Add button. 3. When prompted to create the LINQ to SQL classes in the App_Code folder, click the Yes button. 4. After the LINQ to SQL Designer opens, drag one or more database tables from the Database Explorer/Server Explorer window onto the Designer surface. You can view the code that the Designer generates by expanding the MyDatabase.dbml node in the App_Code folder and double-clicking the MyDatabase.designer.cs file. The Designer generates a strongly typed DataContext class named MyDatabaseContext. Each database table that you drag onto the Designer surface gets exposed by the DataContext class as a strongly typed property. From the Library of Wow! eBook ptg 957 Creating LINQ to SQL Entities The Designer, furthermore, generates a distinct class for each database table you drag onto the Designer. For example, after you drag the Movie table onto the Designer, a new class named Movie is created in the MyDatabase.designer.cs file. NOTE The LINQ to SQL Designer attempts to pluralize table names automatically when you add them to the Designer. So, when you drag the Movie table onto the Designer, the Designer generates a DataContext property named Movies. Most of the time, but not all of the time, it gets the pluralization right. You can turn off this feature by selecting Tools, Options and selecting the Database Tools, O/R D esigner tab. The page in Listing 20.13 demonstrates how you can use the MyDatabaseContext class when performing a LINQ to SQL query (after dragging the Movies database table onto the LINQ to SQL Designer). LISTING 20.13 Entities\ListMoviesByBoxOffice.aspx <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Linq” %> <%@ Import Namespace=”System.Data.Linq” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_Load() { MyDatabaseDataContext db = new MyDatabaseDataContext(); grd.DataSource = db.Movies.OrderBy(m => m.BoxOfficeTotals); grd.DataBind(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml”> <head runat=”server”> <title>List Movies by Box Office</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:GridView id=”grd” runat=”server” /> 20 From the Library of Wow! eBook ptg 958 CHAPTER 20 Data Access with LINQ to SQL </div> </form> </body> </html> The page in Listing 20.13 displays a list of all movies in order of the movie’s box office totals. The LINQ to SQL Designer creates partial classes for each table you drag onto the Designer surface. This means that you extend the functionality of each entity by creating a new partial class. For example, the class in Listing 20.14 extends the Movie class that the LINQ to SQL Designer generates. LISTING 20.14 Entities\App_Code\Movie.cs using System; using System.Collections.Generic; using System.Linq; using System.Data.Linq; public partial class Movie { public static IEnumerable<Movie> Select() { MyDatabaseDataContext db = new MyDatabaseDataContext(); return db.Movies; } public static IEnumerable<Movie> SelectByBoxOfficeTotals() { return Select().OrderBy( m => m.BoxOfficeTotals); } } The Movie class in Listing 20.14 is declared as a partial class. It extends the partial class in the MyDatabase.designer.cs file by adding both a Select() method and a SelectByBoxOfficeTotals() method. NOTE The SelectByBoxOfficeTotals() method calls the Select() method. It is important to understand that this does not cause two SQL SELECT commands to be executed against the database. Until the GridView control starts iterating through the results of the LINQ to SQL query, you are just building an expression. From the Library of Wow! eBook ptg 959 Creating LINQ to SQL Entities The page in Listing 20.15 demonstrates how you represent the Movie class with an ObjectDataSource control. LISTING 20.15 Entities\PartialMovie.aspx <%@ Page Language=”C#” %> <!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>Partial Movie</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:GridView id=”grdMovies” DataSourceID=”srcMovies” Runat=”server” /> <asp:ObjectDataSource id=”srcMovies” TypeName=”Movie” SelectMethod=”SelectByBoxOfficeTotals” runat=”server” /> </div> </form> </body> </html> There is no code in the page in Listing 20.15. All the code is where it should be, in the Data Access layer implemented by the Movie class. Building Entity Associations One entity can be associated with another entity. For example, a MovieCategory entity might be associated with one or more Movie entities. If you have defined foreign key relationships between your database tables, these rela- tionships are preserved when you drag your tables onto the LINQ to SQL Designer. The LINQ to SQL Designer generates entity associations based on the foreign key relationships automatically. 20 From the Library of Wow! eBook ptg 960 CHAPTER 20 Data Access with LINQ to SQL For example, the MovieCategory entity is related to the Movie entity through the Movie entity’s CategoryId property. As long as you have defined a foreign key relationship between Movie.CategoryId and MovieCategory.Id, you can use a query like this following: MyDatabaseDataContext db = new MyDatabaseDataContext(); var category = db.MovieCategories.Single( c => c.Name == “Drama” ); var query = category.Movies; The second statement grabs the Drama movie category. The third statement returns all movies associated with the Drama movie category. In this case, we’ve followed a one-to- many relationship and got a list of movies that match a movie category. You can also go the opposite direction and retrieve the only movie category that matches a particular movie: string categoryName = db.Movies.Single(m=>m.Id==1).MovieCategory.Name; This query retrieves the name of the movie category associated with the movie that has an ID of 1. NOTE Under the covers, the LINQ to SQL Designer creates the entity relationships by adding association attributes to entity properties. The LINQ to SQL Designer also adds some tricky synchronization logic to keep the properties of associated entities synchronized. Although I wish that I could code all my entities by hand, adding all the logic necessary to get the entity associations to work correctly is too much work. For that reason, I use the LINQ to SQL Designer. Using the LinqDataSource Control I want to briefly describe the LinqDataSource control. You can use this control to repre- sent LINQ queries. For example, the page in Listing 20.16 contains a simple search form for searching movies by director. The page uses a LinqDataSource to represent the LINQ query. LISTING 20.16 Entities\ShowLinqDataSource.aspx <%@ Page Language=”C#” %> <!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 LinqDataSource</title> </head> <body> From the Library of Wow! eBook ptg 961 Creating LINQ to SQL Entities <form id=”form1” runat=”server”> <div> <asp:Label id=”lblSearch” AssociatedControlID=”txtSearch” Text=”Search:” Runat=”server” /> <asp:TextBox id=”txtSearch” Runat=”server” /> <asp:Button id=”btnSearch” Text=”Search” Runat=”server” /> <br /><br /> <asp:GridView id=”grd” DataSourceID=”LinqDataSource1” Runat=”server” /> <asp:LinqDataSource ID=”LinqDataSource1” ContextTypeName=”MyDatabaseDataContext” TableName=”Movies” Where=”Director == @Director” OrderBy=”DateReleased” Select=”new (Title, Director)” runat=”server”> <whereparameters> <asp:controlparameter Name=”Director” ControlID=”txtSearch” PropertyName=”Text” Type=”String” /> </whereparameters> </asp:LinqDataSource> </div> </form> </body> </html> 20 From the Library of Wow! eBook ptg 962 CHAPTER 20 Data Access with LINQ to SQL The LinqDataSource in Listing 20.16 represents the following LINQ query: var query = db.Movies .Where(m => m.Director == txtSearch.Text) .OrderBy(m => m.DateReleased) .Select(m => new {m.Title, m.Director}); You also can use the LinqDataSource to generate Update, Insert, and Delete LINQ queries automatically. Simply set the EnableInsert, EnableUpdate,or EnableDelete property to the value True. For example, the page in Listing 20.17 contains a DetailsView control and a GridView control that you can use to insert, edit, and delete movie records. The inserting, editing, and deleting is performed by the LinqDataSource control. LISTING 20.17 Entities\EditLinqDataSource.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> protected void frmMovie_ItemInserted ( object sender, DetailsViewInsertedEventArgs e ) { grdMovies.DataBind(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml”> <head runat=”server”> <title>Edit LinqDataSource</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:DetailsView id=”frmMovie” DataSourceID=”srcMovies” DefaultMode=”Insert” AutoGenerateRows=”false” AutoGenerateInsertButton=”true” Runat=”server” OnItemInserted=”frmMovie_ItemInserted”> From the Library of Wow! eBook ptg 963 Creating LINQ to SQL Entities <Fields> <asp:BoundField DataField=”Title” HeaderText=”Title” /> <asp:BoundField DataField=”Director” HeaderText=”Director” /> <asp:BoundField DataField=”DateReleased” HeaderText=”Date Released” /> </Fields> </asp:DetailsView> <br /><br /> <asp:GridView id=”grdMovies” DataKeyNames=”Id” DataSourceID=”srcMovies” AllowPaging=”true” PageSize=”5” AutoGenerateEditButton=”true” AutoGenerateDeleteButton=”true” Runat=”server” /> <asp:LinqDataSource id=”srcMovies” ContextTypeName=”MyDatabaseDataContext” TableName=”Movies” OrderBy=”Id descending” EnableInsert=”true” EnableUpdate=”true” EnableDelete=”true” AutoPage=”true” Runat=”server” /> </div> </form> </body> </html> One other thing that you should notice about the LinqDataSource control in Listing 20.17: the LinqDataSource control has an AutoPage attribute set to the value True. When this property has the value True, the LinqDataSource performs data source paging automatically. I don’t use the LinqDataSource control in production applications. Instead, I wrap up all my LINQ queries in a separate class and use the ObjectDataSource control to represent the class. The LinqDataSource control is similar to the SqlDataSource control in that both controls are great for prototyping and doing demos, but they are not appropriate controls to use in production applications. 20 From the Library of Wow! eBook . ptg 9 54 CHAPTER 20 Data Access with LINQ to SQL . Storage—Indicates a field where the value of the property is stored. . UpdateCheck—Indicates whether the property participates in optimistic. supply the Name property for the Table attribute to map the correct table to the class. Second, you always want to specify the primary key column by using the IsPrimaryKey property. For example,. runat=”server”> <whereparameters> < ;asp: controlparameter Name=”Director” ControlID=”txtSearch” PropertyName=”Text” Type=”String” /> </whereparameters> < /asp: LinqDataSource> </div>