隨着我們購物車的不斷完善,我們簡單的完成到最后的訂單模塊。我們需要一個擴展的模型,在我們的域模型類庫里,添加一個類(ShippingDetail)類,它的具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.DataAnnotations; namespace SportsStore.Domain.Entities { public class ShippingDetails { [Required(ErrorMessage="Please enter a name")] public string Name { get; set; } [Required(ErrorMessage="Please enter the first Address")] public string Address1 { get; set; } public string Address2 { get; set; } public string Address3 { get; set; } [Required(ErrorMessage="Please enter a city name")] public string City { get; set; } [Required(ErrorMessage="Please enter a state name")] public string State { get; set; } public string Zip { get; set; } [Required(ErrorMessage="Please enter a country name")] public string Country { get; set; } public bool GiftWrap { get; set; } } }
接下來需要完善我們結算功能,我們需要添加一個結算按鈕,在我們Web項目的Views/Cart/Index.cshtml修改如下(實際就添加了一個按鈕):
@model SportsStore.WebUI.Models.CartIndexViewModel @{ ViewBag.Title = "Cart Index"; } <h2>Your Cart</h2> <table width="90%" align="center"> <thead> <tr> <th align="center">Qunantity</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> <td> @using (Html.BeginForm("RemoveFromCart","Cart")) { @Html.Hidden("ProductId",line.Product.ProductID) @Html.HiddenFor(h=>h.ReturnUrl) <input class="actionButtons" type="submit" value="Remove" /> } </td> </tr> } </tbody> <tfoot> <tr> <td colspan="3">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> @Html.ActionLink("Checkout now", "Checkout") </p>
上面紅色部分的代碼會呈現出一個帶有支付連接的按鈕,運行我們的項目如下圖1.
圖1.
接着我們需要建立一個視圖(Checkout),在Cartcontroller控制器里添加一個返回視圖(Checkout)的Action(方法),具體代碼如下:
public ViewResult Checkout() { return this.View(new ShippingDetails()); }
添加完Action(方法)后,然后就需要添加視圖(Checkout),右鍵選擇添加視圖,如下圖2.
圖2.這里還是選擇強類型視圖,因我我們需要使用之前我們定義的ShippingDeatails類。創建好視圖(Checkout)后,修改他的內容如下:
@model SportsStore.Domain.Entities.ShippingDetails @{ ViewBag.Title = "SportStore:Checkout"; } <h2>Check out now</h2> Please enter your details,and we'll ship your goods right away! @using (Html.BeginForm()) { @Html.ValidationSummary() <h3>Ship to</h3> <div>Name: @Html.EditorFor(h=>h.Name)</div> <h3>Address</h3> <div>Address1:@Html.EditorFor(h=>h.Address1)</div> <div>Address2:@Html.EditorFor(h=>h.Address2)</div> <div>Address3:@Html.EditorFor(h=>h.Address3)</div> <div>City:@Html.EditorFor(h=>h.City)</div> <div>State:@Html.EditorFor(h=>h.State)</div> <div>Zip:@Html.EditorFor(h=>h.Zip)</div> <div>Country:@Html.EditorFor(h=>h.Country)</div> <h3>Options</h3> <label> @Html.EditorFor(h=>h.GiftWrap) GIft wrap these items </label> <p align="center"> <input class="actionButtons" type="submit" value="Complete order" /> </p> }
搞完視圖運行我們的Web項目,結果如下圖3
圖3.使用Html.EditorFor輔助方法為每一個表單字段呈現input元素,我們讓MVC框架能夠算出view model屬性需要哪一種input元素,而不是顯示的指定。
接下我們實現訂單處理器我們需要一個組件來處理訂單的詳情,為了保持MVC模型的嚴則,首先定義一個接口,並實現該接口。然后使用我們的DI容器--Ninject,在我們的Domain模型域類庫項目里的Abstract文件夾里面定義一個接口(IOrderProcessor),具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract { public interface IOrderProcessor { void ProcessOrder(Cart cart, ShippingDetails shippingDetails); } }
然后我們需要實現IOrderProcessor接口去處理客戶提交的訂單,如果一切OK的話,我們需要給用戶發一份Email郵件告知用戶。當然這里已經把購物流程簡化的不像樣子了,真正的流程這里應該是和銀行(第三方)交互,等待支付成功后需要發郵件給用戶,這里就簡單的實現下。在我們的SportsStore.Domain類庫下的Concrete文件夾下創建EmailOrderProcessor類,在這個類里使用.Net內置的SMTP實現發送電子郵件,具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Mail; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Concrete { public class EmailSettings { public string MailToAddress = "**************"; //自己試着配置 public string MailFromAddress = "*********@qq.com"; //自己試着配置 public bool UseSsl = false; public string Username = "********@qq.com"; //自己試着配置 public string Password = "*************"; //自己試着配置 用QQ的話這里要配置密碼 public string ServerName = "Smtp.qq.com"; //pop.qq.com Smtp.qq.com mail.qq.com public int ServerPort = 25; public bool WriteAsFile = false; public string FileLocation = @"E:\work\SportsStore\sports_store_emails"; } public class EmailOrderProcessor : IOrderProcessor { private EmailSettings emailSetings; public EmailOrderProcessor(EmailSettings settings) { this.emailSetings = settings; } public void ProcessOrder(Cart cart, ShippingDetails shippingInfo) { using (var smtpClient = new SmtpClient()) { smtpClient.EnableSsl = this.emailSetings.UseSsl; smtpClient.Host = this.emailSetings.ServerName; smtpClient.Port = this.emailSetings.ServerPort; smtpClient.UseDefaultCredentials = false; smtpClient.Credentials = new NetworkCredential(this.emailSetings.Username, this.emailSetings.Password); if (this.emailSetings.WriteAsFile) { smtpClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory; smtpClient.PickupDirectoryLocation = this.emailSetings.FileLocation; smtpClient.EnableSsl = false; } StringBuilder body = new StringBuilder().AppendLine("A New Order Has Been Submitted") .AppendLine("---") .AppendLine("Items:"); foreach (var item in cart.Lines) { var subtotal = item.Product.Price * item.Quantity; body.AppendFormat("{0} × {1}(Subtotal:{2:c})", item.Quantity, item.Product.Name, subtotal); } body.AppendFormat("Total order Value:{0:c}", cart.ComputeTotalValue()) .AppendLine("---") .AppendLine("Ship to:") .AppendLine(shippingInfo.Name) .AppendLine(shippingInfo.Address1) .AppendLine(shippingInfo.Address2 ?? "") .AppendLine(shippingInfo.Address3 ?? "") .AppendLine(shippingInfo.City) .AppendLine(shippingInfo.State ?? "") .AppendLine(shippingInfo.Country) .AppendLine(shippingInfo.Zip) .AppendLine("---") .AppendFormat("GIft wrap:{0}", shippingInfo.GiftWrap ? "Yes" : "No"); MailMessage mailMessage = new MailMessage( this.emailSetings.MailFromAddress, //From this.emailSetings.MailToAddress, //To "New Order Submitted!", //Subject body.ToString()); if (this.emailSetings.WriteAsFile) { mailMessage.BodyEncoding = Encoding.ASCII; } smtpClient.Send(mailMessage); } } } }
現在,我們已經實現IOrderProcessor接口,我們可以使用Ninject創建實例方法配置它。在我們之前Web項目的Infrastructure文件夾下的NinjectControllerFactory類,具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Ninject; using System.Web.Routing; using Moq; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.Domain.Concrete; using System.Configuration; namespace SportsStore.WebUI.Infrastructure { public class NinjectControllerFactory : DefaultControllerFactory { private IKernel ninjectKernel; public NinjectControllerFactory() { this.ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType); } private void AddBindings() { //綁定額外數據 this.ninjectKernel.Bind<IProductRepository>().To<EFProductRepository>(); EmailSettings emailSettings = new EmailSettings { WriteAsFile = bool.Parse(ConfigurationManager.AppSettings["Email.WriteAsFile"] ?? "false") }; this.ninjectKernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>() .WithConstructorArgument("settings", emailSettings); } } }
這里的Email.WriteAsFile在配置文件里面配置的,主要是考慮是在沒有smtp服務器的情況下,將郵件復制到指定目錄。其實一般的郵箱都開通了smtp服務的,所以我們將這里的默認值設為false。在Web.config里面配置<add key="Email.WriteAsFile" value="false"/>,具體如下:Web項目的Views文件下Web.config如下:
<appSettings> <add key="webpages:Enabled" value="false" /> <add key="Email.WriteAsFile" value="true" /> </appSettings>
我們現在需要完善我們Cartcontroller,我們需要一個構造函數在用戶單擊支付按鈕的時候,他需要實現IOrderProcessor接口,然后發送郵件,具體修改CartController控制器代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; private IOrderProcessor orderProcessor; public CartController(IProductRepository repo,IOrderProcessor proc) { this.repository = repo; this.orderProcessor = proc; } //添加購物車 public RedirectToRouteResult AddToCart(Cart cart, int productId, string returnUrl) { Product product = this.repository.Products.FirstOrDefault(h => h.ProductID == productId); if (product != null) { cart.AddItem(product, 1); } return this.RedirectToAction("Index", new { returnUrl }); } //移除商品 public RedirectToRouteResult RemoveFromCart(Cart cart, int productId, string returnUrl) { Product product = this.repository.Products.FirstOrDefault(h => h.ProductID == productId); if (product != null) { cart.RemoveLine(product); } return this.RedirectToAction("Index", new { cart = cart, returnUrl = returnUrl }); } private Cart GetCart() { Cart cart = (Cart)Session["Cart"]; if (cart == null) { cart = new Cart(); this.Session["Cart"] = cart; } return cart; } public ViewResult Index(Cart cart, string returnUrl) { return this.View(new CartIndexViewModel { Cart = cart, ReturnUrl = returnUrl }); } //簡易的購物車總結 public ViewResult Summary(Cart cart) { return this.View(cart); } //結算的方法 [HttpPost] public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails) { if (cart.Lines.Count() == 0) { ModelState.AddModelError("", "Sorry,your cart is empty!"); } if (ModelState.IsValid) { this.orderProcessor.ProcessOrder(cart, shippingDetails); cart.Clear(); return this.View("Completed"); } else { return this.View(shippingDetails); } } public ViewResult Checkout() { return this.View(new ShippingDetails()); } } }
然后我們需要添加一個視圖頁面"Completed",他表示我們已經支付成功,返回友好的信息,具體如下圖。
這里我們不要想在選擇強類型視圖,因為我們使用它值呈現一些簡單的東西,所以我們還要使用模版(_Layout)我希望他們的風格還是一樣的,具體代碼如下:
@{ ViewBag.Title = "SportsStore:Order Completed"; } <h2>Thanks!</h2> Thanks for placing you order.We'll ship your goods as soon as possible
添加完成后運行我們的項目如下圖4(展示我們驗證不通過)-圖5(購物成功)。
圖4.
圖5.
項目就簡單的搞到這里,我們需要一個簡單后續在補上一個簡單的后台(簡單項目肯定需要一個簡單的后台)來管理我們項目。幾天我們簡單的購物流程就到這里,文章寫的倉促,要是有那些地方有描述錯誤還是寫錯的地方,還請路過的前輩們多多指導批評,大家同工學習。鑒於好多朋友需要MVC基礎的資料等,可以給我Email,等我整理好了一並發給大家,或者嘗試用一片文章專門來放這些資料到時大家可以任意下載共同學習。