ASP.NET MVC SportStore 购物网示例(6)

定义一个订单提供 IoC 组件

在DomainModel项目中新建文件夹Services添加以下接口:

namespace DomainModel.Services

{

public interface IOrderSubmitter

{

void SubmitOrder(Cart cart);

}

}

修改CartController添加IOrderSubmitter接口。

private IOrderSubmitter orderSubmitter;

public CartController(IProductsRepository productsRepository, IOrderSubmitter orderSubmitter)

{

this.productsRepository = productsRepository;

this.orderSubmitter = orderSubmitter;

}

修改测试中的代码,添加新的测试。

[Test]
 
 
public void
 
 
Submitting_Order_With_No_Lines_Displays_Default_View_With_Error()
 
 
{
 
 
// Arrange
 
 
CartController controller = new CartController(null, null);
 
 
Cart cart = new Cart();
 
 
// Act
 
 
var result = controller.CheckOut(cart, new FormCollection());
 
 
// Assert
 
 
Assert.IsEmpty(result.ViewName);
 
 
Assert.IsFalse(result.ViewData.ModelState.IsValid);
 
 
}
 
 
[Test]
 
 
public void
 
 
Submitting_Empty_Shipping_Details_Displays_Default_View_With_Error()
 
 
{
 
 
// Arrange
 
 
CartController controller = new CartController(null, null);
 
 
Cart cart = new Cart();
 
 
cart.AddItem(new Product(), 1);
 
 
// Act
 
 
var result = controller.CheckOut(cart, new FormCollection {
 
 
{ "Name", "" }
 
 
});
 
 
// Assert
 
 
Assert.IsEmpty(result.ViewName);
 
 
Assert.IsFalse(result.ViewData.ModelState.IsValid);
 
 
}
 
 
[Test]
 
 
public void
 
 
Valid_Order_Goes_To_Submitter_And_Displays_Completed_View()
 
 
{
 
 
// Arrange
 
 
var mockSubmitter = new Moq.Mock<IOrderSubmitter>();
 
 
CartController controller = new CartController(null, mockSubmitter.Object);
 
 
Cart cart = new Cart();
 
 
cart.AddItem(new Product(), 1);
 
 
var formData = new FormCollection {
 
 
{ "Name", "Steve" }, { "Line1", "123 My Street" },
 
 
{ "Line2", "MyArea" }, { "Line3", "" },
 
 
{ "City", "MyCity" }, { "State", "Some State" },
 
 
{ "Zip", "123ABCDEF" }, { "Country", "Far far away" },
 
 
{ "GiftWrap", bool.TrueString }
 
 
};
 
 
// Act
 
 
var result = controller.CheckOut(cart, formData);
 
 
// Assert
 
 
Assert.AreEqual("Completed", result.ViewName);
 
 
mockSubmitter.Verify(x => x.SubmitOrder(cart));
 
 
Assert.AreEqual(0, cart.Lines.Count);
 
 
}

在CartController中添加POST方法的CheckOut。

[AcceptVerbs(HttpVerbs.Post)]
 
 
public ViewResult CheckOut(Cart cart, FormCollection form)
 
 
{
 
 
// Empty carts can't be checked out
 
 
if (cart.Lines.Count == 0)
 
 
{
 
 
ModelState.AddModelError("Cart", "Sorry, your cart is empty!");
 
 
return View();
 
 
}
 
 
// Invoke model binding manually
 
 
if (TryUpdateModel(cart.ShippingDetails, form.ToValueProvider()))
 
 
{
 
 
orderSubmitter.SubmitOrder(cart);
 
 
cart.Clear();
 
 
return View("Completed");
 
 
}
 
 
else // Something was invalid
 
 
return View();
 
 
}

添加一个模拟提交

namespace DomainModel.Services
 
 
{
 
 
public class FakeOrderSubmitter : IOrderSubmitter
 
 
{
 
 
public void SubmitOrder(DomainModel.Entities.Cart cart)
 
 
{
 
 
}
 
 
}
 
 
}

修改Web.config文件。

<component id="OrderSubmitter"
 
 
service="DomainModel.Services.IOrderSubmitter, DomainModel"
 
 
type="DomainModel.Services.FakeOrderSubmitter, DomainModel" />

添加样式

.field-validation-error { color: red; }
 
 
.input-validation-error { border: 1px solid red; background-color: #ffeeee; }
 
 
.validation-summary-errors { font-weight: bold; color: red; }

F5运行测试。

添加“Thanks for you roder”View

右键单击Cart文件夹,添加Complted视图。

clip_image001

使用 EmailOrderSumbitter来替换 FakeOrderSubmitter

在DomainModel项目中添加 EmailOrderSubmitter到Services文件夹。

public class EmailOrderSubmitter : IOrderSubmitter
 
 
{
 
 
const string MailSubject = "New order submitted!";
 
 
string smtpServer, mailFrom, mailTo;
 
 
public EmailOrderSubmitter(string smtpServer, string mailFrom, string mailTo)
 
 
{
 
 
// Receive parameters from IoC container
 
 
this.smtpServer = smtpServer;
 
 
this.mailFrom = mailFrom;
 
 
this.mailTo = mailTo;
 
 
}
 
 
public void SubmitOrder(Cart cart)
 
 
{
 
 
// Prepare the message body
 
 
StringBuilder body = new StringBuilder();
 
 
body.AppendLine("A new order has been submitted");
 
 
body.AppendLine("---");
 
 
body.AppendLine("Items:");
 
 
foreach (var line in cart.Lines)
 
 
{
 
 
var subtotal = line.Product.Price * line.Quantity;
 
 
body.AppendFormat("{0} x {1} (subtotal: {2:c}", line.Quantity,
 
 
line.Product.Name,
 
 
subtotal);
 
 
}
 
 
body.AppendFormat("Total order value: {0:c}", cart.ComputeTotalValue());
 
 
body.AppendLine("---");
 
 
body.AppendLine("Ship to:");
 
 
body.AppendLine(cart.ShippingDetails.Name);
 
 
body.AppendLine(cart.ShippingDetails.Line1);
 
 
body.AppendLine(cart.ShippingDetails.Line2 ?? "");
 
 
body.AppendLine(cart.ShippingDetails.Line3 ?? "");
 
 
body.AppendLine(cart.ShippingDetails.City);
 
 
body.AppendLine(cart.ShippingDetails.State ?? "");
 
 
body.AppendLine(cart.ShippingDetails.Country);
 
 
body.AppendLine(cart.ShippingDetails.Zip);
 
 
body.AppendLine("---");
 
 
body.AppendFormat("Gift wrap: {0}",
 
 
cart.ShippingDetails.GiftWrap ? "Yes" : "No");
 
 
// Dispatch the email
 
 
SmtpClient smtpClient = new SmtpClient(smtpServer);
 
 
smtpClient.Send(new MailMessage(mailFrom, mailTo, MailSubject,body.ToString()));
 
 
}
 
 
}

更新web.config文件

<component id="OrderSubmitter"
 
 
service="DomainModel.Services.IOrderSubmitter, DomainModel"
 
 
type="DomainModel.Services.EmailOrderSubmitter, DomainModel" >
 
 
<parameters>
 
 
<smtpServer>127.0.0.1</smtpServer>
 
 
<!-- Your server here -->
 
 
<mailFrom>sportsstore@example.com</mailFrom>
 
 
<mailTo>admin@example.com</mailTo>
 
 
</parameters>
 
 
</component>

信用卡的处理 Exercise:Credit Card Processing

If you’re feeling ready for a challenge, try this. Most e-commerce sites involve credit card processing, but almost

every implementation is different. The API varies according to which payment processing gateway you sign up

with. So, given this abstract service:

public interface ICreditCardProcessor
 
 
{
 
 
TransactionResult TakePayment(CreditCard card, decimal amount);
 
 
}
 
 
public class CreditCard
 
 
{
 
 
public string CardNumber { get; set; }
 
 
public string CardholderName { get; set; }
 
 
public string ExpiryDate { get; set; }
 
 
public string SecurityCode { get; set; }
 
 
}
 
 
public enum TransactionResult
 
 
{
 
 
Success, CardNumberInvalid, CardExpired, TransactionDeclined
 
 
}

can you enhance CartController to work with it? This will involve several steps:

• Updating CartController’s constructor to receive an ICreditCardProcessor instance.

• Updating /Views/Cart/CheckOut.aspx to prompt the customer for card details.

• Updating CartController’s POST-handling CheckOut action to send those card details to the

ICreditCardProcessor. If the transaction fails, you’ll need to display a suitable message and not

submit the order to IOrderSubmitter.

This underlines the strengths of component-oriented architecture and IoC. You can design, implement, and validate

CartController’s credit card–processing behavior with unit tests, without having to open a web browser and

without needing any concrete implementation of ICreditCardProcessor (just set up a mock instance). When

you want to run it in a browser, implement some kind of FakeCreditCardProcessor and attach it to your IoC

container using web.config. If you’re inclined, you can create one or more implementations that wrap real-world

credit card processor APIs, and switch between them just by editing your web.config file.

SportsStore管理员和最终增强

添加,删除,修改,查询操作

Form认证

文件上传

显示sql数据库中的图片

添加分类管理

创建AdminController,选中创建Create,update,insert select。自动生成代码:

public class AdminController : Controller
 
 
{
 
 
//
 
 
// GET: /Admin/
 
 
public ActionResult Index()
 
 
{
 
 
return View();
 
 
}
 
 
//
 
 
// GET: /Admin/Details/5
 
 
public ActionResult Details(int id)
 
 
{
 
 
return View();
 
 
}
 
 
//
 
 
// GET: /Admin/Create
 
 
public ActionResult Create()
 
 
{
 
 
return View();
 
 
} 
 
 
//
 
 
// POST: /Admin/Create
 
 
[HttpPost]
 
 
public ActionResult Create(FormCollection collection)
 
 
{
 
 
try
 
 
{
 
 
// TODO: Add insert logic here
 
 
return RedirectToAction("Index");
 
 
}
 
 
catch
 
 
{
 
 
return View();
 
 
}
 
 
}
 
 
//
 
 
// GET: /Admin/Edit/5
 
 
public ActionResult Edit(int id)
 
 
{
 
 
return View();
 
 
}
 
 
//
 
 
// POST: /Admin/Edit/5
 
 
[HttpPost]
 
 
public ActionResult Edit(int id, FormCollection collection)
 
 
{
 
 
try
 
 
{
 
 
// TODO: Add update logic here
 
 
return RedirectToAction("Index");
 
 
}
 
 
catch
 
 
{
 
 
return View();
 
 
}
 
 
}
 
 
}
 
 
添加构造函数:
 
 
private IProductsRepository productsRepository;
 
 
public AdminController(IProductsRepository productsRepository)
 
 
{
 
 
this.productsRepository = productsRepository;
 
 
}
 
 
[TestFixture]
 
 
class AdminControllerTests
 
 
{// Will share this same repository across all the AdminControllerTests
 
 
private Moq.Mock<IProductsRepository> mockRepos;
 
 
// This method gets called before each test is run
 
 
[SetUp]
 
 
public void SetUp()
 
 
{
 
 
// Make a new mock repository with 50 products
 
 
List<Product> allProducts = new List<Product>();
 
 
for (int i = 1; i <= 50; i++)
 
 
allProducts.Add(new Product { ProductID = i, Name = "Product " + i });
 
 
mockRepos = new Moq.Mock<IProductsRepository>();
 
 
mockRepos.Setup(x => x.Products)
 
 
.Returns(allProducts.AsQueryable());
 
 
}
 
 
[Test]
 
 
public void Index_Action_Lists_All_Products()
 
 
{
 
 
// Arrange
 
 
AdminController controller = new AdminController(mockRepos.Object);
 
 
// Act
 
 
ViewResult results = controller.Index();
 
 
// Assert: Renders default view
 
 
Assert.IsEmpty(results.ViewName);
 
 
// Assert: Check that all the products are included
 
 
var prodsRendered = (List<Product>)results.ViewData.Model;
 
 
Assert.AreEqual(50, prodsRendered.Count);
 
 
for (int i = 0; i < 50; i++)
 
 
Assert.AreEqual("Product " + (i + 1), prodsRendered[i].Name);
 
 
}
 
 
}
 
 
Rendering a Grid of Products in the Repository
 
 
为AdminController修改Index()
 
 
public ViewResult Index()
 
 
{
 
 
return View(productsRepository.Products.ToList());
 
 
}

测试通过。

声明一个ListView 模板。在Share文件夹,右键单击添加新项:

clip_image003

添加新的样式表:adminstyle.css

BODY, TD { font-family: Segoe UI, Verdana }
; padding-top: 0; font-weight: bold;
 
 
H1 { padding: .5em
 
 
font-size: 1.5em; border-bottom: 2px solid gray; }
 
 
DIV#content { padding: .9em; }
 
 
TABLE.Grid TD, TABLE.Grid TH { border-bottom: 1px dotted gray; text-align:left; }
 
 
TABLE.Grid { border-collapse: collapse; width:100%; }
 
 
TABLE.Grid TH.NumericCol, Table.Grid TD.NumericCol {
 
 
text-align: right; padding-right: 1em; }
 
 
DIV.Message { background: gray; color:White; padding: .2em; margin-top:.25em; }
 
 
.field-validation-error { color: red; }
 
 
.input-validation-error { border: 1px solid red; background-color: #ffeeee; }
 
 
.validation-summary-errors { font-weight: bold; color: red; }

现在可以为AdminControllerr的Index()添加新的View:

clip_image004

添加必要的css

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Admin.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<DomainModel.Entities.Product>>" %>
 
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 
 
All Products
 
 
</asp:Content>
 
 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 
 
<h2>All Products</h2>
 
 
<table>
 
 
<tr>
 
 
<th>
 
 
ID
 
 
</th>
 
 
<th>
 
 
Name
 
 
</th>
 
 
<th class="NumericCol">
 
 
Price
 
 
</th>
 
 
<th>
 
 
Action
 
 
</th>
 
 
</tr>
 
 
<% foreach (var item in Model) { %>
 
 
<tr>
 
 
<td>
 
 
<%= Html.Encode(item.ProductID) %>
 
 
</td>
 
 
<td>
 
 
<%= Html.Encode(item.Name) %>
 
 
</td>
 
 
<td class="NumericCol">
 
 
<%= Html.Encode(String.Format("{0:F}", item.Price)) %>
 
 
</td>
 
 
<td>
 
 
<%= Html.ActionLink("Edit", "Edit", new { id=item.ProductID }) %> |
 
 
<%= Html.ActionLink("Details", "Details", new { id=item.ProductID })%>
 
 
</td>
 
 
</tr>
 
 
<% } %>
 
 
</table>
 
 
<p>
 
 
<%= Html.ActionLink("Add a new product", "Create") %>
 
 
</p>
 
 
</asp:Content>
 
 
> 

访问测试如下:

clip_image006

创建一个产品编辑

创建测试:

[Test]
 
 
public void Edit_Product()
 
 
{
 
 
// Arrange
 
 
AdminController controller = new AdminController(mockRepos.Object);
 
 
// Act
 
 
ViewResult result = controller.Edit(17);
 
 
Product renderedProduct = (Product)result.ViewData.Model;
 
 
Assert.AreEqual(17, renderedProduct.ProductID);
 
 
Assert.AreEqual("Product 17", renderedProduct.Name);
 
 
}
 
 
为AdminController添加GET Edit方法
 
 
public ViewResult Edit(int id)
 
 
{
 
 
Product product = (from p in productsRepository.Products
 
 
where p.ProductID == id
 
 
select p).First();
 
 
return View(product);
 
 
}

为Edit添加View。

clip_image007

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Admin.Master" Inherits="System.Web.Mvc.ViewPage<DomainModel.Entities.Product>" %>
 
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 
 
Admin : Edit <%=Model.Name %>
 
 
</asp:Content>
 
 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 
 
<h2> Edit <%=Model.Name %></h2>
 
 
<% using (Html.BeginForm()) {%>
 
 
<fieldset>
 
 
<legend>Fields</legend>
 
 
<%=Html.Hidden("ProductID") %>
 
 
<p>
 
 
<%= Html.LabelFor(model => model.Name) %>
 
 
<%= Html.TextBoxFor(model => model.Name) %>
 
 
<%= Html.ValidationMessageFor(model => model.Name) %>
 
 
</p>
 
 
<p>
 
 
<%= Html.LabelFor(model => model.Description) %>
 
 
<%= Html.TextBoxFor(model => model.Description) %>
 
 
<%= Html.ValidationMessageFor(model => model.Description) %>
 
 
</p>
 
 
<p>
 
 
<%= Html.LabelFor(model => model.Price) %>
 
 
<%= Html.TextBoxFor(model => model.Price, String.Format("{0:F}", Model.Price)) %>
 
 
<%= Html.ValidationMessageFor(model => model.Price) %>
 
 
</p>
 
 
<p>
 
 
<%= Html.LabelFor(model => model.Category) %>
 
 
<%= Html.TextBoxFor(model => model.Category) %>
 
 
<%= Html.ValidationMessageFor(model => model.Category) %>
 
 
</p>
 
 
<p>
 
 
<input type="submit" value="Save" />
 
 
</p>
 
 
</fieldset>
 
 
<% } %>
 
 
<div>
 
 
<%=Html.ActionLink("Back to List", "Index") %>
 
 
</div>
 
 
</asp:Content>

处理Edit的提交。

创建测试:

[Test]
 
 
public void Edit_Sumbitting_Product_And_Redirects_To_Index()
 
 
{
 
 
// Arrange
 
 
AdminController controller = new AdminController(mockRepos.Object);
 
 
Product newProduct = new Product();
 
 
// Act
 
 
var result = (RedirectToRouteResult)controller.Edit(newProduct);
 
 
// Assert: Saved product to repository and redirected
 
 
mockRepos.Verify(x => x.SaveProduct(newProduct));
 
 
Assert.AreEqual("Index", result.RouteValues["action"]);
 
 
}
 
 
为IProductsRepository添加SaveProduct接口。
 
 
为SqlProductsRepository实现方法:
 
 
public void SaveProduct(Product product)
 
 
{
 
 
// If it's a new product, just attach it to the DataContext
 
 
if (product.ProductID == 0)
 
 
productsTable.InsertOnSubmit(product);
 
 
else
 
 
{
 
 
// If we're updating an existing product, tell the DataContext
 
 
// to be responsible for saving this instance
 
 
productsTable.Attach(product);
 
 
// Also tell the DataContext to detect any changes since the last save
 
 
productsTable.Context.Refresh(RefreshMode.KeepCurrentValues, product);
 
 
}
 
 
productsTable.Context.SubmitChanges();
 
 
}
 
 
修改Post方法的Edit Action。
 
 
[HttpPost]
 
 
public ActionResult Edit(Product product)
 
 
{
 
 
try
 
 
{
 
 
// TODO: Add update logic here
 
 
if (ModelState.IsValid)
 
 
{
 
 
productsRepository.SaveProduct(product);
 
 
TempData["message"] = product.Name + " has been saved.";
 
 
return RedirectToAction("Index");
 
 
}
 
 
else //Validation error, so redisplay save view
 
 
return View(product);
 
 
}
 
 
catch
 
 
{
 
 
return View(product);
 
 
}
 
 
}

编辑Admin.Master模板显示提示信息:

<% if (TempData["message"] != null)
 
 
{ %>
 
 
<div class="Message">
 
 
<%= Html.Encode(TempData["message"]) %></div>
 
 
<% } %>

F5运行测试.

clip_image009

转载请注明出处! Author: im@xingquan.org

转载于:https://www.cnblogs.com/xingquan/archive/2011/03/24/1994306.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值