您的位置:首页 > 编程语言 > ASP

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

2011-03-24 21:03 357 查看

定义一个订单提供 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视图。





使用 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文件夹,右键单击添加新项:





添加新的样式表: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:





添加必要的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>


>

访问测试如下:





创建一个产品编辑

创建测试:

[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。





<%@ 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运行测试.





转载请注明出处! Author: im@xingquan.org
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: