1. Trang chủ
  2. » Công Nghệ Thông Tin

9 chuong 09

30 33 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 30
Dung lượng 1,04 MB

Nội dung

CHAPTER SportsStore: Completing the Cart Trong chương này, tiếp tục xây dựng SportsStore tương tự app Trong chương trước, Chúng ta thêm hổ trợ cho việc mua cart cải thiện hoàn thành chức Sử dụng Model Binding Các MVC Framework sử dụng hệ thống gọi model binding – mơ hình ràng buộc để tạo đối tượng C # từ yêu cầu HTTP để chuyển chúng giá trị tham số cho phương thức hoạt động Đây cách framework MVC xử lý form, ví dụ: nhìn vào thơng số phương thức hoạt động đạt sử dụng model binder để có giá trị form gửi trình duyệt chuyển đổi chúng sang kiểu tham số dạng trước chuyển chúng đến phương thức hoạt động Mơ hình binders tạo C # chuẩn từ thông tin có sẵn yêu cầu Đây đặc điểm MVC Framework Tơi tạo mơ hình binder tùy chỉnh để cải thiện class CartController Tơi thích sử dụng tính điều khiển Cart để lưu trữ quản lý đối tượng Cart mà thiết lập Chương 8, tơi khơng thích cách kiểm tra Nó khơng phù hợp với phần lại mơ hình ứng dụng, mà dựa thơng số hoạt động Có thể có đơn vị sai class CartController muốn kiểm tra phải thử tham số lớp sở Để giải vấn đề này, tơi tạo mơ hình Binder tùy chỉnh lấy đối tượng Cart chứa data Trong MVC Framework tạo đối tượng Cart làm chúng tham số cho phương pháp hoạt động lớp CartController Các tính ràng buộc mơ hình mạnh mẽ linh hoạt Đi sâu tính Chương 24 Creating a Custom Model Binder Tơi tạo mơ hình Binder tuỳ chỉnh triển khai interface System.Web.Mvc.IModelBinder Để thực hiện, thêm thư mục vào project SportsStore.WebUI gọi Infrastructure/Binders tạo tập tin lớp CartModelBinder.cs bên Listing 9-1 cho thấy nội dung tập tin Listing 9-1 The Contents of the CartModelBinder.cs File using System.Web.Mvc; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure.Binders { public class CartModelBinder : IModelBinder { private const string sessionKey = "Cart"; public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { // get the Cart from the session Cart cart = null; if (controllerContext.HttpContext.Session != null) { cart =(Cart)controllerContext.HttpContext.Session[sessionKey]; } // create the Cart if there wasn't one in the session data if (cart == null) { cart = new Cart(); if (controllerContext.HttpContext.Session != null) { controllerContext.HttpContext.Session[sessionKey] =cart; } } // return the cart return cart; } } } Giao diện IModelBinder định nghĩa phương thức: BindModel Hai thông số cung cấp để làm cho việc tạo mơ hình đối tượng miền Các ControllerContext cung cấp quyền truy cập vào tất thông tin mà lớp điều khiển có, bao gồm chi tiết yêu cầu từ khách hàng Các ModelBindingContext cung cấp cho bạn thơng tin đối tượng mơ hình bạn yêu cầu xây dựng số công cụ để làm cho trình liên kết dễ dàng Đối với mục đích tơi, lớp ControllerContext thứ tơi quan tâm Nó có tính HttpContext,mà có tính Session cho phép tơi có thiết lập liệu session Tơi có đối tượng Cart liên kết với session người dùng cách đọc giá trị từ liệu session, tạo Cart khơng có Tơi cần phải nói cho MVCFramework sử dụng lớp CartModelBinder để tạo trường Cart Tôi làm điều phương pháp Application_Start Global.asax, thể Listing 9-2 Listing 9-2 Registering the CartModelBinder Class in the Global.asax.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using SportsStore.Domain.Entities; using SportsStore.WebUI.Infrastructure.Binders; namespace SportsStore.WebUI { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder()); } } } Bây cập nhật điều khiển Cart để loại bỏ phương pháp getCart dựa mơ hình Binder cung cấp điều khiển với đối tượng Cart Listing 9-3 cho thấy thay đổi Listing 9-3 Relying on the Model Binder in the CartController.cs File using System.Linq; 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; public CartController(IProductRepository repo) { repository = repo; } public ViewResult Index(Cart cart, string returnUrl) { return View(new CartIndexViewModel { ReturnUrl = returnUrl, Cart = cart }); } public RedirectToRouteResult AddToCart(Cart cart, int productId, string returnUrl) { Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId); if (product != null) { cart.AddItem(product, 1); } return RedirectToAction("Index", new { returnUrl }); } public RedirectToRouteResult RemoveFromCart(Cart cart, int productId, string returnUrl) { Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId); if (product != null) { cart.RemoveLine(product); } return RedirectToAction("Index", new { returnUrl }); } } } Tôi gỡ bỏ phương pháp getCart thêm tham số Cart để phương thức hoạt động Khi MVC Framework nhận yêu cầu, phương pháp AddToCart gọi, bắt đầu cách nhìn vào thơng số cho phương pháp hoạt đơng Nó nhìn vào danh sách Binders có sẵn cố gắng tạo trường hợp loại tham số Các Binders tùy chỉnh yêu cầu tạo đối tượng Cart, làm cách làm việc với tính trạng thái session Giữa Binder tùy chỉnh Binder mặc định, MVC Framework tạo tập hợp thơng số cần thiết để gọi phương thức hoạt động, cho phép tơi cấu trúc lại điều khiển để không hiểu sai đối tượng Cart tạo nhận u cầu Có nhiều lợi ích sử dụng Binder tùy chỉnh mơ Việc phân chia logic sử dụng để tạo Cart từ điều khiển, cho phép thay đổi cách lưu trữ đối tượng Cart mà không cần phải thay đổi điều khiển Lợi ích thứ hai class điều khiển mà làm việc với đối tượng Cart khai báo chúng tham số phương thức hoạt động tận dụng mơ hình tùy chỉnh Binder Lợi ích thứ ba quan trọng nhất, tơi kiểm tra đơn vị điều khiển Cart mà không cần phải thử nhiều ASP.NET plumbing UNIT TEST: THE CART CONTROLLER Tơi kiểm tra đơn vị class CartController cách tạo đối tượng Cart chuyển chúng tới phương pháp hoạt động Tơi muốn thử nghiệm ba khía cạnh khác điều khiển này:  Phương pháp AddToCart nên thêm tìm kiếm product tuỳ chĩnh Cart  Sau thêm product vào cart, người sử dụng nên chuyển đến giao diện Index  URL mà người dùng trở danh mục cần thơng qua cách xác để phương thức hoạt động Index Tôi thêm file CartTests.cs project portsStore.UnitTests: using using using using using using System; Microsoft.VisualStudio.TestTools.UnitTesting; SportsStore.Domain.Entities; System.Linq; Moq; SportsStore.Domain.Abstract; using SportsStore.WebUI.Controllers; using System.Web.Mvc; using SportsStore.WebUI.Models; namespace SportsStore.UnitTests { [TestClass] public class CartTests { // existing test methods omitted for brevity [TestMethod] public void Can_Add_To_Cart() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Apples"}, }.AsQueryable()); // Arrange - create a Cart Cart cart = new Cart(); // Arrange - create the controller CartController target = new CartController(mock.Object); // Act - add a product to the cart target.AddToCart(cart, 1, null); // Assert Assert.AreEqual(cart.Lines.Count(), 1); Assert.AreEqual(cart.Lines.ToArray()[0].Product.ProductID, 1); } [TestMethod] public void Adding_Product_To_Cart_Goes_To_Cart_Screen() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Apples"}, }.AsQueryable()); // Arrange - create a Cart Cart cart = new Cart(); // Arrange - create the controller CartController target = new CartController(mock.Object); // Act - add a product to the cart RedirectToRouteResult result = target.AddToCart(cart, 2, "myUrl"); // Assert Assert.AreEqual(result.RouteValues["action"], "Index"); Assert.AreEqual(result.RouteValues["returnUrl"], "myUrl"); } [TestMethod] public void Can_View_Cart_Contents() { // Arrange - create a Cart Cart cart = new Cart(); // Arrange - create the controller CartController target = new CartController(null); // Act - call the Index action method CartIndexViewModel result = (CartIndexViewModel)target.Index(cart, "myUrl").ViewData.Model; // Assert Assert.AreSame(result.Cart, cart); Assert.AreEqual(result.ReturnUrl, "myUrl"); } } } Hồn thành Cart Tơi giới thiệu mơ hình tùy chỉnh binder, lúc để hoàn thành chức cart cách thêm hai tính Việc cho phép khách hàng để loại bỏ mục từ Cart Tính thứ hai hiển thị tóm tắt Cart trang Removing Items from the Cart Tôi xác định thử nghiệm phương thức hoạt động RemoveFromCart điều khiển, cho phép khách hàng loại bỏ mặt hàng vấn đề phương pháp lần xem, mà làm cách thêm nút Remove hàng tóm tắt cart Những thay đổi để Views/Cart/Index.cshtml hiển thị Listing 9-4 Listing 9-4 Introducing a Remove Button to the Index.cshtml File @model SportsStore.WebUI.Models.CartIndexViewModel @{ ViewBag.Title = "Sports Store: Your Cart"; } #cartTable td { vertical-align: middle; } Your cart Quantity Item Price Subtotal @foreach (var line in Model.Cart.Lines) { @line.Quantity @line.Product.Name @line.Product.Price.ToString("c") @((line.Quantity * line.Product.Price).ToString("c")) @using (Html.BeginForm("RemoveFromCart", "Cart")) { @Html.Hidden("ProductId",line.Product.ProductID) @Html.HiddenFor(x => x.ReturnUrl) } } Total: @Model.Cart.ComputeTotalValue().ToString("c") Continue shopping Tôi thêm cột cho hàng bảng có chứa form với phần tử Input Tôi định dạng phần tử Input nút với Bootstrap thêm phần tử style id vào phần tử table để đảm bảo nút nội dung cột khác liên kết Lưu ý: Tôi dùng phương thức trợ giúp Html.HiddenFor để tạo field ẩn cho thuộc tính mơ hình ReturnUrl, tơi phải sử dụng phương thức trợ giúp Html.Hidden kiểu chuỗi làm tương tự cho field ProductID Nếu viết Html.HiddenFor(x => line.Product.ProductID), helper tạo field ẩn với tên line.Product.ProductID Tên trường không phù hợp với tên tham số cho phương thức hoạt động CartController.RemoveFromCart, mà ngăn chặn mơ hình Binders mặc định từ cơng việc, MVC Framework khơng thể gọi phương thức Bạn thấy nút Remove nơi làm việc cách chạy ứng dụng thêm mục vào giỏ mua hàng Hãy nhớ giỏ có chứa chức để loại bỏ nó, mà bạn kiểm tra cách nhấn vào nút mới, thể Figure 9-1 Figure 9-1 Xóa hàng từ giỏ hàng Adding the Cart Summary Tơi có chức cart, có vấn đề với cách mà tích hợp vào giao diện Khách hàng cho biết có cart họ cách xem hình cart summary Và họ xem hình cart summary cách thêm mục vào cart Để giải vấn đề này, thêm tiện ích tóm tắt nội dung cart bấm để hiển thị nội dung cart thông qua ứng dụng Tôi làm điều cách mà thêm widget chuyển hướng hoạt động output tơi bố trí vào Razor Để bắt đầu, cần phải thêm phương thức đơn giản, thể Listing 9-5, đến class CartController Listing 9-5 Adding the Summary Method to the CartController.cs File using System.Linq; 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; public CartController(IProductRepository repo) { repository = repo; } // other action methods omitted for brevity public PartialViewResult Summary(Cart cart) { return PartialView(cart); } } } Phương thức đơn giản cần phải trả giao diện, cung cấp Cart (mà thu cách sử dụng mơ hình binder tùy chỉnh) liệu giao diện Để tạo giao diện, kích chuột phải vào phương thức action Summary chọn Add View từ menu pop-up Đặt tên cho giao diện để Summary kích vào nút OK để tạo tập tin Views/Cart/Summary.cshtml Chỉnh sửa khung giao diện cho trùng khớp Listing 9-6 Listing 9-6 The Contents of the Summary.cshtml File @model SportsStore.Domain.Entities.Cart @Html.ActionLink("Checkout", "Index", "Cart", new { returnUrl = Request.Url.PathAndQuery }, new { @class = "btn btn-default navbar-btn" }) Your cart: @Model.Lines.Sum(x => x.Quantity) item(s), @Model.ComputeTotalValue().ToString("c") Giao diện hiển thị số lượng mặt hàng giỏ hàng, tổng chi phí mặt hàng, liên kết hiển thị chi tiết giỏ hàng cho người sử dụng Bây tạo giao diện trả phương thức action Summary, tơi gọi phương thức action Summary tập tin _Layout.cshtml để hiển thị tóm tắt giỏ, thể Listing 9-7 Listing 9-7 Adding the Summary Partial View to the _Layout.cshtml File @ViewBag.Title Figure 9-4 The shipping details form Vấn đề với giao diện có chứa nhiều đánh dấu lặp lặp lại Sử dụng MVC Framework giúp HTML để làm giảm trùng lặp, chúng khó cấu trúc định dạng nội dung theo cách mà muốn Thay vào đó, tơi sử dụng tính lấy metadata đối tượng mơ hình giao diện kết hợp với kết hợp C# biểu thức Razor Bạn thấy tơi làm Listing 9-12 Listing 9-12 Reducing Duplication in the Checkout.cshtml File @model SportsStore.Domain.Entities.ShippingDetails @{ ViewBag.Title = "SportStore: Checkout"; } Check out now

Please enter your details, and we'll ship your goods right away!

@using (Html.BeginForm()) { Ship to Name @Html.TextBoxFor(x => x.Name, new {@class = "form-control"}) Address foreach (var property in ViewData.ModelMetadata.Properties) { if (property.PropertyName != "Name" && property.PropertyName != "GiftWrap") { @(property.DisplayName ?? property.PropertyName) @Html.TextBox(property.PropertyName, null, new {@class = "form-control"}) } } Options @Html.EditorFor(x => x.GiftWrap) Gift wrap these items } Thuộc tính static ViewData.ModelMetadata trả đối tượng System.Web.MVC.ModelMetaData cung cấp thông tin loại mơ hình cho giao diện hiển thị Thuộc tính Properties tơi sử dụng vòng lặp foreach trả tập hợp đối tượng ModelMetaData, đối tượng số đại diện cho thuộc tính xác định loại mơ hình Tơi sử dụng property PropertyName để đảm bảo không tạo nội dung cho properties Name GiftWrap tạo tập hợp thành phần, hoàn chỉnh với lớp Bootstrap, cho tất properties khác Tip: Từ khố for if mà tơi sử dụng nằm phạm vi biểu thức Razor (biểu thức @using tạo form), tơi không cần phải thêm tiền tố với ký tự @ Trong thực tế, làm vậy, Razor báo lỗi Nó chút thời gian để có sử dụng để ký tự @ cần thiết với Razor, trở thành chất thứ hai hầu hết lập trình viên Đối với người khơng thể hồn tồn nhận lần (trong bao gồm tôi), thông báo lỗi Razor hiển thị trình duyệt cung cấp hướng dẫn cụ thể để sửa chữa sai lầm Tôi không thực hồn tồn được, nhiên Nếu bạn chạy ví dụ nhìn vào đầu tạo giao diện, bạn thấy số label khơng hồn tồn xác, Figure 9-5 minh họa Figure 9-5 The problem with generating labels from property names Kiểm tra xem có giá trị DisplayName có sẵn tạo thành phần form, này: @( property.DisplayName ?? property.PropertyName) Để tận dụng lợi property DisplayName, tơi cần phải áp dụng thuộc tính Display với lớp mơ hình, Listing 9-13 Listing 9-13 Áp dụng thuộc tính Display với tập tin ShippingDetails.cs 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 line" ) ] [Display(Name="Line 1" ) ] public string Line1 { get; set; } [Display(Name = "Line 2" ) ] public string Line2 { get; set; } [Display(Name = "Line 3" ) ] public string Line3 { 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; } } } Thiết lập giá trị Name cho thuộc tính Display cho phép để thiết lập giá trị đọc property DisplayName giao diện Bạn thấy hiệu ứng cách khởi động ứng dụng xem trang toán, thể Figure 9-6 Figure 9-6 The effect ofthe Display attribute on the model type Ví dụ cho thấy hai khía cạnh khác làm việc với MVC Framework Việc bạn làm việc xung quanh tình trạng để đơn giản hóa việc đánh dấu viết mã bạn Thứ hai giao diện chạy mơ hình MVC hạn chế việc hiển thị liệu đánh dấu, công cụ mà Razor C# cung cấp cho nhiệm vụ phong phú linh hoạt, chí đến mức làm việc với loại siêu liệu Implementing the Order Processor Tôi cần thành phần ứng dụng để chi tiết đơn đặt hàng để xử lý Để phù hợp với ngun tắc mơ hình MVC, định nghĩa interface cho chức này, viết thực thi interface, sau kết hợp hai container DI, Ninject Defining the Interface Thêm interface gọi IOrderProcessor vào thư mục Abstract dự án SportsStore.Domain chỉnh sửa nội dung cho khớp Listing 9-14 Listing 9-14 The Contents of the IOrderProcessor.cs File using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract { public interface IOrderProcessor { void ProcessOrder( Cart cart, ShippingDetails shippingDetails) ; } } Implementing the Interface Việc thực thi IOrderProcessor xử lý đơn đặt hàng cách gửi chúng vào trang quản trị Tơi đơn giản hóa q trình bán hàng, tất nhiên Hầu hết trang web thương mại điện tử không đơn đặt hàng e-mail đơn giản, không cung cấp hỗ trợ cho xử lý thẻ tín dụng hình thức tốn khác Nhưng tơi muốn giữ cho thứ tập trung vào MVC, sử dụng e-mail Tạo tập tin lớp gọi EmailOrderProcessor.cs thư mục bê tông dự án SportsStore.Domain chỉnh sửa nội dung cho khớp Listing 9-15 Lớp sử dụng xây dựng SMTP hỗ trợ bao gồm thư viện NET Framework để gửi e-mail Listing 9-15 The Contents of the EmailOrderProcessor.cs File using System.Net; using System.Net.Mail; using System.Text; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Concrete { public class EmailSettings { public string MailToAddress = "orders@example.com" ; public string MailFromAddress = "sportsstore@example.com" ; public bool UseSsl = true; public string Username = "MySmtpUsername" ; public string Password = "MySmtpPassword" ; public string ServerName = "smtp.example.com"; public int ServerPort = 587; public bool WriteAsFile = false; public string FileLocation = @"c:\sports_store_emails" ; } public class EmailOrderProcessor : IOrderProcessor { private EmailSettings emailSettings; public EmailOrderProcessor( EmailSettings settings) { emailSettings = settings; } public void ProcessOrder( Cart cart, ShippingDetails shippingInfo) { using ( var smtpClient = new SmtpClient( ) ) { smtpClient.EnableSsl = emailSettings.UseSsl; smtpClient.Host = emailSettings.ServerName; smtpClient.Port = emailSettings.ServerPort; smtpClient.UseDefaultCredentials = false; smtpClient.Credentials = new NetworkCredential( emailSettings Username, emailSettings.Password) ; if ( emailSettings.WriteAsFile) { smtpClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory; smtpClient.PickupDirectoryLocation = emailSettings.FileLocation; smtpClient.EnableSsl = false; } StringBuilder body = new StringBuilder( ) AppendLine( " A new order has been submitted" ) AppendLine( " -" ) 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( ) ) AppendLine( " -" ) AppendLine( " Ship to: " ) AppendLine( shippingInfo.Name) AppendLine( shippingInfo.Line1) AppendLine( shippingInfo.Line2 ?? "" ) AppendLine( shippingInfo.Line3 ?? "" ) 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( emailSettings MailFromAddress, //From emailSettings MailToAddress, //To " New order submitted! ", //Subject body.ToString( ) ) ; //Body if ( emailSettings.WriteAsFile) { mailMessage.BodyEncoding = Encoding.ASCII; } smtpClient.Send(mailMessage) ; } } } } Để đơn giản, định nghĩa lớp EmailSettings Listing 9-15 Một instance lớp yêu cầu cấu trúc EmailOrderProcessor chứa tất cài đặt cần thiết để cấu hình lớp NET e-mail Tip: Đừng lo lắng bạn khơng có máy chủ SMTP có sẵn Nếu bạn thiết lập property EmailSettings.WriteAsFile true, thông điệp e-mail viết tập tin vào thư mục định property FileLocation Thư mục phải tồn có khả ghi Các tập tin viết với mở rộng eml, chúng đọc với trình soạn thảo văn Vị trí tơi thiết lập listing c:\sports_store_emails Registering the Implementation Bây tơi có thực thi interface IOrderProcessor phương tiện để cấu hình nó, tơi sử dụng Ninject để tạo instance Chỉnh sửa tập tin NinjectDependencyResolver.cs thư mục Infrastructure project SportsStore.WebUI thực thay đổi thể Listing 9-16 với phương thức AddBindings Listing 9-16 Adding Ninject Bindings for IOrderProcessor to the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Configuration; using System.Web.Mvc; using Moq; using Ninject; using SportsStore.Domain.Abstract; using SportsStore.Domain.Concrete; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver( IKernel kernelParam) { kernel = kernelParam; AddBindings( ) ; } public object GetService( Type serviceType) { return kernel.TryGet( serviceType) ; } public IEnumerable GetServices( Type serviceType) { return kernel.GetAll( serviceType) ; } private void AddBindings( ) { kernel.Bind( ).To( ) ; EmailSettings emailSettings = new EmailSettings { WriteAsFile = bool.Parse(ConfigurationManager AppSettings["Email WriteAsFile"] ?? " false" ) }; kernel.Bind().To() WithConstructorArgument("settings" , emailSettings) ; } } } Tôi tạo đối tượng EmailSettings, mà sử dụng với phương pháp Ninject WithConstructorArgument để tơi thêm vào constructor EmailOrderProcessor trường hợp tạo để yêu cầu dịch vụ cho giao diện IOrderProcessor Trong Liệt kê 9-16, định giá trị để thuộc tính EmailSettings: WriteAsFile Tơi đọc giá trị tài sản cách sử dụng ConfigurationManager Tài sản AppSettings, cung cấp truy cập để cài đặt ứng dụng định nghĩa Web.config (các file thư mục dự án root), thể Liệt kê 9-17 Bảng liệt kê 9-17 Cài đặt ứng dụng tập tin Web.config Hoàn thành Cart Controller Để hoàn thành lớp CartController, cần chỉnh sửa kiến trúc để yêu cầu thực giao diện IOrderProcessor thêm phương thức hành động mà xử lý yêu cầu POST biểu mẫu HTTP người dùng nhấp chuột vào nút Complete order Listing 918 cho thấy hai thay đổi Listing 9-18 Hoàn thành việc điều khiển tập tin CartController.cs using System.Linq; 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) { repository = repo; orderProcessor = proc; } // phương thức action bỏ qua cho ngắn gọn public ViewResult Checkout() { return View(new ShippingDetails()); } [HttpPost] public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails) { if (cart.Lines.Count() == 0) { ModelState.AddModelError("", "Sorry, your cart is empty!") ; } if (ModelState.IsValid) { orderProcessor.ProcessOrder(cart, shippingDetails); cart.Clear(); return View("Completed"); } else { return View(shippingDetails); } } } } Bạn thấy phương thức Checkout tơi thêm gắn với thuộc tính HttpPost, có nghĩa gọi cho yêu cầu POST - trường hợp này, người dùng submit form Một lần nữa, tơi dựa vào hệ thống nối kết mơ hình, cho tham số ShippingDetails (được tạo tự động cách sử dụng liệu form HTTP) tham số Cart (được tạo cách sử dụng kết nối tùy chỉnh) Chú ý: Sự thay đổi kiến trúc bắt buộc cập nhật kiểm thử tạo cho lớp CartController Gán null cho tham số kiến trúc cho phép biên dịch unit test Các MVC Framework kiểm tra ràng buộc validation mà áp dụng để ShippingDetails sử dụng thuộc tính thích liệu, vi phạm vấn đề validation chuyền sang phương thức action thơng qua thuộc tính ModelState Tơi thấy có vấn đề cách kiểm tra thuộc tính ModelState.IsValid Chú ý gọi phương thức ModelState.AddModelError để ghi thông báo lỗi khơng có sản phẩm giỏ hàng Tơi giải thích làm để hiển thị lỗi thời gian ngắn, tơi có nhiều để nói mơ hình liên kết - model binding validation Chương 24 25 UNIT TEST: ORDER PROCESSING Để hoàn tất việc unit test cho lớp CartController, cần phải kiểm thử cách hoạt động phiên phương thức Checkout Tôi muốn xử lý đơn hàng có giỏ hàng khách hàng cung cấp chi tiết giao hàng hợp lệ Trong trường hợp khác, khách hàng hiển thị lỗi Đây phương thứ kiểm thử đầu tiên: [TestMethod] public void Cannot_Checkout_Empty_Cart() { // Chuẩn bị – tạo đơn hàng giả Mock mock = new Mock(); // Chuẩn bị – tạo giỏ hàng trống Cart cart = new Cart(); // Chuẩn bị – tạo chi tiết vận chuyển ShippingDetails shippingDetails = new ShippingDetails(); // Chuẩn bị – tạo controller CartController target = new CartController(null, mock.Object); // Thực ViewResult result = target.Checkout(cart, shippingDetails); // Xác nhận – kiểm tra đơn hàng không thông qua vào xử lý mock.Verify(m => m.ProcessOrder(It.IsAny(), It.IsAny()), Times.Never()); // Xác nhận – kiểm tra xem phương thức trở trang mặc định Assert.AreEqual("", result.ViewName); // Xác nhận - kiểm tra tơi chuyển mơ hình hợp lệ sang giao diện hiển thị Assert.AreEqual(false, result.ViewData.ModelState.IsValid); } Kiểm tra đảm bảo tốn với giỏ rỗng Tơi kiểm tra điều cách đảm bảo ProcessOrder việc thực IOrderProcessor giả không gọi, trang mà phương thức trả trang mặc định (trong hiển thị lại liệu nhập vào khách hàng cung cấp cho họ hội để sửa nó), model state chuyển tới trang đánh dấu không hợp lệ Phương pháp kiểm thử hoạt động theo cách tương tự, thêm lỗi vào mơ hình view để mô vấn đề cách kết nối mô hình (điều xảy khách hàng nhập liệu chuyển hàng không hợp lệ): [TestMethod] public void Cannot_Checkout_Invalid_ShippingDetails( ) { // Sắp xếp – tạo xử lý đơn hàng giả Mock mock = new Mock(); // Chuẩn bị – tạo giỏ hàng với hàng Cart cart = new Cart( ); cart.AddItem(new Product( ), 1); // Chuẩn bị - tạo controller CartController target = new CartController(null, mock.Object); // Chuẩn bị - thêm lỗi vào mơ hình target.ModelState.AddModelError("error", "error"); // Thực – thử toán ViewResult result = target.Checkout(cart, new ShippingDetails()); // Xác nhận – kiểm tra xem đơn hàng không chuyển sang xử lý mock.Verify(m => m.ProcessOrder(It.IsAny(), It.IsAny()), Times.Never()); // Xác nhận – kiểm tra xem phương thức trả trang mặc định Assert.AreEqual("", result.ViewName); // Xác nhận – kiểm tra xem chuyển mô hình khơng hợp lệ sang giao diện hiển Assert.AreEqual(false, result.ViewData.ModelState.IsValid); } Đã xác định giỏ rỗng chi tiết không hợp lệ ngăn chặn đơn đặt hàng xử lý, cần phải đảm bảo xử lý đơn đặt hàng thích hợp Dưới kiểm tra: [TestMethod] public void Can_Checkout_And_Submit_Order( ) { // Arrange - create a mock order processor Mock mock = new Mock(); // Arrange - create a cart with an item Cart cart = new Cart( ); cart.AddItem( new Product( ), 1); // Arrange - create an instance of the controller CartController target = new CartController( null, mock.Object); // Act - try to checkout ViewResult result = target.Checkout(cart, new ShippingDetails( )); // Assert - check that the order has been passed on to the processor mock.Verify( m => m.ProcessOrder( It.IsAny( ), It.IsAny( )), Times.Once( )); // Assert - check that the method is returning the Completed view Assert.AreEqual( " Completed" , result.ViewName); // Assert - check that I am passing a valid model to the view Assert.AreEqual(true, result.ViewData.ModelState.IsValid); } Chú ý khơng cần phải thử nghiệm mà tơi xác định chi tiết giao hàng hợp lệ Điều xử lý hồn tồn tự động mơ hình binder cách sử dụng thuộc tính áp dụng cho thuộc tính lớp ShippingDetails Displaying Validation Errors MVC Framework sử dụng thuộc tính validation áp dụng cho lớp ShippingDetails để xác nhận liệu người dùng Tuy nhiên, cần phải thực vài thay đổi để hiển thị vấn đề cho người dùng Vấn đề cần phải cung cấp tóm tắt vấn đề cho người dùng Điều đặc biệt quan trọng đối phó với vấn đề khơng liên quan đến lĩnh vực cụ thể, chẳng hạn người dùng cố gắng để kiểm tra khơng có sản phẩm giỏ hàng Để hiển thị tóm tắt hữu ích lỗi validation, tơi sử dụng phương thức trợ giúp Html.ValidationSummary, giống làm Chapter Listing 919 cho thấy việc bổ sung vào Checkout.cshtml Listing 9-19 Thêm Validation Summary vào tập tin Checkout.cshtml @using (Html.BeginForm( )) { @Html.ValidationSummary() Ship to Name @Html.TextBoxFor( x => x.Name, new { @class = "form-control" } ) Address Bước tạo số CSS style nhắm đến lớp sử dụng tóm tắt validation mà MVC Framework thêm phần tử không hợp lệ Tôi tạo Style Sheet gọi ErrorStyles.css thư mục Content project SportsStore.WebUI xác định style hiển thị Listing 9-20 Đây tập hợp style mà sử dụng Chapter Listing 9-20 Nội dung tập tin ErrorStyles.css field-validation-error { color: #f00; } field-validation-valid { display: none; } input-validation-error { border: 1px solid #f00; backgroundcolor: #fee; } validation-summary-errors { font-weight: bold; color: #f00;} validation-summary-valid { display: none; } Trong đơn hàng, để sử dụng style, cập nhật tập tin _Layout.cshtml để thêm phần tử liên kết với tập tin ErrorStyles.css, thể Listing 9-21 Listing 9-21 Thêm phần tử liên kết tập tin _Layout.cshtml @ViewBag.Title

Ngày đăng: 23/10/2019, 21:16

TỪ KHÓA LIÊN QUAN

w