Корзина для покупок
Эрик Рейтан
Скачать пример проекта Wingtip Toys (C#) или скачать электронную книгу (PDF)
В этой серии руководств вы узнаете об основах создания приложения ASP.NET Web Forms с помощью ASP.NET 4.5 и Microsoft Visual Studio Express 2013 для Web. В рамках этой серии руководств доступен проект Visual Studio 2013 с исходным кодом C#.
В этом руководстве описывается бизнес-логика, необходимая для добавления корзины для покупок в пример приложения Wingtip Toys ASP.NET Web Forms. Это руководство создано на основе предыдущего руководства "Отображение элементов данных и сведений" и является частью серии учебников Wingtip Toy Store. По завершении работы с этим руководством пользователи примера приложения смогут добавлять, удалять и изменять продукты в своей корзине.
Из этого руководства вы узнаете, как выполнять такие задачи:
- Как создать корзину для веб-приложения.
- Как разрешить пользователям добавлять элементы в корзину.
- Как добавить элемент управления GridView для отображения сведений о корзине покупок.
- Как вычислить и отобразить общую сумму заказа.
- Удаление и обновление элементов в корзине.
- Как включить счетчик корзины для покупок.
Функции кода в этом руководстве:
- Entity Framework Code First
- Заметки к данным
- Строго типизированные элементы управления данными
- Привязка модели
Создание корзины
Ранее в этой серии учебников вы добавили страницы и код для просмотра данных о продуктах из базы данных. В этом руководстве вы создадите корзину для управления продуктами, которые пользователи хотят купить. Пользователи смогут просматривать и добавлять элементы в корзину, даже если они не зарегистрированы или не вошли в систему. Чтобы управлять доступом к корзине покупок, вы назначите пользователям уникальный ID
идентификатор с помощью глобально уникального идентификатора (GUID), когда пользователь впервые обращается к корзине. Вы сохраните его ID
с помощью состояния сеанса ASP.NET.
Примечание
Состояние сеанса ASP.NET — это удобное место для хранения сведений о пользователе, срок действия которых истекает после того, как пользователь покинет сайт. Хотя неправильное использование состояния сеанса может повлиять на производительность на крупных сайтах, легкое использование состояния сеанса хорошо подходит для демонстрационных целей. В примере проекта Wingtip Toys показано, как использовать состояние сеанса без внешнего поставщика, где состояние сеанса хранится внутри процесса на веб-сервере, на котором размещен сайт. Для крупных сайтов, которые предоставляют несколько экземпляров приложения, или сайтов, на которых выполняется несколько экземпляров приложения на разных серверах, рассмотрите возможность использования службы кэша Windows Azure. Эта служба кэша предоставляет распределенную службу кэширования, которая находится за пределами веб-сайта и решает проблему использования состояния внутрипроцессного сеанса. Дополнительные сведения см. в статье Использование состояния сеанса ASP.NET с веб-сайтами Windows Azure.
Добавление CartItem в качестве класса модели
Ранее в этой серии учебников вы определили схему для категории и данных продукта, создав Category
классы и Product
в папке Models . Теперь добавьте новый класс, чтобы определить схему для корзины. Далее в этом руководстве вы добавите класс для обработки доступа к данным к CartItem
таблице. Этот класс предоставляет бизнес-логику для добавления, удаления и обновления элементов в корзине.
Щелкните правой кнопкой мыши папку Models и выберите Добавить ->Новый элемент.
Откроется диалоговое окно Добавление нового элемента. Выберите Код, а затем — Класс.
Назовите этот новый класс CartItem.cs.
Нажмите кнопку Добавить.
Новый файл класса отображается в редакторе.Замените код по умолчанию на приведенный ниже:
using System.ComponentModel.DataAnnotations; namespace WingtipToys.Models { public class CartItem { [Key] public string ItemId { get; set; } public string CartId { get; set; } public int Quantity { get; set; } public System.DateTime DateCreated { get; set; } public int ProductId { get; set; } public virtual Product Product { get; set; } } }
Класс CartItem
содержит схему, которая определяет каждый продукт, добавляемый пользователем в корзину. Этот класс аналогичен другим классам схем, созданным ранее в этой серии учебников. По соглашению Entity Framework Code First ожидает, что первичным ключом CartItem
для таблицы будет или CartItemId
ID
. Однако код переопределяет поведение по умолчанию с помощью атрибута заметки [Key]
к данным. Атрибут Key
свойства ItemId указывает, что ItemID
свойство является первичным ключом.
Свойство CartId
указывает ID
пользователя, связанного с элементом для покупки. Вы добавите код для создания этого пользователя ID
, когда пользователь обращается к корзине. Он ID
также будет храниться в виде переменной сеанса ASP.NET.
Обновление контекста продукта
Помимо добавления CartItem
класса необходимо обновить класс контекста базы данных, который управляет классами сущностей и предоставляет доступ к данным к базе данных. Для этого необходимо добавить в класс только что созданный CartItem
класс ProductContext
модели.
В Обозреватель решений найдите и откройте файл ProductContext.cs в папке Models.
Добавьте выделенный код в файл ProductContext.cs следующим образом:
using System.Data.Entity; namespace WingtipToys.Models { public class ProductContext : DbContext { public ProductContext() : base("WingtipToys") { } public DbSet<Category> Categories { get; set; } public DbSet<Product> Products { get; set; } public DbSet<CartItem> ShoppingCartItems { get; set; } } }
Как упоминалось ранее в этой серии учебников, код в файле ProductContext.cs добавляет System.Data.Entity
пространство имен, чтобы у вас был доступ ко всем основным функциям Entity Framework. Эта функция включает возможность запрашивать, вставлять, обновлять и удалять данные путем работы со строго типизированными объектами. Класс ProductContext
добавляет доступ к только что добавленному классу CartItem
модели.
Управление бизнес-логикой корзины покупок
Затем вы создадите ShoppingCart
класс в новой папке Logic . Класс ShoppingCart
обрабатывает доступ к данным к CartItem
таблице. Класс также будет включать бизнес-логику для добавления, удаления и обновления элементов в корзине.
Добавленная логика корзины для покупок будет содержать функции для управления следующими действиями:
- Добавление элементов в корзину
- Удаление элементов из корзины
- Получение идентификатора корзины для покупок
- Получение товаров из корзины
- Подсчитывание суммы всех элементов корзины
- Обновление данных корзины
Страница корзины (ShoppingCart.aspx) и класс корзины будут использоваться вместе для доступа к данным корзины. На странице корзины будут отображаться все элементы, добавленные пользователем в корзину. Помимо страницы корзины и класса, вы создадите страницу (AddToCart.aspx) для добавления продуктов в корзину. Вы также добавите код на страницу ProductList.aspx и страницу ProductDetails.aspx , который предоставит ссылку на страницу AddToCart.aspx , чтобы пользователь смог добавить продукты в корзину.
На следующей схеме показан базовый процесс, который происходит, когда пользователь добавляет продукт в корзину.
Когда пользователь щелкает ссылку Добавить в корзину на странице ProductList.aspx или ProductDetails.aspx , приложение перейдет на страницу AddToCart.aspx , а затем автоматически на страницу ShoppingCart.aspx . Страница AddToCart.aspx добавит продукт select в корзину, вызвав метод в классе ShoppingCart. На странице ShoppingCart.aspx будут отображаться продукты, добавленные в корзину.
Создание класса Корзина покупок
Класс ShoppingCart
будет добавлен в отдельную папку в приложении, чтобы было четкое различие между моделью (папка Models), страницами (корневая папка) и логикой (папка логики).
В Обозреватель решений щелкните правой кнопкой мыши проект WingtipToysи выберите добавить новую>папку. Назовите новую папку Logic.
Щелкните правой кнопкой мыши папку Логика и выберите Добавить ->Новый элемент.
Добавьте новый файл класса с именем ShoppingCartActions.cs.
Замените код по умолчанию на приведенный ниже:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; namespace WingtipToys.Logic { public class ShoppingCartActions : IDisposable { public string ShoppingCartId { get; set; } private ProductContext _db = new ProductContext(); public const string CartSessionKey = "CartId"; public void AddToCart(int id) { // Retrieve the product from the database. ShoppingCartId = GetCartId(); var cartItem = _db.ShoppingCartItems.SingleOrDefault( c => c.CartId == ShoppingCartId && c.ProductId == id); if (cartItem == null) { // Create a new cart item if no cart item exists. cartItem = new CartItem { ItemId = Guid.NewGuid().ToString(), ProductId = id, CartId = ShoppingCartId, Product = _db.Products.SingleOrDefault( p => p.ProductID == id), Quantity = 1, DateCreated = DateTime.Now }; _db.ShoppingCartItems.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity. cartItem.Quantity++; } _db.SaveChanges(); } public void Dispose() { if (_db != null) { _db.Dispose(); _db = null; } } public string GetCartId() { if (HttpContext.Current.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name)) { HttpContext.Current.Session[CartSessionKey] = HttpContext.Current.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class. Guid tempCartId = Guid.NewGuid(); HttpContext.Current.Session[CartSessionKey] = tempCartId.ToString(); } } return HttpContext.Current.Session[CartSessionKey].ToString(); } public List<CartItem> GetCartItems() { ShoppingCartId = GetCartId(); return _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId).ToList(); } } }
Метод AddToCart
позволяет включать отдельные продукты в корзину на основе продукта ID
. Продукт добавляется в корзину или, если корзина уже содержит товар для этого продукта, количество увеличивается.
Метод GetCartId
возвращает корзину ID
для пользователя. Корзина ID
используется для отслеживания элементов, которые есть у пользователя в корзине. Если у пользователя нет существующей корзины ID
, для него создается новая корзина ID
. Если пользователь вошел в систему как зарегистрированный пользователь, в корзине ID
устанавливается его имя пользователя. Однако если пользователь не выполнил вход, корзине ID
присваивается уникальное значение (GUID). Идентификатор GUID гарантирует, что для каждого пользователя создается только одна корзина на основе сеанса.
Метод GetCartItems
возвращает список элементов корзины для пользователя. Далее в этом руководстве вы увидите, что привязка модели используется для отображения элементов корзины в корзине GetCartItems
с помощью метода .
Создание функции добавления в корзину
Как упоминалось ранее, вы создадите страницу обработки с именем AddToCart.aspx , которая будет использоваться для добавления новых продуктов в корзину пользователя. Эта страница вызовет AddToCart
метод в только что созданном ShoppingCart
классе. На странице AddToCart.aspx ожидается, что ей будет передан продукт ID
. Этот продукт ID
будет использоваться при вызове AddToCart
метода в ShoppingCart
классе .
Примечание
Вы будете изменять код программной части (AddToCart.aspx.cs) для этой страницы, а не пользовательский интерфейс страницы (AddToCart.aspx).
Чтобы создать функцию надстройки для корзины, выполните следующие действия.
В Обозреватель решений щелкните правой кнопкой мыши проект WingtipToysи выберите команду Добавить ->Новый элемент.
Откроется диалоговое окно Добавление нового элемента.Добавьте стандартную новую страницу (веб-форму) в приложение с именем AddToCart.aspx.
В Обозреватель решений щелкните правой кнопкой мыши страницу AddToCart.aspx и выберите команду Просмотреть код. Файл кода программной части AddToCart.aspx.cs открывается в редакторе.
Замените существующий код в коде программной части AddToCart.aspx.cs следующим кодом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Diagnostics; using WingtipToys.Logic; namespace WingtipToys { public partial class AddToCart : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string rawId = Request.QueryString["ProductID"]; int productId; if (!String.IsNullOrEmpty(rawId) && int.TryParse(rawId, out productId)) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { usersShoppingCart.AddToCart(Convert.ToInt16(rawId)); } } else { Debug.Fail("ERROR : We should never get to AddToCart.aspx without a ProductId."); throw new Exception("ERROR : It is illegal to load AddToCart.aspx without setting a ProductId."); } Response.Redirect("ShoppingCart.aspx"); } } }
При загрузке страницы AddToCart.aspx продукт ID
извлекается из строки запроса. Затем создается экземпляр класса shopping cart, который используется для вызова AddToCart
метода, добавленного ранее в этом руководстве. Метод AddToCart
, содержащийся в файле ShoppingCartActions.cs , включает логику добавления выбранного продукта в корзину или увеличения количества выбранного продукта. Если продукт не был добавлен в корзину, он добавляется в таблицу CartItem
базы данных. Если продукт уже добавлен в корзину, а пользователь добавляет дополнительный элемент того же продукта, количество продуктов увеличивается в CartItem
таблице. Наконец, страница перенаправляется обратно на страницу ShoppingCart.aspx , которую вы добавите на следующем шаге, где пользователь увидит обновленный список элементов в корзине.
Как упоминалось ранее, пользователь ID
используется для идентификации продуктов, связанных с конкретным пользователем. Он ID
добавляется в строку в CartItem
таблице каждый раз, когда пользователь добавляет продукт в корзину.
Создание пользовательского интерфейса корзины
На странице ShoppingCart.aspx будут отображаться продукты, добавленные пользователем в корзину. Он также предоставляет возможность добавлять, удалять и обновлять элементы в корзине.
В Обозреватель решений щелкните правой кнопкой мыши WingtipToys и выберите команду Добавить ->Новый элемент.
Откроется диалоговое окно Добавление нового элемента.Добавьте новую страницу (веб-форму), включающую страницу master, выбрав Веб-форма с помощью главной страницы. Назовите новую страницу ShoppingCart.aspx.
Выберите Site.Master, чтобы присоединить страницу master к созданной aspx-странице.
На странице ShoppingCart.aspx замените существующую разметку следующей разметкой:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ShoppingCart.aspx.cs" Inherits="WingtipToys.ShoppingCart" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <div id="ShoppingCartTitle" runat="server" class="ContentHead"><h1>Shopping Cart</h1></div> <asp:GridView ID="CartList" runat="server" AutoGenerateColumns="False" ShowFooter="True" GridLines="Vertical" CellPadding="4" ItemType="WingtipToys.Models.CartItem" SelectMethod="GetShoppingCartItems" CssClass="table table-striped table-bordered" > <Columns> <asp:BoundField DataField="ProductID" HeaderText="ID" SortExpression="ProductID" /> <asp:BoundField DataField="Product.ProductName" HeaderText="Name" /> <asp:BoundField DataField="Product.UnitPrice" HeaderText="Price (each)" DataFormatString="{0:c}"/> <asp:TemplateField HeaderText="Quantity"> <ItemTemplate> <asp:TextBox ID="PurchaseQuantity" Width="40" runat="server" Text="<%#: Item.Quantity %>"></asp:TextBox> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Item Total"> <ItemTemplate> <%#: String.Format("{0:c}", ((Convert.ToDouble(Item.Quantity)) * Convert.ToDouble(Item.Product.UnitPrice)))%> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Remove Item"> <ItemTemplate> <asp:CheckBox id="Remove" runat="server"></asp:CheckBox> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> <div> <p></p> <strong> <asp:Label ID="LabelTotalText" runat="server" Text="Order Total: "></asp:Label> <asp:Label ID="lblTotal" runat="server" EnableViewState="false"></asp:Label> </strong> </div> <br /> </asp:Content>
Страница ShoppingCart.aspx содержит элемент управления GridView с именем CartList
. Этот элемент управления использует привязку модели для привязки данных корзины покупок из базы данных к элементу управления GridView . При установке ItemType
свойства элемента управления GridView выражение Item
привязки данных доступно в разметке элемента управления и элемент управления становится строго типизированным. Как упоминалось ранее в этой серии руководств, можно выбрать сведения об объекте Item
с помощью IntelliSense. Чтобы настроить элемент управления данными для использования привязки модели для выбора данных, необходимо задать SelectMethod
свойство элемента управления . В приведенной выше разметке вы задали SelectMethod
для использования метода GetShoppingCartItems, который возвращает список CartItem
объектов . Элемент управления данными GridView вызывает метод в соответствующее время жизненного цикла страницы и автоматически привязывает возвращаемые данные. Метод GetShoppingCartItems
по-прежнему необходимо добавить.
Получение элементов корзины
Затем добавьте код в код программной части ShoppingCart.aspx.cs , чтобы получить и заполнить пользовательский интерфейс корзины.
В Обозреватель решений щелкните правой кнопкой мыши страницу ShoppingCart.aspx и выберите пункт Просмотреть код. Файл кода программной части ShoppingCart.aspx.cs открывается в редакторе.
Замените существующий код следующим кодом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; using WingtipToys.Logic; namespace WingtipToys { public partial class ShoppingCart : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } public List<CartItem> GetShoppingCartItems() { ShoppingCartActions actions = new ShoppingCartActions(); return actions.GetCartItems(); } } }
Как упоминалось выше, GridView
элемент управления данными GetShoppingCartItems
вызывает метод в соответствующее время жизненного цикла страницы и автоматически привязывает возвращаемые данные. Метод GetShoppingCartItems
создает экземпляр ShoppingCartActions
объекта . Затем код использует этот экземпляр для возврата элементов в корзине путем вызова GetCartItems
метода .
Добавление продуктов в корзину
При отображении страницы ProductList.aspx или ProductDetails.aspx пользователь сможет добавить продукт в корзину с помощью ссылки. Щелкнув ссылку, приложение переходит на страницу обработки с именем AddToCart.aspx. Страница AddToCart.aspx вызовет AddToCart
метод в ShoppingCart
классе, который вы добавили ранее в этом руководстве.
Теперь вы добавите ссылку Добавить в корзину на страницы ProductList.aspx и ProductDetails.aspx . Эта ссылка будет включать продукт ID
, полученный из базы данных.
В Обозреватель решений найдите и откройте страницу с именем ProductList.aspx.
Добавьте разметку, выделенную желтым цветом, на страницу ProductList.aspx , чтобы вся страница выглядела следующим образом:
<%@ Page Title="Products" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ProductList.aspx.cs" Inherits="WingtipToys.ProductList" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <section> <div> <hgroup> <h2><%: Page.Title %></h2> </hgroup> <asp:ListView ID="productList" runat="server" DataKeyNames="ProductID" GroupItemCount="4" ItemType="WingtipToys.Models.Product" SelectMethod="GetProducts"> <EmptyDataTemplate> <table runat="server"> <tr> <td>No data was returned.</td> </tr> </table> </EmptyDataTemplate> <EmptyItemTemplate> <td runat="server" /> </EmptyItemTemplate> <GroupTemplate> <tr id="itemPlaceholderContainer" runat="server"> <td id="itemPlaceholder" runat="server"></td> </tr> </GroupTemplate> <ItemTemplate> <td runat="server"> <table> <tr> <td> <a href="ProductDetails.aspx?productID=<%#:Item.ProductID%>"> <img src="/Catalog/Images/Thumbs/<%#:Item.ImagePath%>" width="100" height="75" style="border: solid" /></a> </td> </tr> <tr> <td> <a href="ProductDetails.aspx?productID=<%#:Item.ProductID%>"> <span> <%#:Item.ProductName%> </span> </a> <br /> <span> <b>Price: </b><%#:String.Format("{0:c}", Item.UnitPrice)%> </span> <br /> <a href="/AddToCart.aspx?productID=<%#:Item.ProductID %>"> <span class="ProductListItem"> <b>Add To Cart<b> </span> </a> </td> </tr> <tr> <td> </td> </tr> </table> </p> </td> </ItemTemplate> <LayoutTemplate> <table runat="server" style="width:100%;"> <tbody> <tr runat="server"> <td runat="server"> <table id="groupPlaceholderContainer" runat="server" style="width:100%"> <tr id="groupPlaceholder" runat="server"></tr> </table> </td> </tr> <tr runat="server"> <td runat="server"></td> </tr> <tr></tr> </tbody> </table> </LayoutTemplate> </asp:ListView> </div> </section> </asp:Content>
Тестирование корзины для покупок
Запустите приложение, чтобы узнать, как вы добавляете продукты в корзину.
Нажмите клавишу F5 для запуска приложения.
После того как проект повторно создаст базу данных, откроется браузер и отобразится страница Default.aspx .Выберите Автомобили в меню навигации по категориям.
На странице ProductList.aspx отображаются только продукты, включенные в категорию "Автомобили".Щелкните ссылку Добавить в корзину рядом с первым перечисленным продуктом (автомобиль-кабриолет).
Отобразится страница ShoppingCart.aspx с выбранным выбором в корзине.Чтобы просмотреть дополнительные продукты, выберите Плоскости в меню навигации по категориям.
Щелкните ссылку Добавить в корзину рядом с первым перечисленным продуктом.
Страница ShoppingCart.aspx отображается с дополнительным элементом.Закройте браузер.
Вычисление и отображение итогового заказа
Помимо добавления продуктов в корзину, вы добавите метод в ShoppingCart
класс и отобразите общую GetTotal
сумму заказа на странице корзины.
В Обозреватель решений откройте файл ShoppingCartActions.cs в папке Логика.
Добавьте в класс следующий
GetTotal
метод, выделенный желтым цветомShoppingCart
, чтобы класс выглядел следующим образом:using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; namespace WingtipToys.Logic { public class ShoppingCartActions : IDisposable { public string ShoppingCartId { get; set; } private ProductContext _db = new ProductContext(); public const string CartSessionKey = "CartId"; public void AddToCart(int id) { // Retrieve the product from the database. ShoppingCartId = GetCartId(); var cartItem = _db.ShoppingCartItems.SingleOrDefault( c => c.CartId == ShoppingCartId && c.ProductId == id); if (cartItem == null) { // Create a new cart item if no cart item exists. cartItem = new CartItem { ItemId = Guid.NewGuid().ToString(), ProductId = id, CartId = ShoppingCartId, Product = _db.Products.SingleOrDefault( p => p.ProductID == id), Quantity = 1, DateCreated = DateTime.Now }; _db.ShoppingCartItems.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity. cartItem.Quantity++; } _db.SaveChanges(); } public void Dispose() { if (_db != null) { _db.Dispose(); _db = null; } } public string GetCartId() { if (HttpContext.Current.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name)) { HttpContext.Current.Session[CartSessionKey] = HttpContext.Current.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class. Guid tempCartId = Guid.NewGuid(); HttpContext.Current.Session[CartSessionKey] = tempCartId.ToString(); } } return HttpContext.Current.Session[CartSessionKey].ToString(); } public List<CartItem> GetCartItems() { ShoppingCartId = GetCartId(); return _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId).ToList(); } public decimal GetTotal() { ShoppingCartId = GetCartId(); // Multiply product price by quantity of that product to get // the current price for each of those products in the cart. // Sum all product price totals to get the cart total. decimal? total = decimal.Zero; total = (decimal?)(from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity * cartItems.Product.UnitPrice).Sum(); return total ?? decimal.Zero; } } }
GetTotal
Сначала метод получает идентификатор корзины для пользователя. Затем метод получает итог корзины, умножая цену продукта на количество для каждого продукта, указанного в корзине.
Примечание
В приведенном выше коде используется тип , допускающий значение NULL.int?
Типы, допускаемые значением NULL, могут представлять все значения базового типа, а также в виде значения NULL. Дополнительные сведения см. в статье Использование типов, допускающих значение NULL.
Изменение отображения корзины
Далее вы измените код для страницы ShoppingCart.aspx , чтобы вызвать GetTotal
метод и отобразить этот итог на странице ShoppingCart.aspx при загрузке страницы.
В Обозреватель решений щелкните правой кнопкой мыши страницу ShoppingCart.aspx и выберите Пункт Просмотреть код.
В файле ShoppingCart.aspx.cs обновите
Page_Load
обработчик, добавив следующий код, выделенный желтым цветом:using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; using WingtipToys.Logic; namespace WingtipToys { public partial class ShoppingCart : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { decimal cartTotal = 0; cartTotal = usersShoppingCart.GetTotal(); if (cartTotal > 0) { // Display Total. lblTotal.Text = String.Format("{0:c}", cartTotal); } else { LabelTotalText.Text = ""; lblTotal.Text = ""; ShoppingCartTitle.InnerText = "Shopping Cart is Empty"; } } } public List<CartItem> GetShoppingCartItems() { ShoppingCartActions actions = new ShoppingCartActions(); return actions.GetCartItems(); } } }
При загрузке страницы ShoppingCart.aspx она загружает объект корзины покупок, а затем получает итог корзины, вызывая GetTotal
метод ShoppingCart
класса . Если корзина пуста, отображается сообщение об этом.
Тестирование всего корзины
Запустите приложение сейчас, чтобы узнать, как можно не только добавить продукт в корзину, но и просмотреть итог корзины.
Нажмите клавишу F5 для запуска приложения.
Откроется браузер и отобразится страница Default.aspx .Выберите Автомобили в меню навигации по категориям.
Щелкните ссылку Добавить в корзину рядом с первым продуктом.
Отображается страница ShoppingCart.aspx с итоговой суммой заказа.Добавьте в корзину другие продукты (например, плоскость).
Отобразится страница ShoppingCart.aspx с обновленным итогом по всем добавленным продуктам.
Остановите работающее приложение, закрыв окно браузера.
Добавление кнопок обновления и оформления заказа в корзину
Чтобы разрешить пользователям изменять корзину, добавьте кнопки Обновить и Кнопки Оформления заказа на страницу корзины. Кнопка "Извлечь" используется только в дальнейшем в этой серии руководств.
В Обозреватель решений откройте страницу ShoppingCart.aspx в корне проекта веб-приложения.
Чтобы добавить кнопки Обновить и Проверить на страницу ShoppingCart.aspx , добавьте разметку, выделенную желтым цветом, к существующей разметке, как показано в следующем коде:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ShoppingCart.aspx.cs" Inherits="WingtipToys.ShoppingCart" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <div id="ShoppingCartTitle" runat="server" class="ContentHead"><h1>Shopping Cart</h1></div> <asp:GridView ID="CartList" runat="server" AutoGenerateColumns="False" ShowFooter="True" GridLines="Vertical" CellPadding="4" ItemType="WingtipToys.Models.CartItem" SelectMethod="GetShoppingCartItems" CssClass="table table-striped table-bordered" > <Columns> <asp:BoundField DataField="ProductID" HeaderText="ID" SortExpression="ProductID" /> <asp:BoundField DataField="Product.ProductName" HeaderText="Name" /> <asp:BoundField DataField="Product.UnitPrice" HeaderText="Price (each)" DataFormatString="{0:c}"/> <asp:TemplateField HeaderText="Quantity"> <ItemTemplate> <asp:TextBox ID="PurchaseQuantity" Width="40" runat="server" Text="<%#: Item.Quantity %>"></asp:TextBox> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Item Total"> <ItemTemplate> <%#: String.Format("{0:c}", ((Convert.ToDouble(Item.Quantity)) * Convert.ToDouble(Item.Product.UnitPrice)))%> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Remove Item"> <ItemTemplate> <asp:CheckBox id="Remove" runat="server"></asp:CheckBox> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> <div> <p></p> <strong> <asp:Label ID="LabelTotalText" runat="server" Text="Order Total: "></asp:Label> <asp:Label ID="lblTotal" runat="server" EnableViewState="false"></asp:Label> </strong> </div> <br /> <table> <tr> <td> <asp:Button ID="UpdateBtn" runat="server" Text="Update" OnClick="UpdateBtn_Click" /> </td> <td> <!--Checkout Placeholder --> </td> </tr> </table> </asp:Content>
Когда пользователь нажимает кнопку Обновить , UpdateBtn_Click
вызывается обработчик событий. Этот обработчик событий вызовет код, который вы добавите на следующем шаге.
Затем можно обновить код, содержащийся в файле ShoppingCart.aspx.cs , чтобы циклически перебирать элементы корзины и вызывать RemoveItem
методы и UpdateItem
.
В Обозреватель решений откройте файл ShoppingCart.aspx.cs в корне проекта веб-приложения.
Добавьте следующие разделы кода, выделенные желтым цветом, в файл ShoppingCart.aspx.cs :
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; using WingtipToys.Logic; using System.Collections.Specialized; using System.Collections; using System.Web.ModelBinding; namespace WingtipToys { public partial class ShoppingCart : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { decimal cartTotal = 0; cartTotal = usersShoppingCart.GetTotal(); if (cartTotal > 0) { // Display Total. lblTotal.Text = String.Format("{0:c}", cartTotal); } else { LabelTotalText.Text = ""; lblTotal.Text = ""; ShoppingCartTitle.InnerText = "Shopping Cart is Empty"; UpdateBtn.Visible = false; } } } public List<CartItem> GetShoppingCartItems() { ShoppingCartActions actions = new ShoppingCartActions(); return actions.GetCartItems(); } public List<CartItem> UpdateCartItems() { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { String cartId = usersShoppingCart.GetCartId(); ShoppingCartActions.ShoppingCartUpdates[] cartUpdates = new ShoppingCartActions.ShoppingCartUpdates[CartList.Rows.Count]; for (int i = 0; i < CartList.Rows.Count; i++) { IOrderedDictionary rowValues = new OrderedDictionary(); rowValues = GetValues(CartList.Rows[i]); cartUpdates[i].ProductId = Convert.ToInt32(rowValues["ProductID"]); CheckBox cbRemove = new CheckBox(); cbRemove = (CheckBox)CartList.Rows[i].FindControl("Remove"); cartUpdates[i].RemoveItem = cbRemove.Checked; TextBox quantityTextBox = new TextBox(); quantityTextBox = (TextBox)CartList.Rows[i].FindControl("PurchaseQuantity"); cartUpdates[i].PurchaseQuantity = Convert.ToInt16(quantityTextBox.Text.ToString()); } usersShoppingCart.UpdateShoppingCartDatabase(cartId, cartUpdates); CartList.DataBind(); lblTotal.Text = String.Format("{0:c}", usersShoppingCart.GetTotal()); return usersShoppingCart.GetCartItems(); } } public static IOrderedDictionary GetValues(GridViewRow row) { IOrderedDictionary values = new OrderedDictionary(); foreach (DataControlFieldCell cell in row.Cells) { if (cell.Visible) { // Extract values from the cell. cell.ContainingField.ExtractValuesFromCell(values, cell, row.RowState, true); } } return values; } protected void UpdateBtn_Click(object sender, EventArgs e) { UpdateCartItems(); } } }
Когда пользователь нажимает кнопку Обновить на странице ShoppingCart.aspx , вызывается метод UpdateCartItems. Метод UpdateCartItems получает обновленные значения для каждого элемента в корзине. Затем метод UpdateCartItems вызывает UpdateShoppingCartDatabase
метод (добавленный и описанный на следующем шаге) для добавления или удаления элементов из корзины. После обновления базы данных с учетом обновлений корзины элемент управления GridView обновляется на странице корзины покупок путем вызова DataBind
метода для GridView. Кроме того, общая сумма заказа на странице корзины обновляется с учетом обновленного списка товаров.
Обновление и удаление элементов корзины
На странице ShoppingCart.aspx можно увидеть, что добавлены элементы управления для обновления количества элемента и удаления элемента. Теперь добавьте код, который сделает эти элементы управления работой.
В Обозреватель решений откройте файл ShoppingCartActions.cs в папке Логика.
Добавьте следующий код, выделенный желтым цветом, в файл класса ShoppingCartActions.cs :
using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; namespace WingtipToys.Logic { public class ShoppingCartActions : IDisposable { public string ShoppingCartId { get; set; } private ProductContext _db = new ProductContext(); public const string CartSessionKey = "CartId"; public void AddToCart(int id) { // Retrieve the product from the database. ShoppingCartId = GetCartId(); var cartItem = _db.ShoppingCartItems.SingleOrDefault( c => c.CartId == ShoppingCartId && c.ProductId == id); if (cartItem == null) { // Create a new cart item if no cart item exists. cartItem = new CartItem { ItemId = Guid.NewGuid().ToString(), ProductId = id, CartId = ShoppingCartId, Product = _db.Products.SingleOrDefault( p => p.ProductID == id), Quantity = 1, DateCreated = DateTime.Now }; _db.ShoppingCartItems.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity. cartItem.Quantity++; } _db.SaveChanges(); } public void Dispose() { if (_db != null) { _db.Dispose(); _db = null; } } public string GetCartId() { if (HttpContext.Current.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name)) { HttpContext.Current.Session[CartSessionKey] = HttpContext.Current.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class. Guid tempCartId = Guid.NewGuid(); HttpContext.Current.Session[CartSessionKey] = tempCartId.ToString(); } } return HttpContext.Current.Session[CartSessionKey].ToString(); } public List<CartItem> GetCartItems() { ShoppingCartId = GetCartId(); return _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId).ToList(); } public decimal GetTotal() { ShoppingCartId = GetCartId(); // Multiply product price by quantity of that product to get // the current price for each of those products in the cart. // Sum all product price totals to get the cart total. decimal? total = decimal.Zero; total = (decimal?)(from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity * cartItems.Product.UnitPrice).Sum(); return total ?? decimal.Zero; } public ShoppingCartActions GetCart(HttpContext context) { using (var cart = new ShoppingCartActions()) { cart.ShoppingCartId = cart.GetCartId(); return cart; } } public void UpdateShoppingCartDatabase(String cartId, ShoppingCartUpdates[] CartItemUpdates) { using (var db = new WingtipToys.Models.ProductContext()) { try { int CartItemCount = CartItemUpdates.Count(); List<CartItem> myCart = GetCartItems(); foreach (var cartItem in myCart) { // Iterate through all rows within shopping cart list for (int i = 0; i < CartItemCount; i++) { if (cartItem.Product.ProductID == CartItemUpdates[i].ProductId) { if (CartItemUpdates[i].PurchaseQuantity < 1 || CartItemUpdates[i].RemoveItem == true) { RemoveItem(cartId, cartItem.ProductId); } else { UpdateItem(cartId, cartItem.ProductId, CartItemUpdates[i].PurchaseQuantity); } } } } } catch (Exception exp) { throw new Exception("ERROR: Unable to Update Cart Database - " + exp.Message.ToString(), exp); } } } public void RemoveItem(string removeCartID, int removeProductID) { using (var _db = new WingtipToys.Models.ProductContext()) { try { var myItem = (from c in _db.ShoppingCartItems where c.CartId == removeCartID && c.Product.ProductID == removeProductID select c).FirstOrDefault(); if (myItem != null) { // Remove Item. _db.ShoppingCartItems.Remove(myItem); _db.SaveChanges(); } } catch (Exception exp) { throw new Exception("ERROR: Unable to Remove Cart Item - " + exp.Message.ToString(), exp); } } } public void UpdateItem(string updateCartID, int updateProductID, int quantity) { using (var _db = new WingtipToys.Models.ProductContext()) { try { var myItem = (from c in _db.ShoppingCartItems where c.CartId == updateCartID && c.Product.ProductID == updateProductID select c).FirstOrDefault(); if (myItem != null) { myItem.Quantity = quantity; _db.SaveChanges(); } } catch (Exception exp) { throw new Exception("ERROR: Unable to Update Cart Item - " + exp.Message.ToString(), exp); } } } public void EmptyCart() { ShoppingCartId = GetCartId(); var cartItems = _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId); foreach (var cartItem in cartItems) { _db.ShoppingCartItems.Remove(cartItem); } // Save changes. _db.SaveChanges(); } public int GetCount() { ShoppingCartId = GetCartId(); // Get the count of each item in the cart and sum them up int? count = (from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity).Sum(); // Return 0 if all entries are null return count ?? 0; } public struct ShoppingCartUpdates { public int ProductId; public int PurchaseQuantity; public bool RemoveItem; } } }
Метод UpdateShoppingCartDatabase
, вызываемый из UpdateCartItems
метода на странице ShoppingCart.aspx.cs , содержит логику обновления или удаления элементов из корзины. Метод UpdateShoppingCartDatabase
выполняет итерацию по всем строкам в списке корзины для покупок. Если товар корзины помечен для удаления или количество меньше единицы, RemoveItem
вызывается метод . В противном случае элемент корзины проверяется на наличие обновлений при вызове UpdateItem
метода . После удаления или обновления элемента корзины сохраняются изменения базы данных.
Структура ShoppingCartUpdates
используется для хранения всех элементов корзины. Метод UpdateShoppingCartDatabase
использует структуру для ShoppingCartUpdates
определения необходимости обновления или удаления каких-либо элементов.
В следующем руководстве вы будете использовать метод для очистки EmptyCart
корзины после покупки продуктов. Но пока вы будете использовать GetCount
метод, который вы только что добавили в файл ShoppingCartActions.cs , чтобы определить, сколько элементов в корзине.
Добавление счетчика корзины покупок
Чтобы разрешить пользователю просматривать общее количество элементов в корзине, добавьте счетчик на страницу Site.Master . Этот счетчик также будет выступать в качестве ссылки на корзину.
В Обозреватель решений откройте страницу Site.Master.
Измените разметку, добавив в раздел навигации ссылку на счетчик корзины, как показано желтым цветом, чтобы она выглядела следующим образом:
<ul class="nav navbar-nav"> <li><a runat="server" href="~/">Home</a></li> <li><a runat="server" href="~/About">About</a></li> <li><a runat="server" href="~/Contact">Contact</a></li> <li><a runat="server" href="~/ProductList">Products</a></li> <li><a runat="server" href="~/ShoppingCart" ID="cartCount"> </a></li> </ul>
Затем обновите код программной части файла Site.Master.cs , добавив код, выделенный желтым цветом, следующим образом:
using System; using System.Collections.Generic; using System.Security.Claims; using System.Security.Principal; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Linq; using WingtipToys.Models; using WingtipToys.Logic; namespace WingtipToys { public partial class SiteMaster : MasterPage { private const string AntiXsrfTokenKey = "__AntiXsrfToken"; private const string AntiXsrfUserNameKey = "__AntiXsrfUserName"; private string _antiXsrfTokenValue; protected void Page_Init(object sender, EventArgs e) { // The code below helps to protect against XSRF attacks var requestCookie = Request.Cookies[AntiXsrfTokenKey]; Guid requestCookieGuidValue; if (requestCookie != null && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue)) { // Use the Anti-XSRF token from the cookie _antiXsrfTokenValue = requestCookie.Value; Page.ViewStateUserKey = _antiXsrfTokenValue; } else { // Generate a new Anti-XSRF token and save to the cookie _antiXsrfTokenValue = Guid.NewGuid().ToString("N"); Page.ViewStateUserKey = _antiXsrfTokenValue; var responseCookie = new HttpCookie(AntiXsrfTokenKey) { HttpOnly = true, Value = _antiXsrfTokenValue }; if (FormsAuthentication.RequireSSL && Request.IsSecureConnection) { responseCookie.Secure = true; } Response.Cookies.Set(responseCookie); } Page.PreLoad += master_Page_PreLoad; } protected void master_Page_PreLoad(object sender, EventArgs e) { if (!IsPostBack) { // Set Anti-XSRF token ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey; ViewState[AntiXsrfUserNameKey] = Context.User.Identity.Name ?? String.Empty; } else { // Validate the Anti-XSRF token if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue || (string)ViewState[AntiXsrfUserNameKey] != (Context.User.Identity.Name ?? String.Empty)) { throw new InvalidOperationException("Validation of Anti-XSRF token failed."); } } } protected void Page_Load(object sender, EventArgs e) { } protected void Page_PreRender(object sender, EventArgs e) { using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions()) { string cartStr = string.Format("Cart ({0})", usersShoppingCart.GetCount()); cartCount.InnerText = cartStr; } } public IQueryable<Category> GetCategories() { var _db = new WingtipToys.Models.ProductContext(); IQueryable<Category> query = _db.Categories; return query; } protected void Unnamed_LoggingOut(object sender, LoginCancelEventArgs e) { Context.GetOwinContext().Authentication.SignOut(); } } }
Перед отображением страницы в формате HTML Page_PreRender
возникает событие . В обработчике Page_PreRender
общее количество корзины определяется путем GetCount
вызова метода . Возвращаемое значение добавляется в диапазон, cartCount
включенный в разметку страницы Site.Master . Теги <span>
обеспечивают правильную отрисовку внутренних элементов. При отображении любой страницы сайта отображается итог корзины. Пользователь также может щелкнуть общую сумму корзины, чтобы отобразить корзину.
Тестирование завершенной корзины покупок
Теперь вы можете запустить приложение, чтобы узнать, как добавлять, удалять и обновлять элементы в корзине. Итоговая стоимость корзины будет отражать общую стоимость всех товаров в корзине.
Нажмите клавишу F5 для запуска приложения.
Откроется браузер со страницей Default.aspx .Выберите Автомобили в меню навигации по категориям.
Щелкните ссылку Добавить в корзину рядом с первым продуктом.
Отображается страница ShoppingCart.aspx с итоговой суммой заказа.Выберите Плоскости в меню навигации по категориям.
Щелкните ссылку Добавить в корзину рядом с первым продуктом.
Задайте количество первого товара в корзине равным 3 и выберите поле Удалить элемент проверка второго элемента.
Нажмите кнопку Обновить , чтобы обновить страницу корзины и отобразить новый итог заказа.
Итоги
В этом руководстве вы создали корзину для примера приложения Wingtip Toys веб-формы. В этом руководстве вы использовали Entity Framework Code First, заметки к данным, строго типизированные элементы управления данными и привязку модели.
Корзина покупок поддерживает добавление, удаление и обновление элементов, выбранных пользователем для покупки. Помимо реализации функции корзины покупок, вы узнали, как отображать элементы корзины покупок в элементе управления GridView и вычислять общую сумму заказа.
Чтобы понять, как описанная функциональность работает в реальном бизнес-приложении, можно просмотреть пример nopCommerce — ASP.NET на основе открытый код корзине для покупок электронной коммерции. Первоначально, он был построен на веб-формы и на протяжении многих лет он переехал в MVC, а теперь в ASP.NET Core.