Tailspin Spyworks指南第五讲:业务逻辑

来源:互联网 发布:军事武器数据库 编辑:程序博客网 时间:2024/06/06 11:43

Part 5: Business Logic
第五讲:业务逻辑

By Joe Stagner|July 21, 2010
Translated By litdwg |March 12,2014

Tailspin Spyworks demonstrates how extraordinarily simple it is to create powerful, scalable applications for the .NET platform. It shows off how to use the great new features in ASP.NET 4 to build an online store, including shopping, checkout, and administration.

通过Tailspin Spyworks 演示在.NET平台创建功能强大,结构良好的应用程序有多么简单。演示如何使用ASP.NET 4的新特性创建一个包含购物、结算和管理功能的在线网店。

This tutorial series details all of the steps taken to build the Tailspin Spyworks sample application.  Part 5 adds some business logic.

本系列指南对构建案例程序的每一步做了详细的解释。第五部分添加一些业务逻辑。

添加Business Logic

添加业务逻辑

We want our shopping experience to be available whenever someone visits our web site. Visitors will be able to browse and add items to the shopping cart even if they are not registered or logged in. When they are ready to check out they will be given the option to authenticate and if they are not yet members they will be able to create an account.

我们希望访问我们网站的任何人都能体验购物。无论顾客是否登录或注册都可以浏览产品,添加产品到购物车。当顾客准备结算时再进行身份验证,如果顾客还不是我们的用户,可能会完成注册操作。

This means that we will need to implement the logic to convert the shopping cart from an anonymous state to a "Registered User" state.

这也就是说我们需要实现购物车从匿名状态到“已注册用户”状态转换的逻辑。

Let's create a directory named "Classes" then Right-Click on the folder and create a new "Class" file named MyShoppingCart.cs

创建一个名为“Classes”的文件夹,在其上右击选择创建新的类文件,命名为MyShoppingCart.cs。

As previously mentioned we will be extending the class that implements the MyShoppingCart.aspx page and we will do this using .NET's powerful "Partial Class" construct.

之前提到我们将在实现MyShoppingCart.aspx 页时对这个类进行扩展,这是通过.NET强大的”Partial Class“结构实现的。

The generated call for our MyShoppingCart.aspx.cs file looks like this.

自动生成的MyShoppingCart.aspx.cs文件是这样的:

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)        {        }    }}

注意关键词 "partial" .

我们刚创建的类,自动生成的代码是这样的:

using System;using System.Collections.Generic;using System.Linq;using System.Web;namespace TailspinSpyworks.Classes{    public class MyShoppingCart    {    }}

We will merge our implementations by adding the partial keyword to this file as well.

在此添加partial关键词,将它们合二为一。

Our new class file now looks like this.

修改后的类文件应该是这样的:

namespace TailspinSpyworks.Classes{    public partial class MyShoppingCart    {    }}

The first method that we will add to our class is the "AddItem" method. This is the method that will ultimately be called when the user clicks on the "Add to Art" links on the Product List and Product Details pages.

第一个要添加的方法是”AddItem“,用户在产品列表或产品详情页面点击”添加到购物车“时将调用此方法。

Append the following to the using statements at the top of the page.

在首部添加引用:

usingTailspinSpyworks.Data_Access;

And add this method to the MyShoppingCart class.

 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);      }   }}

We are using LINQ to Entities to see if the item is already in the cart. If so, we update the order quantity of the item, otherwise we create a new entry for the selected item

使用LINQ to Entities查看购物车是否存在此商品,如果存在则更新数量,不存在将创建新记录。

In order to call this method we will implement an AddToCart.aspx page that not only class this method but then displayed the current shopping a=cart after the item has been added.

为了调用此方法,创建 AddToCart.aspx页面,在添加商品后显示当前购物车的详情。

Right-Click on the solution name in the solution explorer and add and new page named AddToCart.aspx as we have done previously.

和之前一样的方法,添加AddToCart.aspx。

While we could use this page to display interim results like low stock issues, etc, in our implementation, the page will not actually render, but rather call the "Add" logic and redirect.

此页面并不会显示,而是调用”添加“逻辑,然后重定向了。

To accomplish this we'll add the following code to the Page_Load event.

为完成此功能,在Page_Load添加代码,如下:

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");        }    }}

Note that we are retrieving the product to add to the shopping cart from a QueryString parameter and calling the AddItem method of our class.

注意,我们是通过QueryString参数传递要加入购物车的商品,然后调用AddItem方法。

Assuming no errors are encountered control is passed to the SHoppingCart.aspx page which we will fully implement next. If there should be an error we throw an exception.

如果没有错误则跳转到 SHoppingCart.aspx page 页面(此页面随后实现),如果有错则抛出异常。

Currently we have not yet implemented a global error handler so this exception would go unhandled by our application but we will remedy this shortly.

当前我们还没有实现全局的错误处理机制,此异常将直接抛出不会被处理,随后将修复这一问题。

Note also the use of the statement Debug.Fail() (available via using System.Diagnostics;)

注意Debug.Fail()的用法。

Is the application is running inside the debugger, this method will display a detailed dialog with information about the applications state along with the error message that we specify.

如果程序是处在调试运行状态,此方法将弹出对话框,显示程序的状态和指明的错误信息。

When running in production the Debug.Fail() statement is ignored.

当运行在发行版本时,此语句将被忽略。

You will note in the code above a call to a method in our shopping cart class names "GetShoppingCartId".

注意,语句中调用了另一个方法”GetShoppingCartId“。

Add the code to implement the method as follows.

添加如下代码实现此方法。

Note that we've also added update and checkout buttons and a label where we can display the cart "total".

注意,我们还添加了更新和结算按钮,还有一个现实总价的标签。

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();}

We can now add items to our shopping cart but we have not implemented the logic to display the cart after a product has been added.

我们现在可以把商品添加到购物车了,但还没有实现显示购物车详情的逻辑。

So, in the MyShoppingCart.aspx page we'll add an EntityDataSource control and a GridVire control as follows.

因此,在MyShoppingCart.aspx页面添加EntityDataSource 控件和GridView控件,代码如下:

<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>

Call up the form in the designer so that you can double click on the Update Cart button and generate the click event handler that is specified in the declaration in the markup.

在设计视图双击UpdateCart按钮,生成已经在标记中定义的事件响应函数。

We'll implement the details later but doing this will let us build and run our application without errors.

随后再实现此函数的具体功能,这里先生成空函数避免运行程序出错。

When you run the application and add an item to the shopping cart you will see this.

运行程序,将产品添加到购物车后的效果如下:

Note that we have deviated from the "default" grid display by implementing three custom columns.

注意:我们实现了三个自定义列。

The first is an Editable, "Bound" field for the Quantity:

第一个是可编辑的”Bound“域,用来显示数量。

    <asp:TemplateField>       <HeaderTemplate>Quantity</HeaderTemplate>      <ItemTemplate>         <asp:TextBox ID="PurchaseQuantity" Width="40" runat="server"                       Text='<%# Bind("Quantity") %>'></asp:TextBox>       </ItemTemplate>    </asp:TemplateField>

The next is a "calculated" column that displays the line item total (the item cost times the quantity to be ordered):

”calculated“列显示此商品的总价(单价乘以数量):

     <asp:TemplateField>       <HeaderTemplate>Item Total</HeaderTemplate>      <ItemTemplate>        <%# (Convert.ToDouble(Eval("Quantity")) *               Convert.ToDouble(Eval("UnitCost")))%>      </ItemTemplate>    </asp:TemplateField>

Lastly we have a custom column that contains a CheckBox control that the user will use to indicate that the item should be removed from the shopping chart.

最后一个自定义列包含选择框控件,用户通过选中此控件将不需要的商品从购物车移除。

    <asp:TemplateField>     <HeaderTemplate>Remove Item</HeaderTemplate>      <ItemTemplate>        <center>          <asp:CheckBox id="Remove" runat="server" />        </center>      </ItemTemplate>    </asp:TemplateField>

As you can see, the Order Total line is empty so let's add some logic to calculate the Order Total.

如你所见,总价现在还没有显示,添加一些逻辑来计算总价。

We'll first implement a "GetTotal" method to our MyShoppingCart Class.

首先在MyShoppingCart类实现”GetTotal“方法。

In the MyShoppingCart.cs file add the following code.

代码如下:

//--------------------------------------------------------------------------------------+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);     }   }

Then in the Page_Load event handler we'll can call our GetTotal method. At the same time we'll add a test to see if the shopping cart is empty and adjust the display accordingly if it is.

然后在Page_Load事件处理函数中调用GetTotal方法。同时在其中测试购物车是否为空,如果为空时调整显示内容。

Now if the shopping cart is empty we get this:

如果为空时,显示如下:

And if not, we see our total.

如果不为空,可以看到总价。

However, this page is not yet complete.

此页面还没有做完。

We will need additional logic to recalculate the shopping cart by removing items marked for removal and by determining new quantity values as some may have been changed in the grid by the user.

添加新的逻辑实现用户移除商品或修改商品数量时重新计算购物车。

Lets add a "RemoveItem" method to our shopping cart class in MyShoppingCart.cs to handle the case when a user marks an item for removal.

 MyShoppingCart.cs添加”RemoveItem“方法,处理移除商品的操作。

//------------------------------------------------------------------------------------+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);      }    }}

Now let's ad a method to handle the circumstance when a user simply changes the quality to be ordered in the GridView.

添加”UpdateItem“方法,处理修改数量的操作。

//--------------------------------------------------------------------------------------+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);        }      }}

With the basic Remove and Update features in place we can implement the logic that actually updates the shopping cart in the database. (In 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);        }                }           }

You'll note that this method expects two parameters. One is the shopping cart Id and the other is an array of objects of user defined type.

注意,此方法需要两个参数,一个是购物车Id,另一个是自定义类型的对象数组。

So as to minimize the dependency of our logic on user interface specifics, we've defined a data structure that we can use to pass the shopping cart items to our code without our method needing to directly access the GridView control.

为了最小化业务逻辑对用户接口的依赖,定义了一个数据结构,使用此结构可把购物车记录传递给代码,而无需在代码中直接访问GridView控件。

public struct ShoppingCartUpdates{   public int ProductId;   public int PurchaseQantity;   public bool RemoveItem;}

In our MyShoppingCart.aspx.cs file we can use this structure in our Update Button Click Event handler as follows. Note that in addition to updating the cart we will update the cart total as well.

MyShoppingCart.aspx.cs文件的UpdateBtn_Click中使用此结构。注意除了更新购物车还要更新总价。

//--------------------------------------------------------------------------------------+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));}

Note with particular interest this line of code:

特别要关注这一行代码:

rowValues = GetValues(MyList.Rows[i]);

GetValues() is a special helper function that we will implement in MyShoppingCart.aspx.cs as follows.

GetValues()函数在MyShoppingCart.aspx.cs中实现,代码如下:

//--------------------------------------------------------------------------------------+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;} 

This provides a clean way to access the values of the bound elements in our GridView control. Since our "Remove Item" CheckBox Control is not bound we'll access it via the FindControl() method.

提供了一个干净的方式访问GridView控件中绑定元素的值。”Remove Item“选择框控件不是绑定实现的,随后通过FindControl()方法来操作。

At this stage in your project's development we are getting ready to implement the checkout process.

我们已经为实现结算做好了准备。

Before doing so let's use Visual Studio to generate the membership database and add a user to the membership repository.

在处理结算之前,使用VS生成membership数据库,添加用户。

0 0
原创粉丝点击