《Pro ASP.NET MVC 3 Framework》学习笔记之十七【示例项目SportsStore】

接下来是关于SportsStore的后台管理功能,也就是通常的CRUD操作。
首先添加一个AdminController,代码如下:

View Code

using System.Web.Mvc; using SportsStore.Domain.Abstract; 

namespace SportsStore.WebUI.Controllers { 

public class AdminController : Controller { private IProductRepository repository; 

public AdminController(IProductRepository repo) {             repository = repo;         }     } }

我们通过一个List page来展示已有的products,接着在AdminController里面添加一个Action:

View Code

using System.Web.Mvc; using SportsStore.Domain.Abstract; 

namespace SportsStore.WebUI.Controllers { 

public class AdminController : Controller { private IProductRepository repository; 

public AdminController(IProductRepository repo) {             repository = repo;         } 

public ViewResult Index() { return View(repository.Products);         }     } }

为我们的后台管理界面创建一个布局_AdminLayout.cshtml,在 Views/Shared上右键添加新项MVC 3 Layout Page

View Code

<!DOCTYPE html> 

<html> <head>     <title>@ViewBag.Title</title>     <link href="@Url.Content("~/Content/Admin.css")" rel="stylesheet" type="text/css" /> </head> <body>     <div>         @RenderBody()     </div> </body> </html>

接着添加一个样式表单Admin.css,如下所示:

View Code

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

添加Index视图:

View Code

@model IEnumerable<SportsStore.Domain.Entities.Product>@{    ViewBag.Title = "Index";    Layout = "~/Views/Shared/_AdminLayout.cshtml";}<h1>    All Products</h1><table class="Grid">    <tr>        <th>            ID        </th>        <th>            Name        </th>        <td>            ShowImg        </td>        <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>                <img src="/UploadImgs/@item.ImageURL" alt="img" id="@item.ProductID" width="100" height="100" />            </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>

这里对数据库有改动,需要增加一个字段ImageURL(varchar(255)),并且在我们的SportsStore.WebUI项目新添加一个文件夹UploadImgs用来存放上传的图片。

编辑Product

创建一个Edit Action

View Code

public ViewResult Edit(int productId) {     Product product = repository.Products.FirstOrDefault(p => p.ProductID == productId); return View(product); }

创建Edit View,强类型的并引用了_AdminLayout.cshtml,

如下:

View Code

@model SportsStore.Domain.Entities.Product@{    ViewBag.Title = "Admin:Edit" + @Model.Name;    Layout = "~/Views/Shared/_AdminLayout.cshtml";}<h2>    Edit @Model.Name</h2>@using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" })){    @Html.EditorForModel()    <div class="editor-label">        Image</div>    <div class="editor-field">        @if (Model.ImageURL == null)        {            @:None        }else        {            <img width="150" height="150" alt="img" src="/UploadImgs/@Model.ImageURL" />        }        <div>            Upload new image:<input type="file" name="Image" /></div>    </div>    <input type="submit" value="Save" />    @Html.ActionLink("Cancel and return to list", "Index");}

如果运行程序,这里并不会显示ProductID,因为这里没有必要显示出来。实现这个效果需要在Product实体里面使用模型元数据(model metedata)。它能让我们应用Attributes到Product的属性上面来影响Html.EditorForModel()方法输出的布局。

如下:

View Code

    public class Product    {//通过这个属性来在隐藏ProductID        [HiddenInput(DisplayValue = false)]public int ProductID { get; set; }

[Required(ErrorMessage = "Please enter a product name")]public string Name { get; set; }

[Required(ErrorMessage = "Please enter a description")]        [DataType(DataType.MultilineText)]public string Description { get; set; }

[Required]        [Range(typeof(decimal), "0.01", "999999999999999", ErrorMessage = "Please enter a positive price")]public decimal Price { get; set; }

[Required(ErrorMessage = "Please specify a category")]public string Category { get; set; }

public string ImageURL { get; set; }    }

HiddenInput特性让MVC框架将该属性作为隐藏表单元素呈现,DataType特性允许我们指定一个值怎样显示或编辑。如这里将Description显示为MultilineText(多行文本)
这里需要添加一个引用System.Web.Mvc。

接着添加一些样式到Admin.css里面,如下:

View Code

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

这里看发现css里面的类名如.editor-field,我们并没有在View里面定义一个class属性,这个是MVC框架生成的,你可以查看页面源文件。而且ID都是属性名,这是模型绑定的结果,在前面的笔记有这些内容的。

为了编辑Product,我们需要添加相应的Repository和接口。在IProductsRepository里面添加一个方法:void SaveProduct(Product product);
接着实现该接口,如下:

View Code

    public class ProductsRepository : IProductsRepository    {        ISqlMapper mapper = MapperHelper.Instance();public IList<Product> Products        {get            {

return mapper.QueryForList<Product>("Product-Select", "");            }        }public void SaveProduct(Product product)        {            mapper.Update("Product-Update", product);        }

public void AddProduct(Product product)        {            mapper.Insert("Product-Insert", product);        }

public void DeleteProduct(Product product)        {            mapper.Delete("Product-Delete", product);        }    }

初次路过的朋友可能会惊讶这里是什么东东,书中用的EF框架+MS SQL Server。我学着用的是iBatisnet+MySQL。如果你对iBatisnet不了解,可以先用下iBatisnet,上手还算比较容易。你也可以看看我前面笔记之十二,里面讲了我这里的使用情况。

这里我们需要在iBatisnet的映射文件Product.xml添加update语句,如下:

View Code

<?xml version="1.0" encoding="utf-8" ?><sqlMap namespace="Product"        xmlns="http://ibatis.apache.org/mapping"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >  <alias>    <typeAlias alias="Product" type="SportsStore.Domain.Entities.Product,SportsStore.Domain"></typeAlias>  </alias>

<resultMaps>    <resultMap id="Product-Result" class="Product">      <result property="ProductID" column="ProductID"/>      <result property="Name" column="Name"/>      <result property="Description" column="Description"/>      <result property="Category" column="Category"/>      <result property="Price" column="Price" nullValue="0.00" dbType="decimal"/>      <result property="ImageURL" column="ImageURL" nullValue=""/>    </resultMap>  </resultMaps>

<statements>    <select id="Product-Select" parameterClass="Product" resultMap="Product-Result">select * from products    </select>

<insert id="Product-Insert" parameterClass="Product">      insert into products(Name,Description,Category,Price,ImageURL) values      (      #Name#,#Description#,#Category#,#Price#,#ImageURL#      )      <selectKey resultClass="int" property="ProductID" type="post">select @@IDENTITY      </selectKey>    </insert>

<update id="Product-Update" parameterClass="Product">      update products set Name=#Name#,Description=#Description#,Category=#Category#,Price=#Price#,ImageURL=#ImageURL#where      ProductID=#ProductID#    </update>

<delete id="Product-Delete" parameterClass="Product">      delete from products where ProductID=#ProductID#    </delete>  </statements>

</sqlMap>

接着在AdminController里面重载一个处理HTTP Edit请求的action方法,如下:

        [HttpPost]public ActionResult Edit(Product product, HttpPostedFileBase image)//HttpPostedFileBase提供了对上传的文件单独访问        {if (ModelState.IsValid)//如果验证通过才执行编辑            {if (image != null)                {string path = @"D:\Study\Projects\SportsStore\SportsStore.WebUI\UploadImgs\" + image.FileName;                    image.SaveAs(path);//保存图片                    product.ImageURL = image.FileName;

}                //接着执行保存的方法                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);            }        }

当验证通过,会执行保存。这里我使用了TempData存储了一条提示信息,TempData是MVC框架提供的一个非常爽的功能。它是一个Dictionary的键值对,类似于Session和ViewBag。有一个关键的不同点是TempData会在HTTP请求结束被删除。

从上面的代码我们注意下Edit方法的返回值类型ActionResult.前面我使用大多是ViewResult。ViewResult是从ActionResult派生的,当我们想呈现一个View时就可以使用ViewResult的返回值类型。当然,ActionResult的其他类型也是可用的,其中一个就是RedirectToAction方法的返回值类型。

这里我们不能使用ViewBag,因为用户被重定向了,ViewBag在Controller和View传递数据并且ViewBag保存数据的时间不会比当前的Http请求,也就是说可能在Http请求没有结束时ViewBag里面数据已经丢失了,这就达不到我们要的效果。当然Session可以保存,但是Session会一直保存(除非服务器内存不足或是过期),直到我们显示的移除它,当然我们不情愿这样做。MVC框架提供的TempData功能就很完美的符合这里显示提示信息的要求。而且TempData里面的数据是限制在单个用户会话里面的(用户之前是不能相互查看对方的TempData),并且会一直保存知道我们读取了以后才被删除。我们将会在使用重定向到用户的action方法呈现View时读取TempData的数据。

接着我们在_AdminLayout里面使用TempData,为的是能够在引入了_AdminLayout的所以View里面使用。修改_AdminLayout.cshtml的代码如下:

View Code

<!DOCTYPE html><html><head>    <title>@ViewBag.Title</title>    <link href="@Url.Content("~/Content/Admin.css")" rel="Stylesheet" type="text/css" />    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script></head><body>    <div>        @if (TempData["message"] != null)        {            <div class="Message">@TempData["message"]</div>         }        @RenderBody()    </div></body></html>

上面我们引入了几个js文件是用来进行客户端验证的,这又是MVC框架提供给我们的一个非常爽的功能,我只需要在Product.cs里面为属性添加我需要限制的Attributes就能够实现验证用户输入的目的,引入了上面的两个js文件后,默认是开启了客户端验证的。当然服务器端验证是一定会有的。在大多数情况下,我们都希望进行客户端验证,因为这样会有一个比较好的用户体验。

但是如果我们不想进行客户端验证则可以这样做,则可以使用这两句代码:

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

如果我们将这两句代码放到一个View或Controller里面,那客户端验证对当前的Action就不会使用。

我们也可以如果在配置文件里面配置来禁止整个程序使用客户端验证,如下配置:

View Code

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

接着我们实现创建一个新的Product,添加两个Create() action方法,一个是Get请求(没有加HttpGet Attribute表示默认Get),一个是处理HttpGet请求。我们呈现添加页面的时候调用的是Get,提交时调用Post。在AdminController里面添加的代码如下:

View Code

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

[HttpPost]public ActionResult Create(Product product)        {if (ModelState.IsValid)            {                repository.AddProduct(product);                TempData["message"] = string.Format("{0} has been added", product.Name);return RedirectToAction("Index");            }else            {return View(product);            }        }

接着右键添加Create.cshtml,代码如下:

View Code

@model SportsStore.Domain.Entities.Product@{    ViewBag.Title = "Create";    Layout = "~/Views/Shared/_AdminLayout.cshtml";}<h2>    Create</h2>@using (Html.BeginForm()){    @Html.EditorForModel()    <input type="submit" value="Add" />}

实现删除Product操作

首先添加一个接口方法:void DeleteProduct(Product product);
接着实现该方法

View Code

    public class ProductsRepository : IProductsRepository    {        ISqlMapper mapper = MapperHelper.Instance();public IList<Product> Products        {get            {

return mapper.QueryForList<Product>("Product-Select", "");            }        }public void SaveProduct(Product product)        {            mapper.Update("Product-Update", product);        }

public void AddProduct(Product product)        {            mapper.Insert("Product-Insert", product);        }

public void DeleteProduct(Product product)        {            mapper.Delete("Product-Delete", product);        }    }

关于iBatisnet的映射文件,在上面已经全部列出来了,所以这里就不在展示了。接着是创建Delete action方法,如下:

View Code

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

 

接着实现后台管理的安全性设置

asp.net mvc是建立在asp.net核心平台上的,所以我们可以访问asp.net form验证的功能,这也是一个比较通用的功能。只有登录成功的人才能操作后台。关于From验证,在书的22章会详细讲解。这里只是介绍了最基本的配置。在配置文件里面添加如下代码:

View Code

<authentication mode="Forms">   <forms loginUrl="~/Account/LogOn" timeout="2880">     <credentials passwordFormat="Clear">       <user name="admin" password="secret" />     </credentials>   </forms> </authentication>  <!--这里的timeout是2880分钟即48小时,这里我们硬编码用户名和密码,实际情况肯定不是这样。-->

MVC框架有一个非常强大的功能称为filters。这些都是Attributes,应用到一个action方法或Controller类。当一个请求被处理的时候,这些Attributes引入了额外的逻辑。我们也可以定义自己的filter。

这部分内容会在13章详细介绍的,这里大概知道就行。其实我理解这里就是,filter功能用来实现权限控制,哪些action是需要权限的,只允许哪些用户方法的等等。当添加了这些filter后,处理请求的时候就会做额外的判断。

我们这里只是用到了Authorize。我们在AdminController类的上面添加这个filter,如下所示:

View Code

using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using System.Linq; 

namespace SportsStore.WebUI.Controllers { 

[Authorize] public class AdminController : Controller { private IProductRepository repository; 

public AdminController(IProductRepository repo) {             repository = repo;         } ... 

这样就表示只有通过验证的才可能访问该Controller,当然我可以细化到针对一个action方法。

这个时候运行程序,通过/Admin/Index URL进行后台会报错,因为并不存在Account/LogOn.

下面我们开始实现登录验证功能,在Infrastructure文件夹里面再创建一个Abstract文件夹,在该文件夹里面添加一个接口IAuthProvider,代码如下:

View Code

namespace SportsStore.WebUI.Infrastructure.Abstract { public interface IAuthProvider { 

bool Authenticate(string username, string password);     } } 

接着在Infrastructure文件夹里面再创建一个Concrete文件夹,添加一个实现类FormsAuthProvider.如下:

View Code

using System.Web.Security; using SportsStore.WebUI.Infrastructure.Abstract; 

namespace SportsStore.WebUI.Infrastructure.Concrete { 

public class FormsAuthProvider : IAuthProvider { 

public bool Authenticate(string username, string password) { 

bool result = FormsAuthentication.Authenticate(username, password);if (result) {                 FormsAuthentication.SetAuthCookie(username, false);             } return result;         }     } } 

然后使用Ninject添加绑定,如下:

        private void AddBindings()        {            ninjectKernel.Bind<IProductsRepository>().To<ProductsRepository>();            EmailSettings emailSettings = new EmailSettings {            WriteAsFile = bool.Parse(ConfigurationManager.AppSettings["Email.WriteAsFile"] ?? "false") };            ninjectKernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>().WithConstructorArgument("settings", emailSettings); ninjectKernel.Bind<IAuthProvider>().To<FormsAuthProvider>();        }

接着创建一个AccountController和LogOn action方法,实际上是重载的两个LogOn方法,一个处理Get呈现,一个处理Post提交验证。这之前我们需要创建一个view model来传递。在SportsStore.WebUI/Models里面添加一个新的类LogOnViewModel,代码如下:

View Code

using System.ComponentModel.DataAnnotations; 

namespace SportsStore.WebUI.Models { 

public class LogOnViewModel {         [Required] public string UserName { get; set; } 

[Required]         [DataType(DataType.Password)] public string Password { get; set; }     } } 

接着我们添加AccountController,如下:

View Code

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using SportsStore.WebUI.Infrastructure.Abstract;using SportsStore.WebUI.Models;

namespace SportsStore.WebUI.Controllers{public class AccountController : Controller    {private IAuthProvider authProvider;public AccountController(IAuthProvider auth)        {            authProvider = auth;        }public ViewResult LogOn()        {return View();        }        [HttpPost]public ActionResult LogOn(LogOnViewModel model, string returnUrl)        {if (ModelState.IsValid)            {if (authProvider.Authentication(model.UserName, model.Password))                {return Redirect(returnUrl ?? Url.Action("Index", "Admin"));                }else                {                    ModelState.AddModelError("", "Incorrect username or password");return View();                }            }else            {return View();            }

}

}}

右键添加LogOn View,如下:

View Code

@model SportsStore.WebUI.Models.LogOnViewModel

@{    ViewBag.Title = "LogOn";    Layout = "~/Views/Shared/_AdminLayout.cshtml";}

<h2>Log In</h2><p>Please log in to access the administrator area:</p>@using (Html.BeginForm()){    @Html.ValidationSummary(true)    @Html.EditorForModel()    <p><input type="submit" value="Log in" /></p> }

到这里,整个项目就结束了,呵呵!通过这个项目对MVC的理解应该加深了,后面进入到第二部分MVC3框架的讲解。因为项目后面对数据库进行了更改,所以你需要自己在相关的View里面添加显示图片的代码。如果你是按照我的笔记实际操作,遇到任何问题请及时留言!

好了,今天的笔记就到这里,祝大家周末愉快!公司如果这几天有年会的,祝大家都有好运中奖!

时间: 2024-09-16 12:59:53

《Pro ASP.NET MVC 3 Framework》学习笔记之十七【示例项目SportsStore】的相关文章

ASP.NET MVC 3 Framework学习笔记之Model Templates

.使用模板化的视图Helpers(Using Templated View Helpers) 模版化视图helpers的创意就是它们更加灵活.我们不用自己去指定应该用什么HTML元素来呈现一个模型的属性,MVC自己会搞定,在我们更新了视图模型时,也不用手动的更新视图.下面是一个例子:  代码如下 复制代码 //在Models里面添加Persons.cs using System; using System.Collections.Generic; using System.Linq; using

《Pro ASP.NET MVC 3 Framework》学习笔记目录

<Pro ASP.NET MVC 3 Framework>简介: 作者: Adam Freeman 和 Steven Sanderson 出版社: Apress; New 平装: 820页 语种: 英语 ISBN: 1430234040 声明:笔记里面按我自己的理解翻译了大部分内容,写这个笔记的目的:为了方便自己查阅,也为园友提供学习的方便. 我无意侵犯作者的任何权利,仅仅为了自己学习.也希望路过的朋友不要用于任何商业目的. 第一部分 ASP.NET MVC3介绍   <Pro ASP.

《Pro ASP.NET MVC 3 Framework》学习笔记之一【MVC的历程,优点,HelloWorld】

序论:asp.net mvc出现已经有两三年的时间了(2009开始1.0版本),但是这么方面的中文学习资料仍然非常少,特别是asp.net mvc3,几乎就没有中文的学习书籍.在英文的书籍中有两本是非常经典的mvc3教程:<Professional ASP.NET MVC 3>--作者:Jon Galloway , Phil Haack, Brad Wilson , K. Scott Allen和<Pro ASP.NET MVC 3 Framework>--作者:Steven Sa

ASP.NET MVC Web API 学习笔记----HttpClient简介

  1. HttpClient简单介绍  依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient,用于接收HttpResponseMessage和发送HttpRequestMesssage. 问题在于既然WebClient,HttpWebRequest可以完成相应的功能,为什么还要使用HttpClient类,.NET Framework中既然提出了这样一个类肯定是有其特别之处的,这里罗列几个不同之处: (

ASP.NET MVC Web API 学习笔记---联系人增删改查

本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系人          说明:为了方便数据不使用真正的数据库,而是通过内存数据模拟    1.       Web API中包含的方法 Action HTTP method Relative URI GetAllContact GET /api/contact GetContact GET /api/

《Pro ASP.NET MVC 3 Framework》学习笔记之九【Ninject的使用-下】

接着上次的Ninject的笔记,如果你是初次路过,可以先看看我前面的笔记. 一,创建依赖链(Chains of Dependency) 当我们向Ninject请求创建一个类型时,Ninject会去检查该类型和其他类型之间的耦合关系.如果有额外的依赖,Ninject也会解析它们并创建我们需要的所有类的实例.为了进一步说明,我们创建一个新的接口和一个实现该接口的类.请注意我们的例子是跟前面的笔记衔接的,所以如果你打算跟着一起操作的话,最好能够去看看前面的笔记. 创建一个IDiscountHelper

《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】

主题:应用领域驱动开发(Applying Domain-Driven Development) Domain Model是MVC程序的"心脏",其他的一切,包括Controllers和Views仅仅是用来跟Domain Model交互的一种方式,ASP.NET MVC并没有限制使用在Domain Model上面的技术,我们可以自由的选择跟.net framework交互的技术,并且这样的选择是非常多的.不仅如此,ASP.NET MVC为我们提供了基础的架构和约定来帮助Domain Mo

《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】

一,创建松耦合的组件 1."分解关注点"是MVC模式里面一个非常重要的特性.我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系.理想情况下,每个组件都是孤立的,不知道其他组件的存在,处理应用程序的其他领域仅仅通过抽象接口,这就是所谓的松耦合,它让我们的应用程序更加容易测试和修改.通过一个简单的例子可以帮助我们理解,假如我们想写一个发邮件的组件,暂且就把这个组件命名为MyEmailSender,接着我们实现一个接口,这个接口定义了所有需要发送邮件的功能,也暂且

《Pro ASP.NET MVC 3 Framework》学习笔记之三十五 【部署】

准备要部署的应用程序 在正式进入部署MVC程序到IIS之前,会介绍一些关于应用程序迁移到生产环境之前探测错误以及一旦进入生产环境最大化性能的技术.同时也会展示关于流线型部署过程的有用的功能.   检测视图错误 Razor视图会在服务器需要的时候编译而不是在VS里面生成项目时编译,正常情况下,探测视图编译错误的方式是系统的访问每一个action,从而让每一个view都能够呈现.这显然是非常乏味而且不会一直成功的技术,特别是在基于不同的model状态呈现不同的view的时候.我们可以启用一个特别的项

《Pro ASP.NET MVC 3 Framework》学习笔记之十五【示例项目SportsStore】

绑定Shopping Cart 定义购物车Cart的实体,购物车是我们程序业务领域的一个部分,所以在我们领域模型(Domain Model)里面添加一个cart的实体是合理的.在SportsStore.Domain的Entities文件夹下添加一个Cart的实体类,如下所示: View Code public class Cart {private List<CartLine> lineCollection = new List<CartLine>();//添加 public vo