好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

构建一个真实的应用电子商务SportsStore(七)

构建一个真实的应用电子商务SportsStore(七)

构建一个真实的应用电子商务SportsStore(七)

我们的项目进展相当的不错,但是现在还不能真正的出售商品,因为我们没有为顾客提供购物车。今天,我们就加入购物车的功能,毕竟赚钱才是赢道理啊!购物车的逻辑看起来应该像这样:

我们需要在每件商品的旁边都加一个"Add to cart”的按钮,客户可以随时的添加自己选择的商品。现在就让我们到Domain工程的Entities文件夹去添加一个Cart类吧:

 using   System;
  using   System.Collections.Generic;
  using   System.Linq;
  using   System.Text;
  using   System.Threading.Tasks;

  namespace   SportsStore.Domain.Entities 
{
          public   class   Cart {

                  private  List<CartLine> lineCollection =  new  List<CartLine> ();

                  public   void  AddItem(Product product,  int   quantity) {

                    CartLine line  =  lineCollection
                                      .Where(p  => p.Product.ProductID ==  product.ProductID)
                                      .FirstOrDefault();

                      if  (line ==  null  ) {
                        lineCollection.Add(  new  CartLine { Product =  product,
                        Quantity  =  quantity });
                    }   else   {
                        line.Quantity  +=  quantity;
                    }

                }

                  public   void   RemoveLine(Product product) {
                    lineCollection.RemoveAll(l  => l.Product.ProductID ==  product.ProductID);
                }

                  public   decimal   ComputeTotalValue() {
                       return  lineCollection.Sum(e => e.Product.Price *  e.Quantity);
                }

                  public   void   Clear() {
                    lineCollection.Clear();
                }

                  public  IEnumerable<CartLine>  Lines {
                      get  {  return   lineCollection; }
                }
           }

          public   class   CartLine {
              public  Product Product {  get ;  set  ; }
              public   int  Quantity {  get ;  set  ; }
        }
} 

Cart类使用CartLine去展示客户选择的产品和想购买的数量,我们已经定义了添加商品到购物车的方法, 从购物车删除商品的方法,计算购物车中的商品总额的方法和重置购物车,清空说有商品的方法。我们也提供了一个属性,使用IEnumerble<CartLine>.去访问购物车的内容,所有这些通过LinQ很容易实现。

测试购物车

购物车是我们网站中非常重要的功能,我们必须确保它能正常的运行。为此我们必须要对它进行测试,我们现在就在测试工程中添加一个购物车的测试文件,叫做CartTests.cs。我们要测试的第一项内容是,当一个商品第一次被添加到购物车时,我们希望购物车能添加一个新的CartLine。

 using   System;
  using   Microsoft.VisualStudio.TestTools.UnitTesting;
  using   SportsStore.Domain.Entities;
  using   System.Linq;

  namespace   SportsStore.UnitTests {

        [TestClass]
          public   class   CartTests
        {
            [TestMethod]
              public   void   Can_Add_New_Lines()
            {
                  //   Arrange - create some test products 
                Product p1 =  new  Product { ProductID =  1 , Name =  "  P1  "   };
                Product p2  =  new  Product { ProductID =  2 , Name =  "  P2  "   };
                  //   Arrange - create a new cart 
                Cart target =  new   Cart();
                  //   Act 
                target.AddItem(p1,  1  );
                target.AddItem(p2,   1  );
                CartLine[] results  =  target.Lines.ToArray();
                  //   Assert 
                Assert.AreEqual(results.Length,  2  );
                Assert.AreEqual(results[  0  ].Product, p1);
                Assert.AreEqual(results[  1  ].Product, p2);
            }
      }
} 

然而,这必然存在一个隐患,如果顾客已经添加过这个商品,再次添加时,我们希望增加的是购物车中的数量,而不是创建一个新的CartLine.

             [TestMethod]
              public   void   Can_Add_Quantity_For_Existing_Lines()
            {
                  //   Arrange - create some test products 
                Product p1 =  new  Product { ProductID =  1 , Name =  "  P1  "   };
                Product p2  =  new  Product { ProductID =  2 , Name =  "  P2  "   };
                  //   Arrange - create a new cart 
                Cart target =  new   Cart();
                  //   Act 
                target.AddItem(p1,  1  );
                target.AddItem(p2,   1  );
                target.AddItem(p1,   10  );
                CartLine[] results  = target.Lines.OrderBy(c =>  c.Product.ProductID).ToArray();
                  //   Assert 
                Assert.AreEqual(results.Length,  2  );
                Assert.AreEqual(results[  0 ].Quantity,  11  );
                Assert.AreEqual(results[  1 ].Quantity,  1  );
            } 

用户也会随时改变想法,从购物车中删除商品,这个方法我们应实现了,但我们还是需要去Check,这里也需要测试。

              [TestMethod]
              public   void   Can_Remove_Line()
            {
                  //   Arrange - create some test products 
                Product p1 =  new  Product { ProductID =  1 , Name =  "  P1  "   };
                Product p2  =  new  Product { ProductID =  2 , Name =  "  P2  "   };
                Product p3  =  new  Product { ProductID =  3 , Name =  "  P3  "   };
                  //   Arrange - create a new cart 
                Cart target =  new   Cart();
                  //   Arrange - add some products to the cart 
                target.AddItem(p1,  1  );
                target.AddItem(p2,   3  );
                target.AddItem(p3,   5  );
                target.AddItem(p2,   1  );
                  //   Act 
                 target.RemoveLine(p2);
                  //   Assert 
                Assert.AreEqual(target.Lines.Where(c => c.Product == p2).Count(),  0  );
                Assert.AreEqual(target.Lines.Count(),   2  );
            } 

我们还要测试一下对商品总额的计算是否正确,说干就干,添加如下测试方法:

             [TestMethod]
              public   void   Calculate_Cart_Total() {
                  //   Arrange - create some test products 
                Product p1 =  new  Product { ProductID =  1 , Name =  "  P1  " , Price =  100M};
                Product p2  =  new  Product { ProductID =  2 , Name =  "  P2  "  , Price =  50M};
                  //   Arrange - create a new cart 
                Cart target =  new   Cart();
                  //   Act 
                target.AddItem(p1,  1  );
                target.AddItem(p2,   1  );
                target.AddItem(p1,   3  );
                  decimal  result =  target.ComputeTotalValue();

                  //   Assert 
                 Assert.AreEqual(result, 450M);
            } 

我们还要测试一下,当我们重置购物车的时候,购物车里的内容是否正确:

             [TestMethod]
              public   void   Can_Clear_Contents()
            {
                  //   Arrange - create some test products 
                Product p1 =  new  Product { ProductID =  1 , Name =  "  P1  " , Price =  100M };
                Product p2  =  new  Product { ProductID =  2 , Name =  "  P2  " , Price =  50M };
                  //   Arrange - create a new cart 
                Cart target =  new   Cart();
                  //   Arrange - add some items 
                target.AddItem(p1,  1  );
                target.AddItem(p2,   1  );
                  //   Act - reset the cart 
                 target.Clear();
                  //   Assert 
                Assert.AreEqual(target.Lines.Count(),  0  );
            } 

好了,现在我们得为添加购物车设置一个触发点了,那就是为商品加上“Add to cart” 按钮,编辑Views/Shared/ProductSummary.cshtml 文件,为它添加一个按钮。

 @model SportsStore.Domain.Entities.Product

 <div  class = "  item  " >
    <h3>@Model.Name</h3> 
            @Model.Description

            @using(Html.BeginForm(  "  AddToCart  " ,  "  Cart  "  )) {
                @Html.HiddenFor(x  =>  x.ProductID)
                @Html.Hidden(  "  returnUrl  "  , Request.Url.PathAndQuery)
                <input type= "  submit  "  value= "  + Add to cart  "  /> 
            }
     <h4>@Model.Price.ToString( "  c  " )</h4>
</div>

我们添加了一个Razor语句块,为每个产品链接创建一个小HTML,当我们提交时,它将调用Cart控制器的AddToCart action 方法,现在我们就去看看Cart控制器吧!哦,差点忘了,我们还得为我们这个BeginForm生成的表单添加一个css,不然它实在太丑了,打开你的Site.css文件,添加如下代码:

 FORM  {  margin :  0 ;  padding :  0 ; } 
DIV.item FORM  {  float : right ; } 
DIV.item INPUT  { 
color : White ;  background-color :  #333 ;  border :  1px solid black ;  cursor : pointer ;
}

在每个产品相上使用Html.BeginForm helper意味着每个“Add to cart”按钮都将被渲染在自己分离的Html表单元素中,这可能让你惊讶,ASP.NET MVC每一页的forms没有数量限制吗?的确是这样,你可以有尽可能多的表单,只要你需要绝对没问题。这不是什么技术需求,然而,我们的表单都提交给同一个控制器,并且带有不同的参数,这使得按钮的处理非常简单。


实现Cart控制器

右击控制器文件夹,添加一个名为iCartController的控制器:

 using   SportsStore.Domain.Abstract;
  using   SportsStore.Domain.Entities;
  using   System;
  using   System.Collections.Generic;
  using   System.Linq;
  using   System.Web;
  using   System.Web.Mvc;
  namespace   SportsStore.WebUI.Controllers
{
      public   class   CartController : Controller
    {

          private   IProductsRepository repository;

          public   CartController(IProductsRepository repo)
        {
            repository  =  repo;
        }

          public  RedirectToRouteResult AddToCart( int  productId,  string   returnUrl)
        {
            Product product  =  repository.Products
            .FirstOrDefault(p  => p.ProductID ==  productId);
              if  (product !=  null  )
            {
                GetCart().AddItem(product,   1  );
            }
              return  RedirectToAction( "  Index  " ,  new   { returnUrl });
        }

          public  RedirectToRouteResult RemoveFromCart( int  productId,  string   returnUrl)
        {
            Product product  =  repository.Products
            .FirstOrDefault(p  => p.ProductID ==  productId);
              if  (product !=  null  )
            {
                GetCart().RemoveLine(product);
            }
              return  RedirectToAction( "  Index  " ,  new   { returnUrl });
        }

          private   Cart GetCart()
        {
            Cart cart  = (Cart)Session[ "  Cart  "  ];
              if  (cart ==  null  )
            {
                cart  =  new   Cart();
                Session[  "  Cart  " ] =  cart;
            }
              return   cart;
        }
    }
} 

在这个控制器中有几点需要注意:首先,我们使用了ASP.NET session state特性去存取Cart对象,这也是GetCart方法的用意所在。 ASP.NET有很好的session特性,它能使用cookies 或 URL rewriting与来自用户的请求结合在一起,去形成一个单一的浏览session。一个相关的特性是session state, 它允许我们使用session去整合数据。

我们还需要传递信息到View,当用户点击了继续购物按钮时,购物车对象和链接地址需要显示出来,我们想在Model文件夹创建一个简单的Viewmodel,命名为CartIndexViewModel:

 using   SportsStore.Domain.Entities;
  namespace   SportsStore.WebUI.Models
{
      public   class   CartIndexViewModel
    {
          public  Cart Cart {  get ;  set  ; }
          public   string  ReturnUrl {  get ;  set  ; }
    }
} 

现在我们需要在Cart控制器中实现Index方法:

 using   SportsStore.Domain.Abstract;
  using   SportsStore.Domain.Entities;
  using   System;
  using   System.Collections.Generic;
  using   System.Linq;
  using   System.Web;
  using   System.Web.Mvc;
  using   SportsStore.WebUI.Models;

  namespace   SportsStore.WebUI.Controllers
{
      public   class   CartController : Controller
    {

          private   IProductsRepository repository;

          public   CartController(IProductsRepository repo)
        {
            repository  =  repo;
        }

          public  ViewResult Index( string   returnUrl)
        {
              return  View( new   CartIndexViewModel
            {
                Cart  =  GetCart(),
                ReturnUrl  =  returnUrl
            });
        }

          public  RedirectToRouteResult AddToCart( int  productId,  string   returnUrl)
        {
            Product product  =  repository.Products
            .FirstOrDefault(p  => p.ProductID ==  productId);
              if  (product !=  null  )
            {
                GetCart().AddItem(product,   1  );
            }
              return  RedirectToAction( "  Index  " ,  new   { returnUrl });
        }

          public  RedirectToRouteResult RemoveFromCart( int  productId,  string   returnUrl)
        {
            Product product  =  repository.Products
            .FirstOrDefault(p  => p.ProductID ==  productId);
              if  (product !=  null  )
            {
                GetCart().RemoveLine(product);
            }
              return  RedirectToAction( "  Index  " ,  new   { returnUrl });
        }

          private   Cart GetCart()
        {
            Cart cart  = (Cart)Session[ "  Cart  "  ];
              if  (cart ==  null  )
            {
                cart  =  new   Cart();
                Session[  "  Cart  " ] =  cart;
            }
              return   cart;
        }
    }
} 

最后一步,我们要创建一个强类型的Index View,右击Index方法,选择添加视图:

修改代码如下:

 @model SportsStore.WebUI.Models.CartIndexViewModel
@{
        ViewBag.Title  =  "  Sports Store: 你的购物车  "  ;
}
 <h2>你的购物车</h2>
<table width= "  90%  "  align= "  center  " >
<thead><tr>
<th align= "  center  " >Quantity</th>
<th align= "  left  " >Item</th>
<th align= "  right  " >Price</th>
<th align= "  right  " >Subtotal</th>
</tr></thead>
<tbody> 
@foreach(  var  line  in   Model.Cart.Lines) {
    <tr>
    <td align= "  center  " >@line.Quantity</td>
    <td align= "  left  " >@line.Product.Name</td>
    <td align= "  right  " >@line.Product.Price.ToString( "  c  " )</td>
    <td align= "  right  " >@((line.Quantity * line.Product.Price).ToString( "  c  " ))</td>
   </tr> 
}
 </tbody>
<tfoot>
    <tr>
    <td colspan= "  3  "  align= "  right  " >Total:</td>
    <td align= "  right  " > 
    @Model.Cart.ComputeTotalValue().ToString(  "  c  "  )
     </td>
    </tr>
</tfoot>
</table>
<p align= "  center  "   class = "  actionButtons  " >
    <a href= "  @Model.ReturnUrl  " >Continue shopping</a>
</p>

添加样式单代码:

 H2  {  margin-top :  0.3em  } 
TFOOT TD  {  border-top :  1px dotted gray ;  font-weight :  bold ; } 
.actionButtons A, INPUT.actionButtons  { 
font :  .8em Arial ;  color :  White ;  margin :  .5em ; 
text-decoration :  none ;  padding :  .15em 1.5em .2em 1.5em ; 
background-color :  #353535 ;  border :  1px solid black ;
}

运行程序并选择产品添加到购物车,你将看到如下画面:

现在我们已经有了一个基本的购物功能,但它还有很多问题,在下一篇中,我们将进一步改进并完善我们的购物车,如果你已经想到了更好的完善方案,在下一篇中,我们可以对比一下,看看我们想的是否一致!请继续关注我的续篇!

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于构建一个真实的应用电子商务SportsStore(七)的详细内容...

  阅读:43次