Část 5: Obchodní logika
Tailspin Spyworks ukazuje, jak je mimořádně jednoduché vytvářet výkonné a škálovatelné aplikace pro platformu .NET. Ukazuje, jak používat skvělé nové funkce v ASP.NET 4 k vytvoření online obchodu, včetně nakupování, pokladny a správy.
Tato série kurzů podrobně popisuje všechny kroky k sestavení ukázkové aplikace Tailspin Spyworks. Část 5 přidává obchodní logiku.
Přidání obchodní logiky
Chceme, aby naše nákupní prostředí bylo k dispozici vždy, když někdo navštíví náš web. Návštěvníci budou moct procházet a přidávat položky do nákupního košíku, i když nejsou zaregistrované nebo přihlášené. Jakmile budou připravení k pokladně, budou mít možnost ověření, a pokud ještě nejsou členy, budou si moct vytvořit účet.
To znamená, že budeme muset implementovat logiku pro převod nákupního košíku z anonymního stavu do stavu "Registrovaný uživatel".
Vytvoříme adresář s názvem "Classes" a pak Right-Click do složky a vytvoříme nový soubor Třídy s názvem MyShoppingCart.cs.
Jak jsme již zmínili, rozšíříme třídu, která implementuje stránku MyShoppingCart.aspx, a provedeme to pomocí . Výkonný konstruktor "Částečná třída" sítě NET.
Vygenerované volání souboru MyShoppingCart.aspx.cf vypadá takto.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace TailspinSpyworks
{
public partial class MyShoppingCart : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
Všimněte si použití klíčového slova "partial".
Soubor třídy, který jsme právě vygenerovali, vypadá takto.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TailspinSpyworks.Classes
{
public class MyShoppingCart
{
}
}
Naše implementace sloučíme také přidáním částečného klíčového slova do tohoto souboru.
Náš nový soubor třídy teď vypadá takto.
namespace TailspinSpyworks.Classes
{
public partial class MyShoppingCart
{
}
}
První metodou, kterou přidáme do naší třídy, je metoda "AddItem". Jedná se o metodu, která bude nakonec volána, když uživatel klikne na odkazy "Přidat do obrázku" na stránce Seznam produktů a Podrobnosti o produktu.
K příkazům using v horní části stránky připojte následující příkaz.
using TailspinSpyworks.Data_Access;
A přidejte tuto metodu do třídy MyShoppingCart.
//------------------------------------------------------------------------------------+
public void AddItem(string cartID, int productID, int quantity)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
var myItem = (from c in db.ShoppingCarts where c.CartID == cartID &&
c.ProductID == productID select c).FirstOrDefault();
if(myItem == null)
{
ShoppingCart cartadd = new ShoppingCart();
cartadd.CartID = cartID;
cartadd.Quantity = quantity;
cartadd.ProductID = productID;
cartadd.DateCreated = DateTime.Now;
db.ShoppingCarts.AddObject(cartadd);
}
else
{
myItem.Quantity += quantity;
}
db.SaveChanges();
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Add Item to Cart - " +
exp.Message.ToString(), exp);
}
}
}
Používáme LINQ to Entities k tomu, abychom zjistili, jestli je položka již v košíku. Pokud ano, aktualizujeme množství objednávky položky, jinak vytvoříme novou položku pro vybranou položku.
Abychom mohli tuto metodu volat, implementujeme stránku AddToCart.aspx, která nejen třídí tuto metodu, ale po přidání položky zobrazí aktuální nákupní a=cart.
Right-Click na název řešení v Průzkumníku řešení a přidejte novou stránku s názvem AddToCart.aspx, jak jsme to udělali dříve.
I když bychom tuto stránku mohli použít k zobrazení průběžných výsledků, jako jsou problémy s nízkými sklady atd. v naší implementaci, stránka se ve skutečnosti nevykreslí, ale zavolá logiku "Přidat" a přesměrování.
Abychom toho dosáhli, přidáme do události Page_Load následující kód.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;
namespace TailspinSpyworks
{
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) && Int32.TryParse(rawId, out productId))
{
MyShoppingCart usersShoppingCart = new MyShoppingCart();
String cartId = usersShoppingCart.GetShoppingCartId();
usersShoppingCart.AddItem(cartId, productId, 1);
}
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("MyShoppingCart.aspx");
}
}
}
Všimněte si, že načítáme produkt, který se má přidat do nákupního košíku z parametru QueryString, a voláme metodu AddItem naší třídy.
Za předpokladu, že nedojde k žádným chybám, je ovládací prvek předán na stránku SHoppingCart.aspx, kterou budeme plně implementovat dále. Pokud by se měla zobrazit chyba, vyvoláme výjimku.
V současné době jsme ještě neimplementovali globální obslužnou rutinu chyb, takže tato výjimka by naší aplikací nezvládla, ale brzy to napravíme.
Všimněte si také použití příkazu Debug.Fail() (k dispozici prostřednictvím using System.Diagnostics;)
Je-li aplikace spuštěna v ladicím programu, zobrazí tato metoda podrobné dialogové okno s informacemi o stavu aplikací spolu s chybovou zprávou, kterou zadáme.
Při spuštění v produkčním prostředí se příkaz Debug.Fail() ignoruje.
V kódu výše si všimněte volání metody v našem nákupním košíku s názvy tříd "GetShoppingCartId".
Následujícím způsobem přidejte kód pro implementaci metody.
Všimněte si, že jsme také přidali tlačítka pro aktualizaci a rezervaci a popisek, kde můžeme zobrazit "celkový součet" košíku.
public const string CartId = "TailSpinSpyWorks_CartID";
//--------------------------------------------------------------------------------------+
public String GetShoppingCartId()
{
if (Session[CartId] == null)
{
Session[CartId] = System.Web.HttpContext.Current.Request.IsAuthenticated ?
User.Identity.Name : Guid.NewGuid().ToString();
}
return Session[CartId].ToString();
}
Teď můžeme přidávat položky do nákupního košíku, ale neimplementovali jsme logiku pro zobrazení košíku po přidání produktu.
Takže na stránce MyShoppingCart.aspx přidáme ovládací prvek EntityDataSource a ovládací prvek GridVire následujícím způsobem.
<div id="ShoppingCartTitle" runat="server" class="ContentHead">Shopping Cart</div>
<asp:GridView ID="MyList" runat="server" AutoGenerateColumns="False" ShowFooter="True"
GridLines="Vertical" CellPadding="4"
DataSourceID="EDS_Cart"
DataKeyNames="ProductID,UnitCost,Quantity"
CssClass="CartListItem">
<AlternatingRowStyle CssClass="CartListItemAlt" />
<Columns>
<asp:BoundField DataField="ProductID" HeaderText="Product ID" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ModelNumber" HeaderText="Model Number"
SortExpression="ModelNumber" />
<asp:BoundField DataField="ModelName" HeaderText="Model Name"
SortExpression="ModelName" />
<asp:BoundField DataField="UnitCost" HeaderText="Unit Cost" ReadOnly="True"
SortExpression="UnitCost"
DataFormatString="{0:c}" />
<asp:TemplateField>
<HeaderTemplate>Quantity</HeaderTemplate>
<ItemTemplate>
<asp:TextBox ID="PurchaseQuantity" Width="40" runat="server"
Text='<%# Bind("Quantity") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderTemplate>Item Total</HeaderTemplate>
<ItemTemplate>
<%# (Convert.ToDouble(Eval("Quantity")) *
Convert.ToDouble(Eval("UnitCost")))%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderTemplate>Remove Item</HeaderTemplate>
<ItemTemplate>
<center>
<asp:CheckBox id="Remove" runat="server" />
</center>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<FooterStyle CssClass="CartListFooter"/>
<HeaderStyle CssClass="CartListHead" />
</asp:GridView>
<div>
<strong>
<asp:Label ID="LabelTotalText" runat="server" Text="Order Total : ">
</asp:Label>
<asp:Label CssClass="NormalBold" id="lblTotal" runat="server"
EnableViewState="false">
</asp:Label>
</strong>
</div>
<br />
<asp:imagebutton id="UpdateBtn" runat="server" ImageURL="Styles/Images/update_cart.gif"
onclick="UpdateBtn_Click"></asp:imagebutton>
<asp:imagebutton id="CheckoutBtn" runat="server"
ImageURL="Styles/Images/final_checkout.gif"
PostBackUrl="~/CheckOut.aspx">
</asp:imagebutton>
<asp:EntityDataSource ID="EDS_Cart" runat="server"
ConnectionString="name=CommerceEntities"
DefaultContainerName="CommerceEntities" EnableFlattening="False"
EnableUpdate="True" EntitySetName="ViewCarts"
AutoGenerateWhereClause="True" EntityTypeFilter="" Select=""
Where="">
<WhereParameters>
<asp:SessionParameter Name="CartID" DefaultValue="0"
SessionField="TailSpinSpyWorks_CartID" />
</WhereParameters>
</asp:EntityDataSource>
Zavolejte formulář v návrháři, abyste mohli dvakrát kliknout na tlačítko Aktualizovat košík a vygenerovat obslužnou rutinu události click, která je zadána v deklaraci v kódu.
Podrobnosti budeme implementovat později, ale to nám umožní sestavit a spustit aplikaci bez chyb.
Když spustíte aplikaci a přidáte položku do nákupního košíku, uvidíte to.
Všimněte si, že jsme se odchýlili od zobrazení "výchozí" mřížky implementací tří vlastních sloupců.
První je upravitelné pole "Vázané" pro Množství:
<asp:TemplateField>
<HeaderTemplate>Quantity</HeaderTemplate>
<ItemTemplate>
<asp:TextBox ID="PurchaseQuantity" Width="40" runat="server"
Text='<%# Bind("Quantity") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
Další je "počítaný" sloupec, který zobrazuje celkový počet řádkových položek (náklady na položku krát množství, které se má objednat):
<asp:TemplateField>
<HeaderTemplate>Item Total</HeaderTemplate>
<ItemTemplate>
<%# (Convert.ToDouble(Eval("Quantity")) *
Convert.ToDouble(Eval("UnitCost")))%>
</ItemTemplate>
</asp:TemplateField>
Nakonec máme vlastní sloupec, který obsahuje ovládací prvek CheckBox, který uživatel použije k označení, že položka by měla být odebrána z nákupního grafu.
<asp:TemplateField>
<HeaderTemplate>Remove Item</HeaderTemplate>
<ItemTemplate>
<center>
<asp:CheckBox id="Remove" runat="server" />
</center>
</ItemTemplate>
</asp:TemplateField>
Jak vidíte, řádek Order Total (Celkem objednávky) je prázdný, takže přidáme logiku pro výpočet součtu objednávky.
Nejprve implementujeme metodu GetTotal do třídy MyShoppingCart.
Do souboru MyShoppingCart.cs přidejte následující kód.
//--------------------------------------------------------------------------------------+
public decimal GetTotal(string cartID)
{
using (CommerceEntities db = new CommerceEntities())
{
decimal cartTotal = 0;
try
{
var myCart = (from c in db.ViewCarts where c.CartID == cartID select c);
if (myCart.Count() > 0)
{
cartTotal = myCart.Sum(od => (decimal)od.Quantity * (decimal)od.UnitCost);
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Calculate Order Total - " +
exp.Message.ToString(), exp);
}
return (cartTotal);
}
}
Pak v obslužné rutině události Page_Load můžeme volat metodu GetTotal. Současně přidáme test, abychom zjistili, jestli je nákupní košík prázdný, a podle toho upravíme zobrazení, pokud je.
Pokud je nákupní košík prázdný, dostaneme toto:
A pokud ne, vidíme náš součet.
Tato stránka ale ještě není dokončená.
Budeme potřebovat další logiku k přepočítání nákupního košíku odebráním položek označených k odebrání a určením nových hodnot množství, protože některé mohly být změněny v mřížce uživatelem.
Do třídy nákupního košíku v souboru MyShoppingCart.cs přidáme metodu RemoveItem, která zpracuje případ, kdy uživatel označí položku k odebrání.
//------------------------------------------------------------------------------------+
public void RemoveItem(string cartID, int productID)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
var myItem = (from c in db.ShoppingCarts where c.CartID == cartID &&
c.ProductID == productID select c).FirstOrDefault();
if (myItem != null)
{
db.DeleteObject(myItem);
db.SaveChanges();
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Remove Cart Item - " +
exp.Message.ToString(), exp);
}
}
}
Teď pojďme inzerovat metodu pro řešení okolností, kdy uživatel jednoduše změní kvalitu tak, aby byla seřazena v GridView.
//--------------------------------------------------------------------------------------+
public void UpdateItem(string cartID, int productID, int quantity)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
var myItem = (from c in db.ShoppingCarts where c.CartID == cartID &&
c.ProductID == productID 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);
}
}
}
Se základními funkcemi Odebrat a Aktualizovat můžeme implementovat logiku, která skutečně aktualizuje nákupní košík v databázi. (V souboru MyShoppingCart.cs)
//-------------------------------------------------------------------------------------+
public void UpdateShoppingCartDatabase(String cartId,
ShoppingCartUpdates[] CartItemUpdates)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
int CartItemCOunt = CartItemUpdates.Count();
var myCart = (from c in db.ViewCarts where c.CartID == cartId select c);
foreach (var cartItem in myCart)
{
// Iterate through all rows within shopping cart list
for (int i = 0; i < CartItemCOunt; i++)
{
if (cartItem.ProductID == CartItemUpdates[i].ProductId)
{
if (CartItemUpdates[i].PurchaseQantity < 1 ||
CartItemUpdates[i].RemoveItem == true)
{
RemoveItem(cartId, cartItem.ProductID);
}
else
{
UpdateItem(cartId, cartItem.ProductID,
CartItemUpdates[i].PurchaseQantity);
}
}
}
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Update Cart Database - " +
exp.Message.ToString(), exp);
}
}
}
Všimněte si, že tato metoda očekává dva parametry. Jeden je ID nákupního košíku a druhý je pole objektů uživatelem definovaného typu.
Abychom minimalizovali závislost naší logiky na specifikách uživatelského rozhraní, definovali jsme datovou strukturu, kterou můžeme použít k předání položek nákupního košíku do našeho kódu, aniž by naše metoda potřebovala přímý přístup k ovládacímu prvku GridView.
public struct ShoppingCartUpdates
{
public int ProductId;
public int PurchaseQantity;
public bool RemoveItem;
}
V souboru MyShoppingCart.aspx.cs můžeme tuto strukturu použít v obslužné rutině události Kliknutí na tlačítko aktualizace následujícím způsobem. Všimněte si, že kromě aktualizace košíku aktualizujeme také celkový počet košíků.
//--------------------------------------------------------------------------------------+
protected void UpdateBtn_Click(object sender, ImageClickEventArgs e)
{
MyShoppingCart usersShoppingCart = new MyShoppingCart();
String cartId = usersShoppingCart.GetShoppingCartId();
ShoppingCartUpdates[] cartUpdates = new ShoppingCartUpdates[MyList.Rows.Count];
for (int i = 0; i < MyList.Rows.Count; i++)
{
IOrderedDictionary rowValues = new OrderedDictionary();
rowValues = GetValues(MyList.Rows[i]);
cartUpdates[i].ProductId = Convert.ToInt32(rowValues["ProductID"]);
cartUpdates[i].PurchaseQantity = Convert.ToInt32(rowValues["Quantity"]);
CheckBox cbRemove = new CheckBox();
cbRemove = (CheckBox)MyList.Rows[i].FindControl("Remove");
cartUpdates[i].RemoveItem = cbRemove.Checked;
}
usersShoppingCart.UpdateShoppingCartDatabase(cartId, cartUpdates);
MyList.DataBind();
lblTotal.Text = String.Format("{0:c}", usersShoppingCart.GetTotal(cartId));
}
Se zvláštním zájmem si všimněte tohoto řádku kódu:
rowValues = GetValues(MyList.Rows[i]);
GetValues() je speciální pomocná funkce, kterou budeme implementovat v souboru MyShoppingCart.aspx.cs následujícím způsobem.
//--------------------------------------------------------------------------------------+
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;
}
To poskytuje čistý způsob přístupu k hodnotám vázaných prvků v ovládacím prvku GridView. Vzhledem k tomu, že náš ovládací prvek CheckBox "Remove Item" není vázán, budeme k němu přistupovat prostřednictvím metody FindControl().
V této fázi vývoje vašeho projektu se připravujeme na implementaci procesu rezervace.
Než to uděláme, použijeme Visual Studio k vygenerování databáze členství a přidání uživatele do úložiště členství.