作者:[美]Adam Freeman      来源:《精通ASP.NET MVC 4》

本文将继续构建 SportsStore 应用程序,为网站管理员提供一个管理产品分类的方法。本文将添加一些支持功能,包括通过产品存储库进行产品的创建、编辑和删除,以及上传产品图片并将其显示在产品旁边。

1.添加分类管理

管理条目集合的惯例,是向用户显示两种形式的页面 —— 一个列表页面和一个编辑页面。

这些页面合起来可以让用户创建、读取、更新和删除集合中的条目。这些动作统称为“CRUD”。 开发人员往往需要实现 CRUD,因此 Visual Studio 通常会设法对此提供帮助,以便生成具有 CRUD 操作动作方法的 MVC 控制器,同时也提供对这些操作进行支持的视图。

1.1 创建 CRUD 控制器

本文将创建一个新控制器来处理这些管理功能。 右击 SportsStore.WebUI 项目的 Controllers 文件夹,从弹出菜单中选择“Add Controller”,将该控制器命名为“AdminController”,模板为“空的MVC控制器”:

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 IProductRepository repository; public AdminController(IProductRepository repo)
{
repository = repo;
} public ViewResult Index()
{
return View(repository.Products);
}
}
}

1.2 创建新的布局

本例打算创建一个新的 Razor 布局,以用于 SportsStore 的管理视图。这是一个简单的布局,它提供一个单一的点,可以运用这个点形成所有管理视图。

为了创建该布局,新建布局页文件 Views/Shared/_AdminLayout.cshtml 。

正如前面所解释的那样,布局的命名约定是以一个下划线字符(_)作为首字母。微软的另一个叫做WebMatrix 的技术也使用 Razor ,它利用下划线来阻止浏览器请求布局页面。虽然 MVC 不需要这种防护,但这一约定被延用到了 MVC 应用程序。

在这个布局中,本文希望创建一个对 CSS 文件的引用,详细代码如下:

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/Admin.css" rel="stylesheet" type="text/css" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>

对应的 Admin.css 文件代码如下:

body, td { font-family: 'Segoe UI', Verdana; }
h1 { padding: .5em; padding-top:; 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; }

1.3 实现 List 视图

现在,已经创建了一个新的布局,下面可以对项目添加一个视图,用于 Admin 控制器的 Index 动作方法。新建对应的视图文件 Views/Admin/Index.cshtml 。

此处打算使用支架视图(Scaffold view),在这个视图中,Visual Studio 会为一个强类型视图所选择的类进行考察并创建一个视图,该视图含有为这个模型类型量身定制的标记(可见,所谓支架视图是让用户为视图选择一个模型类型,Visual Studio 会为这个模型创建一个含有相应标记的视图,这种视图则称为强类型视图)。谓词,从模型类列表中选择 Product ,并在“Scanffold template(支架模板)”中选择 List 。

注:当使用 List 支架时,Visual Studio 假设你要使用的是一个 IEnumerable 序列的模型视图类型,因此,用户只能从列表中选择一个单一的类。

自动生成的视图文件内容如下:

@model IEnumerable<SportsStore.Domain.Entities.Product>

@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h2>Index</h2> <p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Description)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th>
@Html.DisplayNameFor(model => model.Category)
</th>
<th></th>
</tr> @foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Description)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Category)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ProductID }) |
@Html.ActionLink("Details", "Details", new { id=item.ProductID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.ProductID })
</td>
</tr>
} </table>

Visual Studio 会考察视图模型对象的类型,并根据该模型所定义的属性,生成一些表格形式的元素。通过启动应用程序,导航到 Admin/Index 地址,可以看到视图是如何渲染的。其结果如下图所示:

这种支架视图为用户用了很好的设置工作。此时得到了 Product 类中各个属性的表格列,以及进行 CRUD 操作的链接,这些链接指向同一控制区中的各个动作方法。这些标题有些冗长,而且有些东西要与先抢创建的 CSS 联系起来。编辑这个 Index.cshtml 文件,代码如下:

@model IEnumerable<SportsStore.Domain.Entities.Product>

@{
ViewBag.Title = "Admin: All Products";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h1>All Products</h1> <table class="grid">
<tr>
<th>ID </th>
<th>Name</th>
<th class="NumericCol">Price </th>
<th>Actions</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="Delete" />} </td>
</tr>
} </table>
<p>@Html.ActionLink("Add a new product", "Create")</p>

现在 有了一个很好的列表页面。管理员可以看到分类中的产品,并有了进行添加、删除以及查看物品的链接和按钮。以下几节将添加一些功能以支持这些动作。

1.4 编辑产品

为了提供创建和更新特性,将添加一个产品编辑页面。此工作由两个部分:

* 显示一个让管理员能够修改产品属性值得页面

* 添加一个动作方法,它能够在递交时对这些修改进行处理

创建 Edit 动作方法

下面代码显示了添加到 AdminController 类中的 Edit 方法。这是在 Index 视图中调用 Html.ActionLink 辅助器方法时所指定的动作方法。

using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
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 IProductRepository repository; public AdminController(IProductRepository repo)
{
repository = repo;
} public ViewResult Index()
{
return View(repository.Products);
} public ViewResult Edit(int productId)
{
Product product = repository.Products.FirstOrDefault(p=>p.ProductID==productId);
return
View(product);
}

}
}

这个简单的方法找出与 productId 参数对应的 ID 的产品,并把它作为一个视图模型对象进行传递。

创建 Edit 视图

现在有了一个动作方法,于是可以为它创建一个视图以便渲染。添加对应的视图文件 Views/Admin/Edit.cshtml

@model SportsStore.Domain.Entities.Product

@{
ViewBag.Title = "Admin: Edit " + @Model.Name;
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h1>Edit @Model.Name</h1> @using (Html.BeginForm())
{
@Html.EditorForModel();
<input type="submit" value="Save" />
@Html.ActionLink("Cancel and return to List", "Index");
}

这个示例并未手工为每个标签和输入项编写标记,而是调用了 Html.EditorForModel 辅助器方法。这个方法要求 MVC 框架创建编辑器界面,这是通过探测其模型类型来实现的——在本例中,该模型类型时 Product 类。

要想看看这个 Edit 视图所生成的页面,可以运行应用程序,并导航到 /Admin/Index 。点击一个产品名,便会看到下图所示页面:

说实话,EditorForModel 方法很方便,但它产生的结果并不令人十分满意。此外,设计者通常不希望管理员可以看到或编辑 ProductID 属性,而且,用于 Description 的文本框太小了。

可以通过使用模型元数据为 MVC 框架提供一些指示,告诉框架如何为属性创建编辑器(这里的“属性编辑器”是指在 HTML 页面上对某一属性进行数据录入或编辑的界面)。这让用户能够将注解属性运用于这个新模型类的属性上,以影响 Html.EditorForModel 方法的输出。下面代码演示了如何在 SportsStore.Domain 项目中的 Product 类上使用元数据:

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; }
}
}

HiddenInput 注解属性告诉 MVC 框架,将该属性渲染为隐藏的表单元素,而 DataType 注解属性能够只是如何显示或编辑一个值。这个示例选择了 MultilineText 选项。HiddenInput 注解属性属于 System.Web.Mvc 命名空间,而 DataType 注解属性包含在 System.ComponentModel.DataAnnotations 命名空间中。

下图显示了运用元数据后的 Edit 页面,此时已不再能看到或编辑 ProductID 属性了,而且有了一个输入 description 的多行文本。然而,这个 UI 看上去还是不够理想。

可以用 CSS 做一些简单的改善。当 MVC 框架为每个属性创建 input 字段时,它给这些 input 赋予了不同的 CSS 的 class 值。当产看上图页面的源代码时便可以看到,为产品描述创建的 textarea 元素被赋予了 "text-box multi-line"这一 CSS 的 class 值。

<textarea name="Description" id="Description" class="text-box multi-line">A boat for one person</textarea>

其他 HTML 元素也被赋予了类似的 class 值,于是可以改善 Edit 视图的外观。添加 CSS 代码如下:

.editor-field {margin-bottom:.8em;}
.editor-label {font-weight:bold;}
.editor-label:after {content:":";}
.text-box {width:25em;}
.multi-line {height:5em;font-family:'Segoe UI',Verdana;}

下图显示了这些样式在 Edit 视图上所具有的效果。所渲染的视图仍然是很基本的,但它的功能已具备了管理的需要。

正如在这个例子中所看到的,EditorForModel 这样的模板视图辅助器方法所创建的页面并不总能满足开发者。

更新产品存储库

在能够处理编辑之前,还需要增强产品存储库,才能够保存所做的修改。首先,要对 IProductRepository 接口添加一个新的方法:

using SportsStore.Domain.Entities;
using System.Linq; namespace SportsStore.Domain.Abstract
{
public interface IProductRepository
{
IQueryable<Product> Products { get; }
void SaveProduct(Product product);
}
}

然后,将这个方法添加到存储库的 Entity Framework 实现上,即 Concrete/EFProductRepository 类,代码如下:

using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System.Linq; namespace SportsStore.Domain.Concrete
{
public class EFProductRepository : IProductRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
} public void SaveProduct(Product product)
{
if (product.ProductID == )
{
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();
}

}
}

如果 ProductID 为0,这一 SaveChanges 方法实现会将一个产品加入存储库(添加产品);否则,会将任何修改用于与数据库中已存在的物品(修改产品)。

总所周知,当接收到到 ProductID 不为 0 的 Product 参数时,需要执行更新。其实现方法是,从存储库中获取同样 ProductID 的 Product 对象,并更新每个属性,以使这些属性与参数对象吻合。

这么做是因为 Entity Framework 会对数据库创建的对象保持跟踪。传递给 SaveChanges 方法的对象是由 MVC Framework 使用默认的模型绑定器创建的,这意味着, Entity Framework 实体框架不知道任何关于参数对象的事情,因而不会对数据库运用更新。解决这一问题的方式有很多,本文采取了最简单的一个,即对 Entity Framework 已知的相应的对象进行定位,并明确地对它进行更新。

另一种办法是,可以创建一个只从存储库中获取对象的自定义模型绑定器。这可能看起来像是一种更优雅地办法,但它需要对存储库接口添加查找功能,才能通过 ProductID 值对 Product 对象进行定位。

这种办法的缺点是,为了解决一个具体实现的局限性,必须从存储库的抽象定义开始添加功能。如果将来切换了存储库实现,所冒的风险则是,新的存储技术或许不能真正支持用户所实现的这种查找功能。这可能会诱使用户使用 MVC Framework 的灵活性,来避免类似于 SaveProduct 方法中的这些变通方案,但这么做可能会违背用户应用程序的设计原则。

处理 Edit 的 POST 请求

此刻已经做好了准备,以便在 Admin 控制器中实现一个重载的 Edit 动作方法,他将在管理员点击“Save”按钮时处理 POST 请求。这个方法如下:

using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
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 IProductRepository repository; public AdminController(IProductRepository repo)
{
repository = repo;
} public ViewResult Index()
{
return View(repository.Products);
} public ViewResult Edit(int productId)
{
Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId);
return View(product);
} [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
{
//数据值有误
return
View(product);
}
}

}
}

该方法通过读取 ModelState.IsValid 属性的值,检查了模型绑定器已经能够验证用户递交的数据(注:模型绑定器绑定用户递交的数据、形成 Product 对象,并将该对象传递给 Edit 方法,如果这一系列活动都正常,则 ModelState.IsValid 为真)。如果一切正常,便将这些修改保存到存储库,然后调用 Index 动作方法,让用户返回到产品列表。如果数据有问题,则再次渲染 Edit 视图,以使用户能够进行修正。

在存储库中保存了这些修改之后,用 Temp Data (临时数据)特性保存了一条消息。这是一个“键/值”字典,它类似于之前已经用过的会话数据和 View Bag(视图包)特性。与会话数据的关键差别是,Temp Data 在HTTP 请求结束时会被删除。

注意, Edit 方法返回的是 ActionResult 类型。到目前为止,此书一直使用的都是 ViewResult 类型。View Result 派生于 ActionResult ,而且它是在用户希望框架去渲染一个视图时使用的。然而,其他类型的 ActionResult 也是可用的, RedirectToAction 方法所返回的便是其中之一(即,有好几种方法都返回 ActionResult 类型, RedirectToAction 的返回类型就是 ActionResult 类型)。这里在 Edit 动作方法中用它去调用 Index 动作方法。

这种情况下不能使用 ViewBag,这是因为用户被重定向了。 ViewBag 在控制器与视图之间传递数据,但它保存数据的时间不能比当前 HTTP 请求长(注意,重定向意味着用户是跨请求的,而 ViewBag 不能用于跨请求情况下控制器与视图之间的数据传递)。读者可以使用会话数据特性,但其消息是持久的,知道它被明确地删除位置,那还不如不用它(可见会话数据占用服务器资源,且需要维护,故这里不用它)。因此,Temp Dat 特性是十分合适的。其数据被限制到一个单一用户的会话(于是用户不会看到相互的 TempData),并且将会一直保存到被读取为止。在动作方法渲染的视图中,把这些数据读给已经被重定向的用户(此时,TempData 保保持的数据也就自然消亡)。

显示确认消息

本文打算在 _AdminLayout.cshtml 布局文件中处理 TempData 存储的消息。通过在模板中处理消息,可以在任何使用此模板的视图中创建消息,而不需要创建额外的 Razor 块。下面是对此文件的修改:

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/Admin.css" rel="stylesheet" type="text/css" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@if (TempData["message"] != null)
{
<div class="Message">@TempData["message"]</div>

}

@RenderBody()
</div>
</body>
</html>

提示:像这样在模板中处理消息的好处是。在用户保存了修改之后,可以看到它显示在任何渲染页面上。此刻,消息被返回给产品列表,但可以改变此工作流程去渲染一些其他视图,而用户将仍然可以看到这些消息(只要下一个书体也使用同样的布局)。

现在有了对编辑产品进行测试的所有元素。运行此应用程序,导航到 Admin/Index ,并进行编辑,点击“Save”按钮,系统将返回到列表系统,而 TempData 消息也将被显示出来,如下图所示:

如果刷新产品列表屏幕,这条消息将会消失,这是因为 TempData 在读取它时被删除了。这是很方便的,因此此处不希望还会残留过时的消息。

添加模型验证

通常情况总是这样,需要对模型实体添加验证规则。此刻,管理员可以输入负数价格或空白产品描述,那么 SportsStore 一样会把这些数据存储到数据库中(这当然不行,所以要添加验证规则)。下面代码演示了如何把数据注解属性(Data Annotations Attributes)运用于 Product 类:

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc; namespace SportsStore.Domain.Entities
{
public class Product
{
[HiddenInput(DisplayValue = false)]
public int ProductID { get; set; } [Required(ErrorMessage = "Please enter a product name")]
public string Name { get; set; } [DataType(DataType.MultilineText)]
[Required(ErrorMessage = "Please enter a description")]
public string Description { get; set; } [Required]
[Range(
0.01, double.MaxValue, ErrorMessage = "Please enter a positive price")]
public decimal Price { get; set; } [Required(ErrorMessage = "Please specify a category")]
public string Category { get; set; }
}
}

在使用 Html.EditorForModel 辅助器方法来创建 form 元素以编辑 Product 时, MVC 框架添加了显示内联的验证错误所需的所有标记和 CSS 。当编辑一个产品,而输入的数据违背了上面代码所运用的验证规则时,会出现下图所示的界面。

启用客户端验证

此时,只有当管理员把编辑递交给服务器时,才会运用数据验证。大多数 Web 用户期望,如果输入的数据有问题,要立即得到反馈。这就是 Web 开发人员经常希望执行客户端验证的原因,此时,数据在浏览器中用 JavaScript 进行检查。 MVC 框架可以根据运用于域模型类的数据来执行客户端验证。

这一特性是默认可用的,但它上不会生效,因为还没有添加对所需的 Javascript 库的链接。最简单的办法是在 _AdminLayout.cshtml 文件中添加这些链接,这样,客户端验证便能在使用这个布局的任何页面上起作用。修改如下:

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/Admin.css" rel="stylesheet" type="text/css" />
<script src="~/Scripts/jquery-1.7.1.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<title>@ViewBag.Title</title>
</head>
<body>
<div>
@if (TempData["message"] != null)
{
<div class="Message">@TempData["message"]</div>
}
@RenderBody()
</div>
</body>
</html>

通过这些添加,客户端验证将对管理视图生效。显示给用户的错误消息的外观是相同的,因为服务器验证所使用的 CSS 的class 也有客户端验证所使用。但得到的响应时及时的,而且不需要把请求发送到服务器。在大多数情况下,客户端验证是一个有用的特性,但如果出于某种原因不希望在客户端验证,则需要使用以下语句。

...
HtmlHelper.ClientValidationEnabled = false;
HtmlHelper.UnobtrusiveJavaScriptEnabled = false;
...

如果把这些语句放在一个视图或一个控制器中,那么客户端验证只针对当前动作失效。要禁用整个应用程序的客户端验证,可以再 Global.asax 的 Application_Start 方法中使用这些语句,或是把这些值运用于 Web.config 文件,使用代码如下:

<appSettings>
<add key="ClientValidationEnabled" value="false" />
<add key="UnobtrusiveJavaScriptEnabled" value="false" />
</appSettings>

1.5 创建新产品

下一步将实现 Create 动作方法,这是在产品列表页面中“Add a new product”链接所指定的方法。它允许管理员把一个新物品添加到产品分类。添加创建新产品的能力只需要一个小的附件,并对应用程序做出一点小改动即可。这是精心构思 MVC 应用程序功能和适应新的一个很好的例子。首先,对 AdminController 类添加 Create 方法:

 public ViewResult Create()
{
return View("Edit", new Product());
}

这个 Create 方法并不渲染它的默认视图,而只是指明应该使用 Edit 视图。让一个动作方法去使用一个通常与另一个视图关联的视图是完全可以的。这里注入了一个新的 Product 对象作为视图模型,以便 Edit 视图用空字段进行填充。

这使用户能够修改这个空白的 Product 对象。通常,用户期望一个表单会会递给渲染它的动作,而这是 Html.BeginForm 在生成一个 HTML 表单时所假设的默认情况。然而 Create 方法并不是这样,因为用户希望将此表单回递给 Edit 动作,以便可以保持这个新创建的产品数据。为了对此进行修正(Create 动作方法调用了Edit 视图,当用户在此视图的表单中编辑数据然后进行递交时,默认会被回递给 Create 动作方法,但用户希望表单被回递给 Edit 动作方法,故需要修正),可以用重载的 Html.BeginForm 辅助器方法加以指明:在 Edit 视图中生成的表单的目标(始终)是 Admin 控制器的 Edit 动作方法,如下代码,它说明了对 Views/Admin/Edit.cshtml 视图文件所做的修改。

@model SportsStore.Domain.Entities.Product

@{
ViewBag.Title = "Admin: Edit " + @Model.Name;
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h1>Edit @Model.Name</h1> @using (Html.BeginForm("Edit", "Admin"))
{
@Html.EditorForModel();
<input type="submit" value="Save" />
@Html.ActionLink("Cancel and return to List", "Index");
}

经此修正之后,此表单将总是被递交给 Edit 动作,而不管渲染它的是哪个动作。

1.6 删除产品

添加对物品进行删除的支持相当简单。首先把一个新方法添加到 IProductRepository 接口,如下所示:

using SportsStore.Domain.Entities;
using System.Linq; namespace SportsStore.Domain.Abstract
{
public interface IProductRepository
{
IQueryable<Product> Products { get; } void SaveProduct(Product product); Product DeleteProduct(int productID);
}
}

下一步,在 Entity Framework 存储库类 EFProductRepository 中实现这个方法:

using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System.Linq; namespace SportsStore.Domain.Concrete
{
public class EFProductRepository : IProductRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
} public void SaveProduct(Product product)
{
if (product.ProductID == )
{
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();
} public Product DeleteProduct(int productID)
{
Product dbEntry = context.Products.Find(productID);
if (dbEntry != null)
{
context.Products.Remove(dbEntry);
context.SaveChanges();
}
return
dbEntry;
}

}
}

最后一步实在 Admin 控制器中实现一个 Delete 动作方法。这个动作方法应当只支持 POST 请求,因为删除对象不是一个幂等的(idempotent)操作。浏览器和缓存会随意的形成 GET 请求而不需要用户明确的同意,因此,必须小心地避免形成 GET 请求的结果,下面代码演示了新的动作方法:

     [HttpPost]
public ActionResult Delete(int productId)
{
Product deletedProduct = repository.DeleteProduct(productId);
if (deletedProduct != null)
{
TempData["message"] = string.Format("{0} was deleted", deletedProduct.Name);
}
return RedirectToAction("Index");
}

简单的点击产品列表页面中的一个“Delete”按钮,可以看到这个新功能已起作用,如下图所示:

【MVC 4】8.SportsSore:管理的更多相关文章

  1. 利用反射和泛型把Model对象按行储存进数据库以及按行取出然后转换成Model 类实例 MVC网站通用配置项管理

    利用反射和泛型把Model对象按行储存进数据库以及按行取出然后转换成Model 类实例 MVC网站通用配置项管理   2018-3-10 15:18 | 发布:Admin | 分类:代码库 | 评论: ...

  2. EasyUI+MVC+EF简单用户管理Demo(问题及解决)

    写在前面 iframe-src EntityFramework版本 connectionStrings View.Action.页面跳转 EasyUI中DataGrid绑定 新增.修改和删除数据 效果 ...

  3. [.Net MVC] 用户角色权限管理_使用CLK.AspNet.Identity

    项目:后台管理平台 意义:一个完整的管理平台需要提供用户注册.登录等功能,以及认证和授权功能. 一.为何使用CLK.AspNet.Identity 首先简要说明所采取的权限控制方式.这里采用了基于角色 ...

  4. 用标准Struts2+mvc写的用户管理

    这星期的实验,最终调好了. 一句话,麻雀虽小,五脏俱全.相信刚学struts2的同学能够通过该实验能够更好地理解struts的结构和mvc 登录的之前写过,这里直接进入用户管理 用struts2都要在 ...

  5. asp.net mvc +easyui 实现权限管理(二)

    一写完后,好久没有继续写了.最近公司又在重新开发权限系统了,但是由于我人微言轻,无法阻止他们设计一个太监版的权限系统.想想确实是官大一级压死人啊, 没办法我只好不参与了 让他们去折腾. 我就大概说一下 ...

  6. MVC应用程序中管理(更新)上传的文件

    实现上传文件功能,有时上传也会操作出错,能让用户有改正有机会,开发上传文件能有更新的功能. 文件上传时,如果是存储于应用程序某一目录的话,在更新时需要了解一些流程,先是删除旧文件,更新数据表相关信息, ...

  7. 一步一步Asp.Net MVC系列_权限管理总结(附MVC权限管理系统源码)

    在上一节中我们总结了关于权限控制的方式,我们这一节讲解关于权限控制中角色权限的授予处理等等并做本系列的总结. 首先,我们来谈谈权限控制中角色权限的控制,上一节只是针对权限拦截中比较粗的控制,如果我们需 ...

  8. MVC设计模式实现权限管理登录,超详细

    功能实现:在页面输入给定的用户名之一,可以显示当前用户的权限,也可以在页面更改该用户的权限,更新之后保存.像下面这样. 填写用户名提交: 显示用户AAA的权限: 修改权限(增加article3): 点 ...

  9. MVC + LigerUI 做后台管理还真是清爽

    LigerUI是基于Jquery,轻量级UI框架.具体可以看官方演示 http://www.ligerui.com/ 我的简单后台 模拟Winodw桌面效果,挺不错呢.最喜欢的还是他的,下拉列表绑定G ...

  10. 一步一步Asp.Net MVC系列_权限管理设计

    http://www.cnblogs.com/mysweet/archive/2012/07/26/2610793.html

随机推荐

  1. jython 2.7 b3发布

    Jython 2.7b3 Bugs Fixed - [ 2108 ] Cannot set attribute to instances of AST/PythonTree (blocks pyfla ...

  2. FreeBSD的新一代包管理工具Pkg 1.3.0 发布

    Pkg 是 FreeBSD 下一代的打包管理工具,用来替代原来的 pkg_info/pkg_create/pkg_add 工具,可以本地打包,也提供远程打包,主要是为了方便远程二进制包升级. Pkg ...

  3. 访问WEB-INFO 目录注意事项

    WEB-INF下面的内容都是只能由服务器级别才能访问,客户端并不能访问.什么是客户端级别?什么是服务器级别呢? 转发就是服务器级别,浏览器的地址不会变,因为,客户端发送一个请求,服务器受理之后,发现要 ...

  4. [Xamarin.Android] 使用Component套件

    [Xamarin.Android] 使用Component套件 前言 在Xamarin中,可以将自己开发的项目包装成为Component套件发布至Xamarin Component Store,来提供 ...

  5. elasticsearch的mapping映射

    Mapping简述 Elasticsearch是一个schema-less的系统,但并不代表no shema,而是会尽量根据JSON源数据的基础类型猜测你想要的字段类型映射.Elasticsearch ...

  6. jquery动态创建节点

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. HTML中图片热区的使用

    在HTML中有一个具有把图片划分成多个作用区域,并链接到不同网页的标记,那就是 <area>地图作用区域标记. <area>标记主要用于图像地图,通过该标记可以在图像地图中设定 ...

  8. Sharepoint学习笔记—习题系列--70-573习题解析 -(Q60-Q62)

    Question 60You have a SharePoint site collection that contains 100 subsites.You plan to create a Web ...

  9. Android 开源库和项目

    1.手势解锁 史上最完美的 手势密码解锁 2.数据库操作 Android数据库框架itePal https://github.com/LitePalFramework/LitePal 轻量级数据库:a ...

  10. Android源码分析之MessageQueue

    下面让我们花些时间来看看MessageQueue的具体实现,不过在分析代码之前让我们来理解下在类开头的一大段comments. MessageQueue是比较低层的类,是持有Message(在Loop ...