Home.NET Creating the Home Page for a Simple Storef...
Creating the Home Page for a Simple Storefront with LINQ
In the last article, we laid out the plans for a simple storefront for the Adventure Works Cycles sample database. We briefly examined the database structure, and created a master page to unify the look of our new web site. We also created a page that would display the appropriate image given a ProductID through the query string. In this article, we'll continue with the construction of the storefront.
Let's create the store's home page. On this page will be a short welcome messages, a link to the product browse page, and then a short list of the latest products. We'll tackle the basic structure of the page first, and then we can move on to the meat of the page: using LINQ with ASP.NET. Create a Default.aspx page. Be sure to set its master page to MasterPage.master, which we created in the last article. The page will be created with a simple Content control. Here's the page without the latest items part:
<%@PageLanguage="C#"MasterPageFile="~/MasterPage.master" AutoEventWireup="true"CodeFile="Default.aspx.cs" Inherits="_Default"Title="Adventure Works Cycles" %>
<p>Welcome to Adventure Works Cycles, your source for cycles and cycling accessories.</p>
<p><ahref="Browse.aspx">Browse Products</a></p>
<p>Check out some of our latest items:</p>
</asp:Content>
Now we need a control that will display the latest items for us. Let's go with a ListView control, which is relatively new and easy to work with:
<asp:ListViewID="LatestItemsView"runat="server">
</asp:ListView>
Before the ListView becomes operational, we need to provide it with a few templates. First is the LayoutTemplate, which defines the “master layout,” if you will, of the ListView. Place this inside of the ListView tags:
Notice how we provide a PlaceHolder control here. It's a placeholder for the list of items, and it's a necessary part of LayoutTemplate. It also must have the name “itemPlaceholder” in order to work.
The above displays a picture of the product, the name of the product, and a link to view the product (though, of course, the view functionality is not done). We access the various fields of the entry using Eval, passing in the appropriate field's name. When the page is viewed, the code tags are, of course, replaced with the appropriate values.
It doesn't take much to see that the above markup will generate a very primitive list of items, but we'll worry about replacing the layout in a moment. Right now we need to wire up our ListView. There are two ways we can databind our control using LINQ and ASP.NET. The first way is to databind it directly through code, and the second way is to use LinqDataSource. Here, we're going to use the first method and manually databind it in Page_Load. We'll discuss the second method in a bit.
To databind the control, we simply need to query the database using LINQ as we would in a normal application, and then we need to assign the result to the ListView control's DataSource attribute. Finally, we need to call the DataBind method. Here's how it's done:
AdventureWorksDataContext db = newAdventureWorksDataContext();
var latestItems = (from p in db.Products
orderby p.SellStartDate descending
select p).Take(3);
LatestItemsView.DataSource = latestItems;
LatestItemsView.DataBind();
}
The only thing strange in the code, perhaps, is Take. Take simply limits the amount of items returned. Here, we get the first three results. Besides Take, in the query, we order the table's entries by the SellStartData field, in reverse. This will make it so the latest items are first, and the oldest items are last.
Run the page, and you should see the three latest products:
Don't be alarmed by the “No Image Available” pictures. Many of the items, unfortunately, don't include real pictures. Instead, they use a “No Image Available” graphic. But at least we know that the ProductPicture.aspx page is properly working.
As mentioned earlier, though, the layout of the items is a bit ugly, and the items don't come with any sort of a description beyond a picture. So, let's give the list of items a new style, and let's add more information.
One of the things we'll want to display is the product's category, but to get the name of the category, we need access to the ProductCategory table. Open up AdventureWorks.dbml and drag the ProductCategory table on to the Designer:
Visual Studio associates the tables, and we now have access to the name of the product's category.
Let's add a few classes to our stylesheet:
div.itemBoxSmall
{
background-color: #F0FFFF; /* Azure */
border: solid1pxOrange;
margin: 5pxauto0pxauto;
padding: 5px;
text-align: left;
width: 40%;
}
img.productImage
{
border: solid1pxBlack;
float: left;
margin-right: 10px;
}
a.productLink
{
color: Black;
font-weight: bold;
}
And now let's complete the style by recreating the ItemTemplate to show the new style, as well as a few extra fields:
There, now the list of latest items looks a bit more presentable, and the product's category, color and list price are all displayed, as well as the name of the product. Of course, feel free to add more to it if that suits you.
Now we can create Product.aspx, the page where the details of an individual product can be viewed. Go ahead and create Product.aspx. Make sure you set MasterPage.master as its master page.
Before we do anything, recall that the description of a product isnot in the Product table. Rather, descriptions are associated with models through the ProductModelProductDescription table. So, in order to get the description of an item, we need to first determine a product's model. It's a long trail, though. We need to go from Product to ProductModel to ProductModelProductDescription to ProductDescription. We need to add these tables to AdventureWorks.dbml before we continue with the Product.aspx page:
Now, pay attention to the direction of the arrows, which represent associations. Notice how the arrow between ProductModel and ProductModelProductDescription is pointed away from Product, while every other arrow is pointed towardProduct. This is because ProductModel is set as the parent class, and ProductModelProductDescription is set as the child class. Basically, the two terms involve how each table handles the field around which the association is built. With ProductModel and Product, for example, the association is built around ProductModelID. In the ProductModel table, this field is the primary key, and in Product, it's just a normal field. So, ProductModel is the parent class here. However, with ProductModelProductDescription and ProductModel, both tables have ProductModelID as a primary key, so either table can be the parent class.
For our purposes, the association is backward, though. The association suggests that more than one entry in the ProductModelProductDescription table (the child table) can be matched with a given ProductModelID (part of the parent table), just as more than one entry in the Product table can be matched with a given ProductModelID (part of the parent table). It's entirely possible for us to build our application with this configuration, but then to access the ProductDescription for a given Product (p), we have to use First, which is ugly and troublesome:
Fixing this is easy. Click the arrow and delete it to delete the association. Then, right click the Designer and add a new association. Set ProductModelProductDescription as the parent and ProductModel as the child. Then, select the ProductModelID property for both of them:
Now all of the arrows face the same direction:
This makes more sense: while every ProductDescription can be matched with multiple Product entries, every Product can only be matched with oneProductDescription. Now we can drop the call to First when accessing a Product's description: