Asp.Net MVC3.0【MVC项目实战の五】

来源:互联网 发布:淘宝网店发货流程 编辑:程序博客网 时间:2024/06/05 18:26

随着我们购物车的不断完善,我们简单的完成到最后的订单模块。我们需要一个扩展的模型,在我们的域模型类库里,添加一个类(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.

项目就简单的搞到这里,我们需要一个简单后续在补上一个简单的后台(简单项目肯定需要一个简单的后台)来管理我们项目。几天我们简单的购物流程就到这里