构建一个真实的应用电子商务SportsStore(十)

时间:2023-03-08 21:43:30

构建一个真实的应用电子商务SportsStore(十)

我们现在还需要为管理员提供一个途径,使他能方便的管理网站的商品目录,这也是所有网站都需要的功能,常用到了几乎所有开发人员都要开发这种功能的地步,为了简化这种重复开发、又没有技术含量的工作,VS的设计和开发者们也试图通过MVC框架来自动生成这些功能,帮助开发人员,这也是我们开发这个管理后台的主要目的---学习如何通过MVC生成一个具有CRUD功能的管理模块。

创建CRUD Controller

我们将会创建一个新的controller去处理administration功能.右击SportsStore.WebUI工程的Controllers文件夹并选择添加Controller. 命名为AdminController,并确保模板下拉框中选择的是Empty MVC Controller:

构建一个真实的应用电子商务SportsStore(十)

修改代码如下:

构建一个真实的应用电子商务SportsStore(十)
using SportsStore.Domain.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace SportsStore.WebUI.Controllers
{
public class AdminController : Controller
{
private IProductsRepository repository;
public AdminController(IProductsRepository repo)
{
repository = repo;
}
public ViewResult Index()
{
return View(repository.Products);
}
}
}
构建一个真实的应用电子商务SportsStore(十)

我们所关心的是Index方法能否正确返回Product对象,所以,我们不能偷懒,还是创建一个mock repository 的实现,通过action 方法返回数据,做一个比较。现在我们就添加一个新的单元测试类,命名为AdminTests.cs:

构建一个真实的应用电子商务SportsStore(十)
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using SportsStore.WebUI.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc; namespace SportsStore.UnitTests
{
[TestClass]
public class AdminTests
{
[TestMethod]
public void Index_Contains_All_Products()
{
// Arrange - create the mock repository
Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
mock.Setup(m => m.Products).Returns(new Product[] {
new Product {ProductID = 1, Name = "P1"},
new Product {ProductID = 2, Name = "P2"},
new Product {ProductID = 3, Name = "P3"},
}.AsQueryable()); // Arrange - create a controller
AdminController target = new AdminController(mock.Object);
// Action
Product[] result = ((IEnumerable<Product>)target.Index().
ViewData.Model).ToArray();
// Assert
Assert.AreEqual(result.Length, 3);
Assert.AreEqual("P1", result[0].Name);
Assert.AreEqual("P2", result[1].Name);
Assert.AreEqual("P3", result[2].Name);
}
}
}
构建一个真实的应用电子商务SportsStore(十)

创建一个新的Layout

右击SportsStore.WebUI工程的Views/Shared并选择添加新建项. 选择MVC 4 布局页模板并设置名字为_AdminLayout.cshtml:

构建一个真实的应用电子商务SportsStore(十)

我们这个命名使用了一个下划线开头,这是因为微软还有另外一种技术,叫做WebMatrix,这种技术也使用Razor语法,它通过带下划线的命名来保持一个页面被驻留在浏览器中,在MVC4中不存在这种需求,当把一个启动layout命名为带下划线的这种命名规则,却一直被保存着,在任何地方都适用。

构建一个真实的应用电子商务SportsStore(十)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/Admin.css" rel="stylesheet" type="text/css" />
<title></title>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
构建一个真实的应用电子商务SportsStore(十)

我们还用为这个View添加一个Admin.css样式单,右击Content文件夹,添加一个样式单:

构建一个真实的应用电子商务SportsStore(十)
BODY, TD { font-family: Segoe UI, Verdana }
H1 { padding: .5em; padding-top: 0; font-weight: bold;
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; }
FORM {margin-bottom: 0px; }
DIV.Message { background: gray; color:White; padding: .2em; margin-top:.25em; }
.field-validation-error { color: red; display: block; }
.field-validation-valid { display: none; }
.input-validation-error { border: 1px solid red; background-color: #ffeeee; }
.validation-summary-errors { font-weight: bold; color: red; }
.validation-summary-valid { display: none; }
构建一个真实的应用电子商务SportsStore(十)

我们已经完成了这个layout,现在要为这个Admin控制器添加一个Index视图了:

构建一个真实的应用电子商务SportsStore(十)

这里一定要仔细看清楚上面的图片哦!我们选择了List scaffold(支架模板),这很重要.点击添加按钮,生成了一个带有代码的新视图,仔细看看这些代码,你可以将它汉化一下,为了不引起大家误解,我不去汉化它,就让它保持原样吧!在地址栏中输入/Admin/index,你会看到下图:

构建一个真实的应用电子商务SportsStore(十)

现在,你该明白scaffold为我们做了什么了吧!好了,现在你已经看到了效果了,让我们再去修善一下Index.cshtml文件吧!

构建一个真实的应用电子商务SportsStore(十)
@model IEnumerable<SportsStore.Domain.Entities.Product>
@{
ViewBag.Title = "Admin: 全部商品";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h1>全部商品</h1>
<table class="Grid">
<tr>
<th>ID</th>
<th>名称</th>
<th class="NumericCol">价格</th>
<th>操作</th>
</tr>
@foreach (var item in Model) {
<tr>
<td>@item.ProductID</td>
<td>@Html.ActionLink(item.Name, "Edit", new { item.ProductID })</td>
<td class="NumericCol">@item.Price.ToString("c")</td>
<td>
@using (Html.BeginForm("Delete", "Admin")) {
@Html.Hidden("ProductID", item.ProductID)
<input type="submit" value="删除"/>
}
</td>
</tr>
}
</table>
<p>@Html.ActionLink("添加新产品", "Create")</p>
构建一个真实的应用电子商务SportsStore(十)
构建一个真实的应用电子商务SportsStore(十)

编辑Products

要做这项工作很简单,和我们之前的操作非常相似,我们要做的无非是两件事:

1.显示一个允许管理员修改价格和属性的页面。

2.添加一个Action方法,当数据被提交后,这个方法能处理修改后的数据。

现在,我们就去为AdminController添加一个Edit方法,记住,你要添加下面的代码到你的AdminController类:

using SportsStore.Domain.Entities;
构建一个真实的应用电子商务SportsStore(十)
         public ViewResult Edit(int productId)
{
Product product = repository.Products
.FirstOrDefault(p => p.ProductID == productId);
return View(product);
}
构建一个真实的应用电子商务SportsStore(十)

添加测试方法到你的AdminTest类中:

构建一个真实的应用电子商务SportsStore(十)
        [TestMethod]
public void Can_Edit_Product() {
// Arrange - create the mock repository
Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
mock.Setup(m => m.Products).Returns(new Product[] {
new Product {ProductID = 1, Name = "P1"},
new Product {ProductID = 2, Name = "P2"},
new Product {ProductID = 3, Name = "P3"},
}.AsQueryable());
// Arrange - create the controller
AdminController target = new AdminController(mock.Object);
// Act
Product p1 = target.Edit(1).ViewData.Model as Product;
Product p2 = target.Edit(2).ViewData.Model as Product;
Product p3 = target.Edit(3).ViewData.Model as Product;
// Assert
Assert.AreEqual(1, p1.ProductID);
Assert.AreEqual(2, p2.ProductID);
Assert.AreEqual(3, p3.ProductID);
} [TestMethod]
public void Cannot_Edit_Nonexistent_Product()
{
// Arrange - create the mock repository
Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
mock.Setup(m => m.Products).Returns(new Product[] {
new Product {ProductID = 1, Name = "P1"},
new Product {ProductID = 2, Name = "P2"},
new Product {ProductID = 3, Name = "P3"},
}.AsQueryable());
// Arrange - create the controller
AdminController target = new AdminController(mock.Object);
// Act
Product result = (Product)target.Edit(4).ViewData.Model;
// Assert
Assert.IsNull(result);
}
构建一个真实的应用电子商务SportsStore(十)

创建Edit视图

构建一个真实的应用电子商务SportsStore(十)

修改代码如下:

构建一个真实的应用电子商务SportsStore(十)
@model SportsStore.Domain.Entities.Product
@{
ViewBag.Title = "Admin: 编辑" + @Model.Name;
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h1>Edit @Model.Name</h1> @using (Html.BeginForm()) {
@Html.EditorForModel()
<input type="submit" value="保存" />
@Html.ActionLink("取消并返回", "Index")
}
构建一个真实的应用电子商务SportsStore(十)

现在,你点击一下商品名称,我们能看到下图的结果:

构建一个真实的应用电子商务SportsStore(十)

Wow,MVC真是太方便了,但是,我仔细看看才发现,我们的ProductID居然能够编辑,这个是一个严重的Bug,我绝不允许它的存在,但是,这是什么原因导致的呢?仔细分析一下,原来我们的View中使用了

EditorForModel 方法, 这个方法的确非常的方便,但我们却没有得到一个非常理想的结果,我们不希望管理员能够看到或编辑产品编号,而且这个产品简介的字段也太短了,参阅MVC指导手册后,我才发现,我们需要使用模块数据元的特性去影响 Html.EditorForModel方法,不管怎么说,去试试看吧!打开SportsStore.Domain工程Entities文件夹中的Product类,添加两个新的引用:

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

修改后的代码如下:

构建一个真实的应用电子商务SportsStore(十)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc; namespace SportsStore.Domain.Entities
{
public class Product
{
[HiddenInput(DisplayValue = false)]
public int ProductID { get; set; }
public string Name { get; set; } [DataType(DataType.MultilineText)]
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
}
构建一个真实的应用电子商务SportsStore(十)

好,再运行一下!

构建一个真实的应用电子商务SportsStore(十)

哈哈,这才是我们想要的结果!

更新Product Repository

现在,我们需要一个新的功能,使我们能够保存产品的更新。首先,我们要在IProductRepository接口中添加一个新的方法:

构建一个真实的应用电子商务SportsStore(十)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract
{ public interface IProductsRepository
{
IQueryable<Product> Products { get; } void SaveProduct(Product product);
}
}
构建一个真实的应用电子商务SportsStore(十)

现在我们要把这个方法的实现添加的我们的EF实现类中,你还记得它放在哪吗?去找找吧!

构建一个真实的应用电子商务SportsStore(十)
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System.Linq;
namespace SportsStore.Domain.Concrete
{
public class EFProductRepository : IProductsRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
} public void SaveProduct(Product product)
{
if (product.ProductID == 0
)
{
context.Products.Add(product);
}
else

{
Product dbEntry
= context.Products.Find(product.ProductID);
if (dbEntry != null
)
{
dbEntry.Name =
product.Name;
dbEntry.Description =
product.Description;
dbEntry.Price =
product.Price;
dbEntry.Category =
product.Category;
}
}
context.SaveChanges();
}

}
}
构建一个真实的应用电子商务SportsStore(十)

当管理员点击保存按钮时,会产生一个Edit的Post请求,我们需要重载一个Edit方法,去处理这个请求,在你的AdminController中添加如下方法:

构建一个真实的应用电子商务SportsStore(十)
        [HttpPost]
public ActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
repository.SaveProduct(product);
TempData["message"] = string.Format("{0} has been saved", product.Name);
return RedirectToAction("Index");
}
else
{
// there is something wrong with the data values
return View(product);
}
}
构建一个真实的应用电子商务SportsStore(十)

添加如下测试方法到AdminTest类:

构建一个真实的应用电子商务SportsStore(十)
[TestMethod]
public void Can_Save_Valid_Changes() {
// Arrange - create mock repository
Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
// Arrange - create the controller
AdminController target = new AdminController(mock.Object);
// Arrange - create a product
Product product = new Product {Name = "Test"};
// Act - try to save the product
ActionResult result = target.Edit(product);
// Assert - check that the repository was called
mock.Verify(m => m.SaveProduct(product));
// Assert - check the method result type
Assert.IsNotInstanceOfType(result, typeof(ViewResult));
} [TestMethod]
public void Cannot_Save_Invalid_Changes() {
// Arrange - create mock repository
Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
// Arrange - create the controller
AdminController target = new AdminController(mock.Object);
// Arrange - create a product
Product product = new Product { Name = "Test" };
// Arrange - add an error to the model state
target.ModelState.AddModelError("error", "error");
// Act - try to save the product
ActionResult result = target.Edit(product);
// Assert - check that the repository was not called
mock.Verify(m => m.SaveProduct(It.IsAny<Product>()), Times.Never());
// Assert - check the method result type
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
构建一个真实的应用电子商务SportsStore(十)

显示确认信息

现在我们要在_AdminLayout.cshtml文件中添加一条标签,我们使用TempData模板处理一个消息,这样,我们就能够在任何使用了这个模板的页面处理消息,而不用到处去写Razor:

构建一个真实的应用电子商务SportsStore(十)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/Admin.css" rel="stylesheet" type="text/css" />
<title></title>
</head>
<body>
<div>
@if (TempData["message"] != null) {
<div class="Message">@TempData["message"]</div>
}
@RenderBody()
</div>
</body>
</html>
构建一个真实的应用电子商务SportsStore(十)

今天的内容实在是不少,暂时先写到这里吧,活不是一天干完的,我相信,你花一天时间画的画,你用一年的时间都卖不出去,而你花一年的时间画的画,你却能在一天之内卖出去!明天我们继续为我们的管理后台添加数据验证的功能和删除的功能,这些虽然没有太高的技术含量,但却是我们经常需要用到的东西,在本系列教程的最后部分,我们将进入到本教程最精彩的部分---网站安全。我将详尽的讲解如何使用MVC授权和过滤等MVC的重量级技术,请继续关注我们的项目,关注我的续篇!

王志岳 塔塔信息技术(中国)股份有限公司 | 开发者导航

多图杀猫

构建一个真实的应用电子商务SportsStore(十)

看到标题大家会想,什么是动态静图呀?!动态静图(Cinemagraphs)指的是一些静止的图片,可是里面含有一些动态的元素,让人可以反复的 回味一些短暂的瞬间。动态静图是一项将视频与图片结合起来的技术手法。这个技术手法是在纽约的时尚摄影师Jamie Beck与动态影像设计师Kevin Burg合作下创造的。自从有了这项技术,许多设计师已经制作了许多类型相同的动态静图。典型的动态静图一般会制作成.gif格式的文件,方便人们在网页 上使用。这篇文章里,我主要是与大家分享一些炫酷无比的动态静图,给大家一些创造灵感。

提醒:待会鼠标滚轮别滚太快了,如果你想要真切的体验动态静图的震撼,请你仔细地看——一些你意想不到的东西也许会动起来。

动态静图一般是.gif(也可能会是视频形式)的文件,可是相比起那些随处可见的普通图片,它给人的感觉是更具有艺术感。典型的动态静图就是一张静态的图片,里面只有一点点的元素会细微的活动。这样的手法使得动态静图有一种神秘感,我喜欢盯着那些会动的地方看。

原文地址:http://www.goodfav.com/40-examples-of-stunning-cinemagraphs-748.html

想要制作一张动态静图,你需要一张高质量的图片。当你构图完成后,你要在这个构图下需要录制一段视频。这是最难的一步:视频里,只有那些你想要让它 在动态静图里动起来的元素,它才能动。如果你用的是模特,那你要确保她们保持绝对的静止,就像她们是在相片里一样。录像的时候要采用你摄像设备最高的分辨 率,同样的,要使用三脚架来避免镜头抖动。你的素材收集好了之后,就可以开始制作动态静图了。一些专业的工具,比如说Photoshop,可以让你十分轻 而易举的进行制作与修饰。视频文件可以在Photoshop里导入,通过“Now go to File>import>video frame to layers”

构建一个真实的应用电子商务SportsStore(十)

Cinemagraph showcase

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

构建一个真实的应用电子商务SportsStore(十)

Cinemagraph Tutorials