Опубликован: 04.05.2010 | Доступ: свободный | Студентов: 4032 / 454 | Оценка: 4.64 / 4.44 | Длительность: 41:24:00
Практическая работа 3:

Обеспечение взаимодействия Интернет-магазина с базой данных

< Лекция 8 || Практическая работа 3: 123 || Лекция 9 >

11.2. Доработка страницы продуктов с использованием LINQ to SQL

После того, как разработано меню, позволяющее ограничивать категорию товара, которой интересуется пользователь, необходимо переработать страницу продуктов так, чтобы она извлекала сведения о продуктах из базы данных, и могла их ограничивать по категориям.

Для доступа к данным мы воспользуемся другой технологией, входящей в состав .NET Framework – LINQ to SQL. Эта технология позволяет извлекать данные из БД в виде связанных объектов, которые более привычны разработчику, программирующем на объекто-ориентированных языках программирования, и позволяет использовать язык интегрированных запросов LINQ для манипуляций с этими объектами.

Примечание: стоит заметить, что LINQ to SQL не является полноценной ORM (Object-Relation Mapping) платформой, однако способна решать большинство необходимых задач. Если какая-то проблема преобразования реляционных данных на объекты не решается с использованием LINQ to SQL или решается крайне сложно, стоит воспользоваться другой технологией – Entity Framework, входящим в состав .NET Framework 3.5. Рассмотрение этого подхода выходит за рамки курса.

Для того, чтобы добавить отображение таблиц данных в виде LINQ to SQL в проект, необходимо кликнуть правой клавишей мыши по папке App_Data, в контекстном меню выбрать пункт Add New Item. В открывшемся диалоговом окне ненужно выбрать элемент LINQ to SQL Classes (рис. 11.5).

Добавление классов LINQ to SQL в проект

увеличить изображение
Рис. 11.5. Добавление классов LINQ to SQL в проект

После добавление классов в проект, откроется дизайнер классов (рис. 11.6). Добавим в него таблицы ProductCategory, ProductSubcategory, ProductModel, ProductModelIllustrtions, Product, ProductDescriptions, ProductModelProductDescriptionCulture, ProductPhoto, ProductProductPhoto и Productreview, перетащив их мышкой из окна Server Explorer.

Вид SQL to LINQ классов после добавления таблиц

увеличить изображение
Рис. 11.6. Вид SQL to LINQ классов после добавления таблиц

Прежде чем приступить к дальнейшей работе, откроем файл DataClasses.designer.cs и увидим сгенерированный код для классов. Перейдем в класс Product и добавим в него следующий код:

public string FullSize
	{
        get
        {
            return Size + " " + SizeUnitMeasureCode;
        }
	}
Примечание: редактирование этого файла на самом деле является не очень правильным подходом хотя бы потому, что при внесении каких-либо изменений в дизайнере, этот файл будет перезаписан и все сделанные в нем изменения будут утеряны. Правильный подход состоит в доопределении класса Product в отдельном файле. Это возможно, так как класс Product объявлен как partial.

Теперь мы определили собственное свойство у объекта product, которое отображает его размер с указанными единицами измерений. Стоит отметить, что это свойство вычислимое, оно не хранится в БД и его нельзя изменить.

Теперь перейдем к странице products/default.aspx и переопределим код левого PlaceHolder' а. прежде всего, добавим в него определение нового источника данных – LinqDataSource. Это можно сделать, набрав код руками, а можно перетащить соответствующий элемент из окна Toolbox и настроить его в специальном мастере. Так или иначе, должен получиться следующий код:

<asp:LinqDataSource ID="LinqDataSource1" runat="server" 
   ContextTypeName="DataClassesDataContext" 
   Select="new (ProductID, Name, ProductNumber, Color, ListPrice, Size, 
   SizeUnitMeasureCode, WeightUnitMeasureCode, Weight, FullSize)" 
   TableName="Product">
</asp:LinqDataSource>

В источнике данных LinqDataSource указано, что необходимо брать объекты типа Products, определенные в DataClassesDataContext, и предоставлять доступ компонентам, которые будут использовать этот источник данных только к тем свойствам, которые указаны в атрибуте Select. Теперь переопределим GridView так, чтобы он использовал новый источник данных:

<asp:GridView ID="GridViewProducts" runat="server" CellPadding="4"
        GridLines="None" CssClass="GridViewProduct"
        AllowSorting="True" AutoGenerateColumns="False"
        AllowPaging="True" DataSourceID="LinqDataSource1" 
		OnSelectedIndexChanged="OnSelectedIndexChanged" >
        <RowStyle CssClass="tr_nechet" />
        <AlternatingRowStyle CssClass="tr_chet" />
        <Columns>
            <asp:CommandField SelectText="Выбор" ShowSelectButton="True" />
            <asp:BoundField DataField="ProductID" HeaderText="ProductID" 
			    ReadOnly="True" 
                SortExpression="ProductID" Visible="False" />
            <asp:BoundField DataField="ProductNumber" HeaderText="Номер продукта" 
                ReadOnly="True" SortExpression="ProductNumber" />
            <asp:BoundField DataField="Name" HeaderText="Название" ReadOnly="True" 
                SortExpression="Name" />
            <asp:BoundField DataField="Color" HeaderText="Цвет" ReadOnly="True" 
                SortExpression="Color" />
            <asp:BoundField DataField="ListPrice" HeaderText="Цена" ReadOnly="True" 
                SortExpression="ListPrice" NullDisplayText="Цена не указана" 
                DataFormatString="{0:F2}"  />
            <asp:BoundField DataField="FullSize" HeaderText="Размер" ReadOnly="True" 
                SortExpression="FullSize" />
            <asp:TemplateField HeaderText="Вес" SortExpression="Weight" >
              <ItemTemplate> 
                <asp:Label ID="Label1" runat="server" 
                    Text='<%# Eval("Weight") + " " + Eval("WeightUnitMeasureCode") %>'>
                    </asp:Label>
               </ItemTemplate>
            </asp:TemplateField>
        </Columns>
        <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
        <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="Navy" />
        <HeaderStyle CssClass="tr_main" />
    </asp:GridView>

Здесь стоит обратить внимание на то, что для GridView установлен источник данных ( DataSourceID="LinqDataSource1" ), разрешены операции сортировки и постраничного вывода ( AllowSorting="True" и AllowPaging="True" ). При этом изменилось и определение колонок – теперь мы можем использовать BoundField, в которых при помощи атрибута DataField указать, какое свойство необходимо отображать. Также, для свойства ListPrice, определяющего цену на товар задан формат вывода ( DataFormatString="{0:F2}" ), который определяет, что строку надо выводить как число с двумя знаками после запятой. Также, для примера, GridView содержит один столбец, использующий TemplateField, который отображает вес товара вместе с единицей измерения веса. Это альтернативный способ добиться отображения сложных полей (для этих же целей мы определили свойство FullSize ).

Теперь необходимо изменить метод Page_Load. В нем нам необходимо ввести ограничения на извлекаемые данные, ограничив категорию и подкатегорию продуктов. Для этого у объектов LinqDataSource есть свойства Where и WhereParameters. При нажатии на определенный пункт меню, происходит переадресация на страницу products/default.aspx, при этом категория и подкатегория передаются через параметры category и subCategory. Прежде всего, необходимо сбросить текущие фильтры, после чего, если определена подкатегория (это можно узнать, проверив содержимое Request.QueryString["subCategory"] ) то достаточно указать свойство Where = "ProductSubcategoryID == @subCategory", а в коллекцию WhereParameters добавить новый параметр "subCategory" со значением, переданным в строке запроса – Request.QueryString["subCategory"]. Если же подкатегория не была указана, но была указана категория, то свойству where необходимо присвоить значение "ProductSubcategory.ProductCategoryID == @category" и задать параметр category в WhereParameters.

Примечание: Несмотря на то, что мы использовали точечный синтаксис в ограничении, и обратились к свойству ProductCategoryID связанного с продуктом объекта ProductSubcategory, это не значит, что фильтрация будет происходить уже после извлечения данных из БД. В действительности LINQ to SQL преобразует этот код, построив пересечение нескольких таблиц (в нашем случае – Product и ProductSubcategory ) при помощи оператора SQL JOIN.

Ниже приведен полный код метода Page_Load.

protected void Page_Load(object sender, EventArgs e)
{
        GridViewProducts.DataKeyNames = new string[]
                                            {
                                                "ProductID"
                                            };
        
        LinqDataSource1.Where = "";
        LinqDataSource1.WhereParameters.Clear();
        if (!string.IsNullOrEmpty(Request.QueryString["subCategory"]))
        {
            LinqDataSource1.Where = "ProductSubcategoryID == @subCategory";
            LinqDataSource1.WhereParameters.Add("subCategory", DbType.Int32, Request.QueryString["subCategory"]);
        }
        else
        {
            if (!string.IsNullOrEmpty(Request.QueryString["category"]))
            {
                LinqDataSource1.Where = "ProductSubcategory.ProductCategoryID == @category";
                LinqDataSource1.WhereParameters.Add("category", DbType.Int32, Request.QueryString["category"]);
            }
        }
}

На рис. 11.7 представлен вид страницы продуктов при выбранном пункте меню "Mountain Bikes".

Страница продуктов, попадающих в подкатегорию "Mountain Bikes"

увеличить изображение
Рис. 11.7. Страница продуктов, попадающих в подкатегорию "Mountain Bikes"
< Лекция 8 || Практическая работа 3: 123 || Лекция 9 >