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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值