ショッピング カート
作成者: Erik Reitan
Wingtip Toys サンプル プロジェクト (C#) のダウンロードまたは電子書籍 (PDF) のダウンロード
このチュートリアル シリーズでは、ASP.NET 4.5 と Microsoft Visual Studio Express 2013 for Web を使用した ASP.NET Web Forms アプリケーション構築の基本について説明します。 Visual Studio 2013 の C# ソース コードを含むプロジェクトは、このチュートリアル シリーズと併せて使用できます。
このチュートリアルでは、ショッピング カートを Wingtip Toys サンプル ASP.NET Web Forms アプリケーションに追加するために必要なビジネス ロジックについて説明します。 このチュートリアルは、前のチュートリアル「データ項目と詳細の表示」に基づいており、Wingtip Toy Store チュートリアル シリーズの一部です。 このチュートリアルを完了すると、サンプル アプリのユーザーは、ショッピング カート内の商品を追加、削除、変更できるようになります。
ここでは、次の内容について学習します。
- Web アプリケーション用のショッピング カートを作成する方法。
- ユーザーがショッピング カートにアイテムを追加できるようにする方法。
- ショッピング カートの詳細を表示する GridView コントロールを追加する方法。
- 注文合計を計算して表示する方法。
- ショッピング カート内のアイテムを削除および更新する方法。
- ショッピング カートのカウンターを含める方法。
このチュートリアルのコード機能:
- Entity Framework Code First
- データの注釈
- 厳密に型指定されたデータ コントロール
- モデル バインド
ショッピング カートの作成
このチュートリアル シリーズの前半では、データベースから商品データを表示するためのページとコードを追加しました。 このチュートリアルでは、ユーザーが購入に関心のある商品を管理するためのショッピング カートを作成します。 ユーザーは、登録またはログインしていない場合でも、アイテムを参照してショッピング カートに追加できます。 ショッピング カートへのアクセスを管理するには、ユーザーがショッピング カートに初めてアクセスするときに、グローバル一意識別子 (GUID) を使用してユーザーに一意の ID
を割り当てます。 この ID
は、ASP.NET セッション状態を使用して保存します。
Note
ASP.NET セッション状態は、ユーザー固有の情報を保存するのに便利な場所ですが、ユーザーがサイトを離れると有効期限が切れます。 大規模なサイトでは、セッション状態の誤用がパフォーマンスに影響を与える可能性がありますが、デモンストレーション目的の軽度な使用では、セッション状態は問題なく動作します。 Wingtip Toys サンプル プロジェクトは、外部プロバイダーなしでセッション状態を使用する方法を示しています。この場合、サイトをホストしている Web サーバーにセッション状態がインプロセスで保存されます。 アプリケーションの複数のインスタンスを提供する大規模なサイト、または異なるサーバー上でアプリケーションの複数のインスタンスを実行するサイトの場合は、Windows Azure キャッシュ サービスの使用を検討してください。 このキャッシュ サービスは、Web サイトの外部にある分散キャッシュ サービスを提供し、インプロセス セッション状態の使用の問題を解決します。 詳細については、「Windows Azure Web サイトで ASP.NET セッション状態を使用する方法」を参照してください。
CartItem をモデル クラスとして追加する
このチュートリアル シリーズの前半では、Models フォルダー内に Category
および Product
クラスを作成することで、カテゴリおよび商品データのスキーマを定義しました。 ここでは、ショッピング カートのスキーマを定義する新しいクラスを追加します。 このチュートリアルの後半では、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]
属性を使用して既定の動作をオーバーライドします。 ItemId プロパティの Key
属性は、ItemID
プロパティが主キーであることを指定します。
CartId
プロパティは、購入するアイテムに関連付けられているユーザーの ID
を指定します。 ユーザーがショッピング カートにアクセスしたときにこのユーザー ID
を作成するコードを追加します。 この ID
は、ASP.NET セッション変数としても保存されます。
商品コンテキストの更新
CartItem
クラスの追加に加えて、エンティティ クラスを管理し、データベースへのデータ アクセスを提供するデータベース コンテキスト クラスを更新する必要があります。 これを行うには、新しく作成した CartItem
モデル クラスを ProductContext
クラスに追加します。
ソリューション エクスプローラーで、Models フォルダー内の ProductContext.cs ファイルを見つけて開きます。
次のように、強調表示されたコードを 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 ファイル内のコードでは、ユーザーが Entity Framework のすべてのコア機能にアクセスできるように System.Data.Entity
名前空間を追加します。 この機能には、厳密に型指定されたオブジェクトを操作することによってデータのクエリ、挿入、更新、削除を行う機能が含まれています。 ProductContext
クラスは、新しく追加された CartItem
モデル クラスへのアクセスを追加します。
ショッピング カートのビジネス ロジックの管理
次に、新しい Logic フォルダーに ShoppingCart
クラスを作成します。 ShoppingCart
クラスは、CartItem
テーブルへのデータ アクセスを処理します。 このクラスには、ショッピング カート内のアイテムを追加、削除、更新するためのビジネス ロジックも含まれています。
追加するショッピング カートのロジックには、次のアクションを管理する機能が含まれています。
- ショッピング カートへのアイテムの追加
- ショッピング カートからのアイテムの削除
- ショッピング カート ID の取得
- ショッピング カートからのアイテムの取得
- すべてのショッピング カート アイテムの金額の合計
- ショッピング カートのデータの更新
ショッピング カート ページ (ShoppingCart.aspx) とショッピング カート クラスは、ショッピング カートのデータにアクセスするために一緒に使用されます。 ショッピング カート ページには、ユーザーがショッピング カートに追加するすべてのアイテムが表示されます。 ショッピング カート ページとショッピング カート クラスに加えて、ショッピング カートに商品を追加するページ (AddToCart.aspx) を作成します。 また、ユーザーがショッピング カートに商品を追加できるように、AddToCart.aspx ページへのリンクを提供する ProductList.aspx ページと ProductDetails.aspx ページにコードを追加します。
次の図は、ユーザーがショッピング カートに商品を追加するときに発生する基本的なプロセスを示しています。
ユーザーが ProductList.aspx ページまたは ProductDetails.aspx ページの [カートに追加] リンクをクリックすると、アプリケーションは AddToCart.aspx ページに移動してから、ShoppingCart.aspx ページに自動的に移動します。 AddToCart.aspx ページでは、ShoppingCart クラスのメソッドを呼び出すことで、選択した商品がショッピング カートに追加されます。 ShoppingCart.aspx ページには、ショッピング カートに追加された商品が表示されます。
ショッピング カート クラスの作成
モデル (Models フォルダー)、ページ (ルート フォルダー)、ロジック (Logic フォルダー) の区別が明確になるように、ShoppingCart
クラスはアプリケーション内の別のフォルダーに追加されます。
ソリューション エクスプローラーで、WingtipToys プロジェクトを右クリックし、[追加] ->[新しいフォルダー] を選択します。 新しいフォルダーに Logic という名前を付けます。
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 により、セッションに基づいて、ユーザーごとに 1 つのカートのみが作成されるようになります。
GetCartItems
メソッドは、ユーザーのショッピング カート アイテムのリストを返します。 このチュートリアルの後半では、モデル バインドを使用して、GetCartItems
メソッドを使用してショッピング カート内のカート アイテムを表示する方法を示します。
カートへの追加機能の作成
前述のように、AddToCart.aspx という名前の処理ページを作成します。このページを使用して、ユーザーのショッピング カートに新しい商品を追加します。 このページでは、先ほど作成した ShoppingCart
クラス内の AddToCart
メソッドが呼び出されます。 AddToCart.aspx ページでは、このメソッドに商品 ID
が渡されることが想定されます。 この商品 ID
は、ShoppingCart
クラス内の AddToCart
メソッドを呼び出すときに使用されます。
Note
このページのページ UI (AddToCart.aspx) ではなく、分離コード (AddToCart.aspx.cs) を変更します。
カートへの追加機能を作成するには:
ソリューション エクスプローラーで WingtipToys プロジェクトを右クリックし、[追加] ->[新しいアイテム] をクリックします。
[新しい項目の追加] ダイアログ ボックスが表示されます。AddToCart.aspx という名前の標準の新しいページ (Web フォーム) をアプリケーションに追加します。
ソリューション エクスプローラーで 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
が取得されます。 次に、ショッピング カート クラスのインスタンスが作成され、このチュートリアルで前に追加した AddToCart
メソッドを呼び出すために使用されます。 AddToCart
ShoppingCartActions.cs ファイルに含まれる メソッドには、選択した商品をショッピング カートに追加するか、選択した商品の商品数量をインクリメントするロジックが含まれています。 商品がショッピング カートに追加されていない場合、商品はデータベースの CartItem
テーブルに追加されます。 商品がショッピング カートに既に追加されているときに、ユーザーが同じ商品の追加アイテムを追加する場合、商品の数量は CartItem
テーブル内でインクリメントされます。 最後に、このページは、次の手順で追加する ShoppingCart.aspx ページにリダイレクトし戻されます。このページには、カート内のアイテムの更新されたリストが表示されます。
前述のように、ユーザー ID
は、特定のユーザーに関連付けられている商品を識別するために使用されます。 この ID
は、ユーザーがショッピング カートに商品を追加するたびに、CartItem
テーブルの行に追加されます。
ショッピング カート UI の作成
ShoppingCart.aspx ページには、ユーザーがショッピング カートに追加した商品が表示されます。 また、ショッピング カート内のアイテムを追加、削除、更新する機能も提供されます。
ソリューション エクスプローラーで WingtipToys を右クリックし、[追加] ->[新しいアイテム] をクリックします。
[新しい項目の追加] ダイアログ ボックスが表示されます。[マスター ページを使用した Web フォーム] を選択して、マスター ページを含む新しいページ (Web フォーム) を追加します。 新しいページに ShoppingCart.aspx という名前を付けます。
Site.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 ページには、CartList
という名前の GridView コントロールが含まれています。 このコントロールでは、モデル バインドを使用して、データベースからショッピング カートのデータを GridView コントロールにバインドします。 GridView コントロールの ItemType
プロパティを設定すると、コントロールのマークアップでデータ バインディング式 Item
を使用できるようになり、コントロールが厳密に型指定されます。 このチュートリアル シリーズの前半で説明したように、IntelliSense を使用して Item
オブジェクトの詳細を選択できます。 モデル バインドを使用してデータを選択するようにデータ コントロールを構成するには、コントロールの SelectMethod
プロパティを設定します。 上記のマークアップでは、CartItem
オブジェクトのリストを返す GetShoppingCartItems メソッドを使用するように SelectMethod
を設定します。 GridView データ コントロールは、ページ ライフ サイクルの適切なタイミングでメソッドを呼び出し、返されたデータを自動的にバインドします。 GetShoppingCartItems
メソッドは引き続き追加する必要があります。
ショッピング カート アイテムの取得
次に、ShoppingCart.aspx.cs 分離コードにコードを追加して、ショッピング カートの UI を取得して設定します。
ソリューション エクスプローラーで 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 ページでは、このチュートリアルで前に追加した ShoppingCart
クラスの AddToCart
メソッドが呼び出されます。
ここで、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 ページに別のアイテムが表示されます。ブラウザーを閉じます。
注文合計の計算と表示
ショッピング カートに商品を追加するだけでなく、GetTotal
メソッドを ShoppingCart
クラスに追加し、ショッピング カート ページに合計注文金額を表示します。
ソリューション エクスプローラーで、Logic フォルダー内の 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
メソッドは、ユーザーのショッピング カートの ID を取得します。 次に、カートに一覧表示されている各商品の商品数量を商品価格に乗算して、カートの合計を取得します。
Note
上記のコードでは、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 ページが読み込まれると、ショッピング カート オブジェクトが読み込まれ、ShoppingCart
クラスの GetTotal
メソッドを呼び出してショッピング カートの合計が取得されます。 ショッピング カートが空の場合は、その影響に関するメッセージが表示されます。
ショッピング カートの合計のテスト
今すぐアプリケーションを実行して、ショッピング カートに商品を追加する方法だけでなく、ショッピング カートの合計を表示する方法を確認します。
F5 キーを押してアプリケーションを実行します。
ブラウザーが開き、 Default.aspx ページが表示されます。カテゴリのナビゲーション メニューから [自動車] を選択します。
最初の商品の横にある [カートに追加] リンクをクリックします。
ShoppingCart.aspx ページに注文合計が表示されます。その他の商品 (飛行機など) をカートに追加します。
ShoppingCart.aspx ページに、追加したすべての商品の更新された合計が表示されます。
ブラウザー ウィンドウを閉じて、実行中のアプリを停止します。
ショッピング カートへの [更新] ボタンと [チェックアウト] ボタンの追加
ユーザーがショッピング カートを変更できるようにするには、[更新] ボタンと [チェックアウト] ボタンをショッピング カート ページに追加します。 [チェックアウト] ボタンは、このチュートリアル シリーズの後半まで使用されません。
ソリューション エクスプローラーで、Web アプリケーション プロジェクトのルートにある 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
メソッドを呼び出すことができます。
ソリューション エクスプローラーで、Web アプリケーション プロジェクトのルートにある 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 コントロールは、GridView の DataBind
メソッドを呼び出すことで、ショッピング カート ページで更新されます。 また、ショッピング カート ページの合計注文金額は、更新されたアイテムのリストを反映するように更新されます。
ショッピング カート アイテムの更新と削除
ShoppingCart.aspx ページでは、アイテムの数量の更新とアイテムの削除のためのコントロールが追加されたことを確認できます。 ここで、これらのコントロールを機能させるコードを追加します。
ソリューション エクスプローラーで、Logic フォルダー内の 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; } } }
ShoppingCart.aspx.cs ページの UpdateCartItems
メソッドから呼び出される UpdateShoppingCartDatabase
メソッドには、ショッピング カート内のアイテムを更新または削除するロジックが含まれています。 UpdateShoppingCartDatabase
メソッドは、ショッピング カート リスト内のすべての行を繰り返します。 ショッピング カート アイテムが削除対象としてマークされている場合、または数量が 1 未満の場合は、RemoveItem
メソッドが呼び出されます。 それ以外の場合は、UpdateItem
メソッドが呼び出されたときに、ショッピング カート アイテムが更新のためにチェックされます。 ショッピング カート アイテムが削除または更新されると、データベースの変更が保存されます。
ShoppingCartUpdates
構造体は、すべてのショッピング カート アイテムを保持するために使用されます。 UpdateShoppingCartDatabase
メソッドは、ShoppingCartUpdates
構造体を使用して、いずれかのアイテムを更新または削除する必要があるかどうかを判断します。
次のチュートリアルでは、EmptyCart
メソッドを使用して、商品を購入した後にショッピング カートをクリアします。 ただし、ここでは、先ほど ShoppingCartActions.cs ファイルに追加した GetCount
メソッドを使用して、ショッピング カート内のアイテムの数を確認します。
ショッピング カート カウンターの追加
ユーザーがショッピング カート内のアイテムの合計数を表示できるようにするには、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
メソッドを呼び出すことでショッピング カートの合計数が確認されます。 返された値は、Site.Master ページのマークアップに含まれる cartCount
スパンに追加されます。 <span>
タグを使用すると、内部要素を適切にレンダリングできます。 サイトの任意のページが表示されると、ショッピング カートの合計が表示されます。 ユーザーは、ショッピング カートの合計をクリックして、ショッピング カートを表示することもできます。
完成したショッピング カートのテスト
ここで、アプリケーションを実行して、ショッピング カート内のアイテムを追加、削除、更新する方法を確認できます。 ショッピング カートの合計には、ショッピング カート内のすべてのアイテムの合計コストが反映されます。
F5 キーを押してアプリケーションを実行します。
ブラウザーが開き、Default.aspx ページが表示されます。カテゴリのナビゲーション メニューから [自動車] を選択します。
最初の商品の横にある [カートに追加] リンクをクリックします。
ShoppingCart.aspx ページに注文合計が表示されます。カテゴリのナビゲーション メニューから [飛行機] を選択します。
最初の商品の横にある [カートに追加] リンクをクリックします。
ショッピング カートの最初のアイテムの数量を 3 に設定し、2 番目のアイテムの [アイテムの削除] チェック ボックスを選択します。
[更新] ボタンをクリックしてショッピング カート ページを更新し、新しい注文合計を表示します。
まとめ
このチュートリアルでは、Wingtip Toys Web Forms サンプル アプリケーションのショッピング カートを作成しました。 このチュートリアルでは、Entity Framework Code First、データ注釈、厳密に型指定されたデータ コントロール、およびモデル バインドを使用しました。
ショッピング カートでは、ユーザーが購入のために選択したアイテムの追加、削除、更新がサポートされています。 ショッピング カート機能の実装に加えて、GridView コントロールにショッピング カート アイテムを表示し、注文合計を計算する方法を学習しました。
説明されている機能が実際のビジネス アプリケーションでどのように機能するかを理解するために、nopCommerce - ASP.NET に基づくオープンソースの eコマース ショッピング カートを確認できます。 このカートはもともと、Web Forms 上に構築され、長年にわたって MVC に移行し、現在は ASP.NET Core に移行しています。