mvc5 新功能model修改后基架会自动修改吗

&&&&&& 在本篇文章中,我将添加一个新的查询页面(),可以按书籍的种类或名称来进行查询。这个新页面的网址是http://localhost:36878。该页面中用一个下拉框来显示种类,用一文本框让用户输入书籍的名称。当用户在点击&查询&按钮之后,页面会被刷新,显示用户的查询结果。控制器会根据用户提交查询参数,由动作方法(Action&Motehd)去解析用户提交的参数值,并使用这些值来查询数据库。
第一步,创建SearchIndex查询页面&&&&&&&&&&& 首先,在中添加一个动作方法。该方法将返回一个包含表单的视图。代码如下:
public ActionResult SearchIndex(string searchString)
var books = from m in db.Books
if (!String.IsNullOrEmpty(searchString))
books = books.Where(s =& s.Name.Contains(searchString));
return View(books);
&&&&&&& 上面SearchIndex方法代码中的第一行如下面,其作用是创建一个LINQ查询从数据库中筛选相应的书籍数据:
var books = from m in db.Books
&&&&&& 这一句表示查询已经建立,但是尚未对数据存储区中的数据进行筛选。&&&&&&& 如果&searchString&参数变量中有一个字符串,则这个&books&查询语句将以&searchString&参数变量的值做为查询条件进行筛选相应的书籍数据,如下面的代码:
if (!String.IsNullOrEmpty(searchString))
books = books.Where(s =& s.Name.Contains(searchString));
&&&&&& 上面代码中的s&=&s.Title代码是一个L表达式。&Lambda&表达式用在基于方法的&LINQ&查询中,作为诸如&Where&和&Where&等标准查询运算符方法的参数。&&例如上面的代码中使用的方法。查询时未执行它们的定义,或者当他们通过调用一个方法进行修改,或在排序时进行修改。相反,查询执行的延迟,这意味着一个表达式的计算被延迟,直到其变量实际上的迭代结束或者可以调用的方法(ToList方法)被调用。在这个页面中,查询是在视图下执行。
&&&&&& 接下来,我们来制作一个视图,以将这个查询界面呈现给用户看。在Visual Studio中的文件中右键单击里面的SearchIndex方法,在弹出菜单中单击&添加视图&菜单。在添加视图对话框中,指定你要一个Book对象传递给视图模板作为它的模型类。在支架模板列表中,选择列表,然后单击&添加&。如下图。
&&&&&&& 当单击&添加&按钮,将创建一个Views\Book\&SearchIndex.cshtml视图模板。因为你选择了列表中的支架模板列表中,Visual&Studio会自动生成(搭建)的视图中的一些默认的标记。基架创建一个HTML表单。它检查Book类和创建的代码来呈现为类的每个属性&label&元素。下面的清单显示生成的创建视图:
@model IEnumerable&MvcApplication1.Models.Book&
ViewBag.Title = "书籍查询";
&h2&书籍查询&/h2&
@Html.ActionLink("Create New", "Create")
@Html.DisplayNameFor(model =& model.Category)
@Html.DisplayNameFor(model =& model.Name)
@Html.DisplayNameFor(model =& model.Numberofcopies)
@Html.DisplayNameFor(model =& model.AuthorID)
@Html.DisplayNameFor(model =& model.Price)
@Html.DisplayNameFor(model =& model.PublishDate)
@foreach (var item in Model) {
@Html.DisplayFor(modelItem =& item.Category)
@Html.DisplayFor(modelItem =& item.Name)
@Html.DisplayFor(modelItem =& item.Numberofcopies)
@Html.DisplayFor(modelItem =& item.AuthorID)
@Html.DisplayFor(modelItem =& item.Price)
@Html.DisplayFor(modelItem =& item.PublishDate)
@Html.ActionLink("Edit", "Edit", new { id=item.BookID }) |
@Html.ActionLink("Details", "Details", new { id=item.BookID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.BookID })
&&&&& 按F5, 运行该应用程序,并使用浏览器导航到SearchIndex。在后面会追加一个查询字符串,如&&。提交之后,符合条件的书籍数据将显示。如下图。
&第三,修改查询方法&&&&&&& 我们也可以修改方法的参数,传入一个命名参数&&,这个&&参数将匹配在文件中的缺省路由设置中的{id}占位符。路由设置如下:&
{controller}/{action}/{id}
原来方法如下所示
public ActionResult SearchIndex(string searchString)
var books = from m in db.Books
if (!String.IsNullOrEmpty(searchString))
books = books.Where(s =& s.Name.Contains(searchString));
return View(books);
&&&&&& 修改后的SearchIndex方法将如下所示:
public ActionResult SearchIndex(string id)
string searchString =
var books = from m in db.Books
if (!String.IsNullOrEmpty(searchString))
books = books.Where(s =& s.Name.Contains(searchString));
return View(books);
&&&&&&&&& 现在,您可以把查询条件&书籍名称&作为路由数据(URL中的一段字符),而不是作为查询字符串值。如下图。
&&&&&& 第三,添加&书籍名称&查询条件&&&& 通过上面的学习,我们实现了数据查询功能。但是我们不能希望普通用户也能像我们一样,会在URL中添加查询条件。当需要进行查询时,自己去修改URL中的查询条件内容来进行书籍查询。所以,需要添加一个查询页面,方便普通用户输入相应的查询条件,根据查询条件去查询书籍。
&&&&&& 一、改变SearchIndex方法的签名,并通过路由绑定到ID参数上,让SearchIndex方法接受一个名为&searchString&字符串作为参数。代码如下:
public ActionResult SearchIndex(string searchString)
var books = from m in db.Books
if (!String.IsNullOrEmpty(searchString))
books = books.Where(s =& s.Name.Contains(searchString));
return View(books);
&&&&&&& 二、打开SearchIndex.cshtml文件,在@Html.ActionLink("Create&New",&"Create")的位置处,添加一个文本框与一个查询按钮。内容如下:&
@using (Html.BeginForm())
&p& 书籍名称: @Html.TextBox("SearchString")&br /&
&input type="submit" value="查询" /&&/p&
&&&&&&& 下面的代码显示了SearchIndex.cshtml文件的一部分与增加的筛选标记。&
@Model IEnumerable&MvcApplication1.Models.Book&
ViewBag.Title = "书籍查询";
&h2&书籍查询&/h2&
@using (Html.BeginForm())
&p& 书籍名称: @Html.TextBox("SearchString")&br /&
&input type="submit" value="查询" /&&/p&
&&&&&&& 这个辅助方法创建了一个标签。当用户通过单击&查询&按钮提交表单时,辅助方法将会把这个Form中的内容提交给控制器,同时刷新页面。&&&&&& 三、按F5,运行该应用程序,并在&书籍名称&文本框中输入&sql&,然后点击&查询&按钮,进行书籍查询。如下图。
&&&&&& 还有不需要重载SearchIndex的HttpPost的方法。因为该方法不改变应用程序的状态,只是是用来查询数据。&&&&&&& 你可以象下面一样把加在方法的上面。在添加了之后,浏览器将首先调用匹配的方法。的方法运行结果如下图。
[HttpPost]
public string SearchIndex(FormCollection fc, string searchString)
return "&h3& From [HttpPost]SearchIndex: " + searchString + "&/h3&";
&&&&&&&& 然而,即使添加了SearchIndex的HttpPost版本的方法。假设一下如下场景,你想把某个查询结果发给你的朋友,你又想只发一个特定链接给朋友,让他们可以查询查看相同的一个查询结果。请注意,如果是相同的查询结果,一个为HTTP&POST请求,另一个为相同的URL&GET请求(例如本地主机:XXXXX&/图书/&SearchIndex&)&。则HTTP&POST请求在URL本身没有带查询参数的情况下,是无法看到查询结果的。如果只是一个URL,这就意味着你与朋友们永远不能看到相同的查询结果。&&&&&&& 你应该使用以下的解决方案,使用BeginForm的重载方法,指定POST请求应该在URL中添加的相应的查询参数信息&,它应该被路由到SearchIndex方法的HTTPGET版本。使用下面的代码替换现有参数的BeginForm方法将:如下图。
@using (Html.BeginForm("SearchIndex", "book", FormMethod.Get))
&&&&&&& 现在,当你点击&查询&按钮时,URL中将包含查询条件的字符串。查询请求也将去请求HTTPGET的&SearchIndex操作方法,即使你有一个HttpPost的SearchIndex方法。如下图。
第三,添加&书籍种类&查询方法&&&&& 第一步,删除代码中HttpPost版本的SearchIndex方法。&
&&&&&& 第二步,添加新的查询功能。在页面中添加一个&书籍种类&的查询条件,让用户可以通过&书籍种类&查询到相关书籍。用下面的代码替换SearchIndex方法:
public ActionResult SearchIndex(string Category, string searchString)
var cateLst = new List&string&();
var cateQry = from d in db.Books
orderby d.Category
select d.C
cateLst.AddRange(cateQry.Distinct());
ViewBag.category = new SelectList(cateLst);
var books = from m in db.Books
if (!String.IsNullOrEmpty(searchString))
books = books.Where(s =& s.Name.Contains(searchString));
if (string.IsNullOrEmpty(Category))
return View(books);
return View(books.Where(x =& x.Category == Category));
&&&&& 这个新的SearchIndex方法增加了一个新的查询条件,即Category。代码的前几行创建一个List对象从数据库中查询&书籍种类&,并将查询到的结果添加到List对象中。
&&&&&& 下面的代码是通过一个查询从数据库把所有书籍种类都查了出来。&&
var cateQry = from d in db.Books
orderby d.Category
select d.C
&&&&&&& 以下代码的作用是使用泛型集合的方法所有书籍类型添加到列表中。&(如果没有修饰符,会将相同的类型增加到列表中&-&例如,MS,SAP将会被我们添加两次)。
cateLst.AddRange(cateQry.Distinct());
&& && 然后,该代码存储在对象流派列表。&
ViewBag.category = new SelectList(cateLst);
&&&& 下面的代码演示如何检查Category参数。如果它不是空的,该代码进一步限制了书的查询所选择的书限制到指定的种类。&&&&&&
if (string.IsNullOrEmpty(Category))
return View(books);
return View(books.Where(x =& x.Category == Category));
&&&&&&&&&& 第四,在SearchIndex查询页面中添加&书籍种类&查询条件&&&&&&&&&& 添加一个样式的下拉列表框()到SearchIndex.cshtml文件中,放在&书籍名称&文本框的前面。已完成的代码如下所示:
@using (Html.BeginForm("SearchIndex","book",FormMethod.Get)){
&p&书籍种类: @Html.DropDownList("category", "All")
书籍名称: @Html.TextBox("SearchString")
&input type="submit" value="查询" /&&/p&
&&&&&&& 按F5,运行这个应用程序,在浏览器中浏览/Book/&SearchIndex这个地址。就可以按&书籍种类&、&书籍名称&进行书籍信息查询。如下图1,图2。&
图2&在本篇中学习如何创建一个新的查询操作方法和视图,让用户通过书籍名称和书籍类型来进行查询。
阅读(...) 评论()传统的MVC概念
模型:组类,描述了要处理的数据以及修改和操作数据的业务规则
视图:定义应用程序用户界面的显示方式
控制器:一组类,用来处理来自用户,整个应用程序流以及特定应用程序逻辑的通信
MVC在web框架中的应用
模型:模型是描述程序设计人员感兴趣问题域的一些类,这些类通常封装存储在数据库中的数据,以及操作这些数据和执行特定域业务逻辑的代码。在ASP.NET MVC中,模型就像是一个使用了某个工具的数据访问层,包括实体框架。
视图:一个动态生成HTML页面的模板
控制器:一个协调视图和模型之间关系的特殊类。它响应用户输入,与模型进行对话,并决定呈现哪个视图。
约定优于配置
MVC中的控制器主要用来响应用户的输入,并且在响应时通常会修改模型.通过这种方式MVC关注的是应用程序流.输入数据的处理.以及对相关视图输出数据的提供.在MVC中,URL告诉路由机制去实例化哪个控制器,调用哪个操作方法,然后决定使用哪个视图.MVC提供的是方法调用的结果,而不是动态生成的页面.
整个项目的结构
&&& & & & & & & & & & &
HomeControler:负责网站根目录下的&home page&和&about page&
AccountControler:响应与账户相关的请求,比如登陆和账户注册
&&& public class HomeController : Controller
&&&&&&& public ActionResult Index()
&&&&&&&&&&& ViewBag.Message = "欢迎使用 ASP.NET MVC!";
&&&&&&&&&&& return View();
&&&&&&& }o
&&&&&&& public ActionResult About()
&&&&&&&&&&& return View();
这个类非常的简单,继承与Controler基类,HomeControler类的Index方法负责决定当浏览网站首页时触发的事件。
控制器一般以Controller结尾
&&& public class StoreController : Controller
&&&&&&& //
&&&&&&& // GET: /Store/
&&&&&&& public string Index()
&&&&&&&&&&& return "Hello from Store.Index()";
&&&&&&& public string Browse()
&&&&&&&&&&& return "Hello from Store.Browse()";
&&&&&&& public string Details(string name)
&&&&&&&&&&& return "Hello from Store.Details():Name"+
Controller URL操作中的参数.
/Store/Browse?name=liuzhongdong
&&&&&&& public string Details(string name)
&&&&&&&&&&& return "Hello from Store.Details():Name"+
Name可以自动检测。
return HttpUtility.HtmlEncode("Hello from Store.Details():Name"+name);
利用实用方法HttpUtility.HtmlEncode来预处理用户输入。这能阻止用户向视图中用链接注入JavaScript代码或者html标记。
也可以使用/Srore/Deatails/5,URL路由可以自动检测到值。
控制器就好像是浏览器再直接调用控制器类中的方法。类方法和参数都被具体化为URL中特定路径片段或查询字符串,结果就是一个返回给浏览器的字符串。这就进行了极大的简化,而忽略了下面这些细节:
l& 路由将URL映射到操作的方式
l& 将试图作为模板生成向浏览器返回的字符串(通常是html格式)
l& 操作很少返回原始的字符串,它通常返回合适的ActionResult来处理像HTTP状态码和调用视图模板系统这样的事项。
视图的职责是向用户提供用户界面。向它提供对模型的引用后,它会将模型转换为准备提供给用户的格式。在ASP.NET MVC中,这个过程由两部分组成。第一个部分是检查由控制器提交的ViewDataDictionary(通过ViewData)属性访问。另外一部分是将其内容转换为HTML格式。视图并不一定只是渲染html,视图还可以渲染其他的内容。
从ASP.NET MVC3开始,视图数据可以通过ViewBag属性访问。ViewBag属性是动态的,它语法简单,可以通过访问ViewData属性访问的相同数据。它是一个高效地利用C#4中新的dynamic关键字的封装器,其中封装了ViewData。这样就可以使用类似属性访问的方法来检索字典中的数据。
所以ViewBag.Message就等同于ViewData[&Message&]
注意:如果在ViewData[& this is a key&]中存放一个值,那么将不能使用ViewBag访问这个值。另外应该知道的一点是,这个动态的值不能作为一个参数传递给扩展方法。因为C#编译器为了选择正确的扩展方法,在编译时必须知道每个参数的真正类型。
@Html.TextBox("name", (string)ViewBag.Name)或者@Html.TextBox("name", ViewData["Name"]);因为动态类型只有在编译时才确定。
在强类型视图的情形下,ViewDataDictionary拥有一个视图渲染的强类型模型对象。这个模型可能代表了实际的域对象,或者它可能是一个视图专有的呈现模型对象。为了方便起见,这个模型对象可以通过视图的Model属性进行引用。
视图总是被一个控制器渲染,该控制器向它提供了要渲染的数据
&&&&&&& public ActionResult Details()
&&&&&&&&&&& ViewBag.Message = "Hello World.Welcome to ASP.NET MVC";
&&&&&&&&&&& return View();
&&& Layout =
&!DOCTYPE html&
&&& &title&Details&/title&
&&& &h1&@ViewBag.Message&/h1&
&&&&&& &p&
&&&&&&&&&& This is a sample view.It is not much to look at,But is gets the job done.
&&&&&& &/p&
&&& &/div&
按照约定,每个控制器在Views目录下面都有一个对应的文件夹,其名称与控制器一样。只是没有Controller后缀名。在每一个控制器的View文件夹中,每一个操作方法都有一个名称相同的视图文件与之对应。这就提供了操作方法与视图关联的基础。
public ActionResult Details()
&&&&&&&&&&& ViewBag.Message = "Hello World.Welcome to ASP.NET MVC";
&&&&&&&&&&& return View();
默认就会去对应的文件夹中找到与操作名称相同的视图。但是这个约定是可以重写的。
return View("Index");
使用上述方式,也只会在控制器对应的View文件夹里查找,如果要在不同的视图目录中查找,可以使用
return View("~/Views/Store/Details.cshtml");
强类型视图
强类型视图指给视图指定一个实体对象或者对象集合,也就是所谓的数据。那么视图这个时候就可以展现数据。
实体类Person
Controller代码
public ActionResult Details()
&&&&&&&&&&& List&Person& persons = new List&Person&();
&&&&&&&&&&& persons.Add(new Person() { PersonId=1, FirstName="Liu", LastName="Zhongdong" });
&&&&&&&&&&& persons.Add(new Person() { PersonId = 2, FirstName = "Ma", LastName = "Jun" });
&&&&&&&&&&& persons.Add(new Person() { PersonId = 3, FirstName = "Yang", LastName = "Huan" });
&&&&&&&&&&& persons.Add(new Person() { PersonId = 3, FirstName = "Zhou", LastName = "Yajie" });
&&&&&&&&&&& ViewBag.Persons =
&&&&&&&&&&& return View();
&&&&&&&&& @foreach (Person p in ViewBag.Persons)
&&&&&&&&& {
&&&&&&&&&&&&& &li&PersonId:@p.PersonId FirstName:@p.FirstName LastName:@p.LastName&/li&
这里传递类型到视图的方式是使用ViewBag。然后在视图里面去迭代。
可以使用View的重载方法传递模型实例来指定模型
return View(persons);
在后台,传进View方法的值将赋给ViewData.Model属性。接下来是告诉视图那种类型的模型正在使用@model声明。注意,这里需要使用类型的完全限定名。
@using FirstMVCTest.M
@model IEnumerable&Person&
@foreach (Person p in Model)
&&&&&&&&& {
&&&&&&&&&&&&& &li&PersonId:@p.PersonId FirstName:@p.FirstName LastName:@p.LastName&/li&
&&&&&&&&& }
对于在视图中经常使用的名称空间,一个比较好的方式就值在Views目录下的web.config文件中声明。
&pages pageBaseType="System.Web.Mvc.WebViewPage"&
&&&&& &namespaces&
&&&&&&& &add namespace="System.Web.Mvc" /&
&&&&&&& &add namespace="System.Web.Mvc.Ajax" /&
&&&&&&& &add namespace="System.Web.Mvc.Html" /&
&&&&&&& &add namespace="System.Web.Routing" /&
&&&&&&& &add namespace="FirstMVCTest.Models"/&
&&&&& &/namespaces&
视图通常需要显示各种没有映射到域模型的数据,例如,可能需要视图来显示单个商品的详细信息,但是同以试图也要显示商品附带的其他信息,显示视图与主模型无关的额外数据的一种简单的方法就是把这些数据存放在ViewBag属性中,这样是可以实现的,但是并非对每一个人都实用,如果想要严格控制流进视图的数据,就必须使所有的数据都要强类型的。以便视图编写人员可以使用智能感知功能。可能采用的一个方法就是编写自定义的视图模型类,这里的视图模型不是MVVM里面的VM。这里的视图模型之的是视图特定模型。相当于为视图提供数据模型的类,而不是特定于域的模型。
&&& public class PersonSchoolViewModel
&&&&&&& private School _myS
&&&&&&& public School MySchool
&&&&&&&&&&& get { return _myS }
&&&&&&&&&&& set { _mySchool = }
&&&&&&& private IEnumerable&Person& _
&&&&&&& public IEnumerable&Person& Persons
&&&&&&&&&&& get { return _ }
&&&&&&&&&&& set { _persons = }
View里面要显示的就是既包括了School又包括Person的数据&
1,在控制器的方法里面右键添加
也可以在解决方案管理器中直接添加
视图名称:指定视图的名称。
视图引擎:指定是使用aspx或者razor渲染view(生成HTML)。
创建强类型视图:是否在创建视图的时候选择一个模型类
支架模板:
l& Empty:创建一个空视图,使用@model语法指定模型类型
l& Create:创建一个视图,其中带有创建模型新实例的表单,并为模型类型的每一个属性生成一个标签和编辑器
l& Delete:创建一个视图,其中带有删除现有模型实例的表单,并为模型的每一个属性显示一个标签以及当前该属性的值
l& Details:创建一个视图,它显示了模型类型的每一个属性的标签及其相应值
l& Edit:创建一个视图,其中带有编辑现有模型的表单,并为模型的每个属性生成一个标签和编辑器
l& List:创建一个带有模型实例表的视图.为模型类型的每一个属性生成一列.确保操作方法向视图传递的是IEnumerable&YourModelType&类型.同时为了执行创建/编辑/删除操作,视图中还包含了指向操作的链接.
创建为分部视图:选择这个选项意味着要创建的视图不是一个完整的视图,因此,layout选项是不可用的.对于Razor视图引擎来说,生成的分部视图除了再其顶部没有&html&标签和&head&标签之外,很像一个常规视图.
使用布局或母版页:这个选项决定了要创建的视图是否引用布局(或母版页),或是成为一个完全独立的视图.对于Razor视图引擎来说,如果选择使用默认布局.就没有必要指定一个布局了,因为在_ViewStart.cshtml文件中已经指定了布局.这个选项是用来重写默认布局文件的
自定义T4视图模板
上面提到的视图模板是由C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 3\CodeTemplates\AddView\CSHTML中的文件提供的.可以自定义这些模板,毕竟简单的一种方式是直接重写这些文件,但是会对所有的项目产生影响.所以我们可以把这些文件导入到我们自己的项目中.
但是这里会有一个编译错误
解决的方式是清空所有模板的自定义工具属性
Razor视图引擎
用Razor代码来渲染视图,有点是干净,轻量级,简单.减少了语法障碍.
Razor中的核心字符是@.这个单一字符用作标记-代码的转换字符,有时也反过来用作代码-标记的转换字符.这里一共有两种基本类型转换:代码表达式和代码块.求出表达式的值,然后将值写入到响应中.
&h1&Listing @stuff.Lenght items&/h1&
表达式@stuff.Lenght是作为隐式代码表达式求解的,然后在输出中显示表达式的值。这里不需要指出代码表达式的结束位置,Razor十分智能,它能知道后面的空格符不是一个有效的标示符。
但是在有些时候,Razor也是存在二义性的。
&&& string rootNamespace = "MyApp";
&span&@rootNamespace.Model&/span&
这个时候会提示string没有Model属性,其实我们想打印的是MyApp.Model,但是Razor不能理解我们的意图,而会认为@rootNamespace.Models是代码表达式。幸亏Razor还可以通过将表达式用圆括号括起来来支持显示代码表达式,这样就告知了Razor,.Model是字面量文本,而不是代码的一部分。
下面了解一下Razor在显示电子邮件时的情况
&span&&/span&
乍看之下会觉得有错误,因为@看起来像是一个企图打印出变量microsoft的com属性的有效代码表达式。但是Razor足够智能,可以辨别出电子邮箱的一般模式。这可以适应大多数的情况,在一些有效的电子邮件地址可能会显示不出来的时候,可以用两个@@来转义。但是这种情况也会存在问题,比如,我们要求下面的表达式
&li&Lenght@rootNamespace.Length&/li&
这种情况下Razor会逐步打印,因为它会把它匹配成为一个电子邮件。但是我们希望的结果是
&li&Lenght3&/li&
这里Razor再次有了二义性,那么我们就可以使用圆括号,任何时候Razor有了二义性,都可以使用圆括号指明想要的内容
到目前为止,我们还有一个二义性没有解决,比如微博的时候,我们要打印@Edrick,@Majun等等。Razor会尝试去解析这么隐式代码表达式,但是会以失败告终,这种情况,我们应该使用@@来转义。
因为在许多情况下都需要用视图显示用户输入。所以总是存在潜在的跨站脚本注入攻击。但是值得称赞的是Razor表示是是用HTML编码的
&&&&& string message = "&script&alert('haacked')&/script&";
&span&@message&/span&
这段代码不会弹出警告对话框。而会显示编码消息
&script&alert('haacked')&/script&
然而在javascript中将用用户提供的值赋给变量时,要使用javascript字符串编码而不仅仅是HTML编码。要使用
@Ajax.JavaScriptStringEncode(message);
当我们需要在页面中输入多行代码的时候需要用到代码块
&&& string rootNamespace = "MyApp";
&&& string message = "&script&alert('haacked')&/script&";
Foreach为代码块。另外一个例子需要用代码块的是调用了没有返回值的方法
@{Html.RenderPartial("");}
Razor和纯文本
&&&&&&&&&&& @if (isShowMessage)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& &text&This is Span text&/text&
&&&&&&&&&&&&&&& @:This is Span text Too!
&&&&&&&&&&& }
以上使用text标签或者@:都可以。
调用泛型方法
@(Html.SomeMothed&A Type&())
Razor中的布局有助于使应用程序中的多个视图保持一致的外观.布局与ASP.NET中的母版页是作用是相同的.可以使用布局为网站定义公共模板(或只是其中的一部分).公共模板包含了一个或者多个占位符.应用程序中的其他视图为他们提供内容.从某些角度看,布局很想视图的抽象基类.
&div class="page"&
&&&&&&& &header&
&&&&&&&&&&& &div id="title"&
&&&&&&&&&&&&&&& &h1&我的 MVC 应用程序&/h1&
&&&&&&&&&&& &/div&
&&&&&&&&&&& &div id="logindisplay"&
&&&&&&&&&&&&&&& @Html.Partial("_LogOnPartial")
&&&&&&&&&&& &/div&
&&&&&&&&&&& &nav&
&&&&&&&&&&&&&&& &ul id="menu"&
&&&&&&&&&&& &&&&&&&&&li&@Html.ActionLink("主页", "Index", "Home")&/li&
&&&&&&&&&&&&&&&&&&& &li&@Html.ActionLink("关于", "About", "Home")&/li&
&&&&&&&&&&&&&&& &/ul&
&&&&&&&&&&& &/nav&
&&&&&&& &/header&
&&&&&&& &section id="main"&
&&&&&&&&&&& @RenderBody()
&&&&&&& &/section&
这就是一个最简单的布局,看起来像一个标准的Razor视图,但是需要注意的是再视图中有一个@RenderBody调用.这是一个占位符,用来标记使用这个布局的视图将渲染他们的主要内容的位置.现在多个视图可以利用这一布局显示一致的外观
Layout = "~/Views/Shared/_Layout.cshtml";
View.Title= " xxx";
&&&&&&& &footer&
&&&&&&&&&&& @RenderSection("Footer", false);
&&&&&&& &/footer&
@section Footer{
&&& This is the &strong&Footer&/strong&
完成布局中的其他节。
那么如果每一个视图都使用layout属性来指定它的布局。如果多个视图使用同一个布局,那么会产生冗余,很难维护。那么解决的方法是_ViewStart.cshtml可以消除这种冗余。这个文件优先于同目录下任何视图代码的执行。这个文件也可以递归地应用到子目录下的任何视图。
&&& Layout = "~/Views/Shared/_Layout.cshtml";
当然,在视图中可以重写这种设置。
模型:应用程序关注的域,模型就是要保存,创建,更新,和删除的对象。
我们可以使用工具来为每个模型对象的标准索引,创建,编辑和删除功能构建控制器和视图。这个构建工作称为基架。我们可以在Model文件夹下面创建我们的模型。下面我们开始构建两个模型。Album和Artist
&&& /// &summary&
&&& /// 专辑模型类
&&& /// &/summary&
public class Album
&&&&&&& public virtual int AlbumId { }
&&&&&&& public virtual int GenreId { }
&&&&&&& public virtual int ArtistId { }
&&&&&&& public virtual string Title { }
&&&&&&& public virtual decimal Price { }
&&&&&&& public virtual string AlbumArtUrl { }
&&&&&&& public virtual Genre Genre { }
&&&&&&& public virtual Artist Artist { }
&&& /// &summary&
&&& /// 艺术家模型类
&&& /// &/summary&
&&& public class Artist
&&&&&&& public virtual int ArtistId { }
&&&&&&& public virtual string Name { }
&&& /// &summary&
&&& /// 流派模型类
&&& /// &/summary&
&&& public class Genre
&&&&&&& public virtual int GenreId { }
&&&&&&& public virtual string Name { }
&&&&&&& public virtual string Description { }
&&&&&&& public virtual List&Album& Albums { }
为商店管理器构建基架
商店管理器其实是一个可以用来编辑专辑信息的控制器。我们来创建一个控制器
MVC中的基架可以为应用程序的创建,读取,更新和删除功能生成所需的样板代码。基架模板检测模型类的定义,然后生成控制器以及该控制器的关联视图。基架知道如何命名控制器,视图以及每个组件需要执行什么代码。(如果不喜欢默认的基架,可以根据自己的需要自定义基架模板,也可以通过NuGet查找可替代的基架模板。)基架不是必须的,但是它最起码可以代劳在正确位置创建文件的操作。节省开发人员的时间
MVC有3个基架可以选择。
l& 空控制器:会向Controller文件夹中添加一个具有指定名称且派生自Controller的类。这个控制器带有唯一的操作就是Index操作。这个模板不会生成任何视图
l& 包含空的读写操作的控制器:这个模板会向项目中添加一个带有Index,Details,Create,Edit和Delete操作的控制器,虽然控制器内部的操作不是完全空白,但是,他们不会执行任何有实际意义的操作。也不创建视图
l& 包含读写操作和视图的控制器(使用EntityFramerwork):它不仅生成带有整套Index,Details,Create,Edit和Delete操作的控制器及其需要的所有相关视图,而且还生成与数据库交互的代码。为了让模板产生合适的代码,需要指定一个模型类,为了生成数据访问代码。基架需要一个数据上下文对象。这里可以指定一个现有的,也可以根据需要创建一个新的。
EF4.1会随MVC3一起安装,EF4.1支持代码优先的开发风格,代码优先指的是可以在不创建数据库模式,也不打开VS设计器的情况下载SQL SERVER中存储或检索信息。我们在实体类中的属性都是虚拟的,虚拟属性不是必须的,但是他们会给EF提供一个纯指向C#类集的钩子,并为EF启动一些新特性,如高效的修改跟踪机制。实体框架需要知道模型属性的修改时刻,因为它要在这一刻生成并执行一个SQL UPDAGTE语句,使这些改变和数据库保持一致。
当使用EF的代码优先方法时,需要使用从EF的DBContext类派生出的一个类来访问数据库。该类具有一个或者多个DBSet&T&类型的字段。类型T表示一个要持久保存的对象。
Content代码
&&& public class MusicStoreContext : DbContext
&&&&&&& public MusicStoreContext() : base("name=MusicStoreContext")
&&&&&&& public DbSet&Album& Albums { }
&&&&&&& public DbSet&Genre& Genres { }
&&&&&&& public DbSet&Artist& Artists { }
Controller部分代码
&&& public class StoreManagerController : Controller
&&&&&&& private MusicStoreContext db = new MusicStoreContext();
&&&&&&& //
&&&&&&& // GET: /StoreManager/
&&&&&&& public ViewResult Index()
&&&&&&&&&&& var album = db.Albums.Include(a =& a.Genre).Include(a =& a.Artist);
&&&&&&&&&&& return View(album.ToList());
&&&&&&& //
&&& &&&&// GET: /StoreManager/Details/5
&&&&&&& public ViewResult Details(int id)
&&&&&&&&&&& Album album = db.Albums.Find(id);
&&&&&&&&&&& return View(album);
&&&&&&& &..
var album = db.Albums.Include(a =& a.Genre).Include(a =& a.Artist);
这句代码为预先加载,加载跟Album相关的Genre和Artist。还有一种方式为延迟加载。
一旦完成了基架,下面我们就可以来看看视图了。运用这个基架,MVC会帮我们生成对应的视图
执行基架的代码(生成数据库)
EF的代码优先的方法会尽可能地使用约定而非配置。如果不配置从模型到数据中表和列的具体映射。EF将使用约定创建一个数据库模式。如果在运行时不配置一个具体的数据库连接,那么EF将按照约定创建一个连接。
显示地为代码优先于数据上下文配置连接很简单,即向web.config文件中添加一个连接字符串。该字符串的名称必须与数据上下文类的名称一样。
&add name="MusicStoreContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=MusicStoreContext-11; Integrated Security=T MultipleActiveResultSets=T AttachDbFilename=|DataDirectory|MusicStoreContext-11.mdf"
&&&&& providerName="System.Data.SqlClient" /&
这里为MVC为我们生成的配置为localDB,那是因为在Application_Start里面设置了Database.DefaultConnectionFactory。如果我们要改变这一配置,只需要改变我们的配置文件
如果不配置具体的连接,那么EF将尝试使用默认的数据库模式进行连接。然后查找数据库,如果找不到数据库则创建新的数据库,如果找到数据库,则依据数据库初始化器设置来创建或者不创建数据库
数据库初始化器
保持数据库和模型变化同步的一个简单的方法时允许实体框架重新创建一个现有的数据库。可以告知EF是在应用程序每次启动时重新创建数据库或者仅当检测到模型变化时重新创建
Database.SetInitializer&FirstMVCTest.Models.MusicStoreContext&(new DropCreateDatabaseIfModelChanges&FirstMVCTest.Models.MusicStoreContext&());
Database.SetInitializer&FirstMVCTest.Models.MusicStoreContext&(new DropCreateDatabaseAlways&FirstMVCTest.Models.MusicStoreContext&());
Database.SetInitializer为创建数据库的初始策略,而DropCreateDatabaseAlways,
DropCreateDatabaseIfModelChanges为具体的策略。一种是任何时候只要应用程序启动就创建,一种是当模型改变才创建
但是不管我们使用哪种形式创建数据库。我们的数据都会被清空,那么我们可以设置一些初始化数据。我们可以创建自己的策略类,这个类来继承与DropCreateDatabaseAlways或者DropCreateDatabaseIfModelChanges
&&& public class MusicStoreDBInitializerAlways:DropCreateDatabaseAlways&MusicStoreContext&
&&&&&&& protected override void Seed(MusicStoreContext context)
&&&&&&&&&&& context.Artists.Add(new Artist() { Name="Al Di Meola" });
&&&&&&&&&&& context.Genres.Add(new Genre() { Name="Jazz" });
&&&&&&&&&&& context.Albums.Add(new Album() { Artist = new Artist() { Name = "Rush" },
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Genre = new Genre() { Name="Rock" },
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Price=9.9m,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Title="Caravan" });
&&&&&&&&&&& base.Seed(context);
&&& public class MusicStoreDBInitializerAlwaysIfModelChange:DropCreateDatabaseIfModelChanges&MusicStoreContext&
&&&&&&& protected override void Seed(MusicStoreContext context)
&&&&&&&&&&& context.Artists.Add(new Artist() { Name = "Al Di Meola" });
&&&&&&&&&&& context.Genres.Add(new Genre() { Name = "Jazz" });
&&&&&&&&&&& context.Albums.Add(new Album()
&&&&&&&&&&& {
&&&&&&&&&&&&&&& Artist = new Artist() { Name = "Rush" },
&&&&&&&&&&&&&&& Genre = new Genre() { Name = "Rock" },
&&&&&&&&&&&&&&& Price = 9.9m,
&&&&&&&&&&&&&&& Title = "Caravan"
&&&&&&&&&&& });
&&&&&&&&&&& base.Seed(context);
Database.SetInitializer&MusicStoreContext&(new MusicStoreDBInitializerAlwaysIfModelChange());
在编辑试图的时候,有些关联数据我们需要从数据库里面取到,然后显示给用户,比如专辑相关联的流派,艺术家等等,那么我们有两种办法解决
l& ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);基架是可以理解模型之间的关联,然后从现有的数据中选择
l& 使用ViewModel
&&&&&&& public Album AlbumToEdit { }
&&&&&&& public SelectList Genres { }
&&&&&&& public SelectList Artists { }
然后选择合适的数据。
响应编辑时的Post请求
我们可以看到在生成的Controller中,有两个Edit方法。一个是用来获取信息,一个是用来提交更改,因为它具有[HttpPost]特性。
编辑Happy path和sad path
Hayyp path:就是当模型处于有效状态并可以将对象保存到数据库时执行的代码路径,操作通过ModelState.IsValid属性来检查模型对象的有效性。如果模型处于有效的状态,那么Edit操作将会执行下面的一行代码
db.Entry(album).State = EntityState.M
完整代码如下
&&&&&&& [HttpPost]
&&&&&&& public ActionResult Edit(Album album)
&&&&&&&&&&& if (ModelState.IsValid)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& db.Entry(album).State = EntityState.M
&&&&&&&&&&&&&&& db.SaveChanges();
&&&&&&&&&&&&&&& return RedirectToAction("Index");
&&&&&&&&&&& }
&&&&&&&&&&& ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
&&&&&&&&&&& ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
&&&&&&&&&&& return View(album);
Sad path:当模型无效时操作采取的路径。一般来说就是错误验证和提示
由上面的代码我们可以看到,Edit方法的参数为Album类型的对象,而不是传统的从表单挖取值。当操作带有一个参数时,MVC运行时环境就会使用一个模型绑定器来构建这个参数,在MVC中,可以为不同的模型注册多个模型绑定器,但是一般情况下,默认的绑定器是DefaultModelBinder。在Album对象的情形中,默认的模型绑定器检查Album类,并查找所有能用于绑定的所有属性。遵照前面讲解的命名约定,默认的模型绑定器能自动将请求中的值转换盒移入到一个Album对象中(也可以创建一个实例填充)。
& 当模型绑定器看到Album有Title属性时,它就在请求中查找名为Title的参数。这里说的是请求中,而不是表单中。模型绑定器使用称为值提供器的组件在请求的不同区域中查找参数值。模型绑定器可以查看路由数据,查询字符串和表单集合,当然,如果愿意的话也可以添加自定义的值提供器。
& 模型绑定不局限于HTTP POST操作和复合类型参数。模型绑定也可以将原始参数传入操作,比如下面的链接
模型绑定器会从URL路由中截取数据,然后将值传递给参数。
一样可以完成
安全性:见安全性章节
显示模型绑定
当操作中有参数时,模型绑定会隐式的工作。也可以使用控制器中的UpdateModel和TryUpdateModel方式显示的调用模型绑定。如果模型绑定期间出现错误或者模型无效,那么UpdateModle方法将抛出一个异常
&&&&&&& public ActionResult Edit()
&&&&&&&&&&& Album album = new Album();
&&&&&&&&&&& try
&&&&&&&&&&& {
&&&&&&&&&&&&&&& UpdateModel(album);
&&&&&&&&&&&&&&& db.Entry(album).State = EntityState.M
&&&&&&&&&&&&&&& db.SaveChanges();
&&&&&&&&&&&&&&& return RedirectToAction("Index");
&&&&&&&&&&& }
&&&&&&&&&&& catch
&&&&&&&&&&& {
&&&&&&&&&&&&&&& ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
&&&&&&&&&&&&&&& ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
&&&&&&&&&&&&&&& return View(album);
&&&&&&&&&&& }
TryUpdateModel方法也可以调用模型绑定,但是不会抛出异常,它会返回一个bool值。指示模型绑定是否成功
&&&&&&&&&&& if (TryUpdateModel(album))
&&&&&&&&&&& {
&&&&&&&&&&&&&&& UpdateModel(album);
&&&&&&&&&&&&&&& db.Entry(album).State = EntityState.M
&&&&&&&&&&&&&&& db.SaveChanges();
&&&&&&&&&&&&&&& return RedirectToAction("Index");
&&&&&&&&&&& }
&&&&&& &&&&&else
&&&&&&&&&&& {
&&&&&&&&&&&&&&& ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
&&&&&&&&&&&&&&& ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
&&&&&&&&&&&&&&& return View(album);
& &&&&&&&&&&}
模型绑定的副产品就是模型状态。模型绑定器移进模型中的每一个值在模型状态中都有相应的一条记录。模型绑定后,可以随时查看模型状态以检查模型绑定是否成功。如果模型绑定出现错误。那么模型状态将包含导致绑定失败的属性名,尝试的值以及错误消息。接下来的数据注解和验证中,将讲到怎么显示错误消息
TryUpdateModel(album);
&&&&&&&&&&& if (ModelState.IsValid)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& UpdateModel(album);
&&&&&&&&&&&&&&& db.Entry(album).State = EntityState.M
&&&&&&&&&&&&&&& db.SaveChanges();
&&&&&&&&&&&&&&& return RedirectToAction("Index");
&&&&&&&&&&& }
&&&&&&&&&&& else
&&&&&&&&&&& {
&&&&&&&&&&&&&&& ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
&&&&&&&&&&&&&&& ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
&&&&&&&&&&&&&&& return View(album);
&&&&&&&&&&& }
表单和HTML辅助方法
HTML辅助方法是用来辅助HTML开发的,有些时候处理HTML是比较简单的,但是有些时候却有些复杂,比如,页面中的链接指向正确的位置。表单元素拥有可用于模型绑定的合适名称和值,以及当模型绑定失败时,其他元素能够显示相应的错误提示消息。这个时候,掌握HTML标记是远远不够的。还需要试图和运行环境之间的协调配合。
表单的使用(理解表单)
表单是包含输入元素的容器,其中包含按钮,复选框,文本框等元素。单表中的这些输入元素使得用户能够像页面输入信息。并把输入的信息提交给服务器。但是提交给什么服务器,这些信息又是如何到达服务器的。其实由form的action和method特性完成
&form action=""&
&&& &input name="q" type="text" /&
&&& &input name="q" type="submit" value="Search"/&
那么这段代码上面是没有method特性的,默认方式是使用get方式。当用户使用http get请求时,浏览器会提取表单中输入元素的name特性值及其相应的value值,并将他们放在查询字符串中。如果不想让浏览器吧输入值放在查询字符串中,而是想放在http请求的主体重,可以使用post方式。我们来看看get和post的一些不同之处
l& Get请求的信息放在查询字符串中,post在请求主体中,相对安全
l& Get因为参数都在URL中,因此可以建立书签,而post不行
l& Get方法代表的是幂等操作和只读操作,不会改变服务器上的状态,因而可以重复提交,而post却会改变服务器的状态,一般来说,post用于信息安全较高的操作,比如银行卡信息,购物车,密码等信息的修改。
l& Get用于读操作,而Post用于写操作
l& IE会对URL长度限制,即get的长度限制(2083字节),而Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持
&&& ViewBag.Title = "SearchName";
&h2&SearchName&/h2&
&form action="/Home/Search" method="get"&
&&& &input type="text" name="searchName" /&
&&& &input type="submit" value="Search" /&
HomeController下面的Search Action
&&&&&&& public ActionResult Search(string searchName)
&&&&&&&&&&& var albums = db.Albums.Include("Artist").Where(a =& a.Title.Contains(searchName) || searchName == null).Take(10);
&&&&&&&&&&& return View(albums);
Search View
@model IEnumerable&FirstMVCTest.Models.Album&
&&& ViewBag.Title = "Search";
&h2&Search&/h2&
&&&&&& &th&Artist&/th&
&&&&&& &th& Title&/th&
&&&&&& &th&Price&/th&
@foreach (var item in Model) {
&&&&&&& &td&@item.Artist.Name&/td&
&&&&&&& &td& @item.Title&/td&
&&&&&&& &td&@String.Format("{0:C}",item.Price);&/td&
以上代码,我们可以完成一次对服务器的请求,并能查询到值。有时候并非所有的情况都像以上的情况那么的简单,如果网站不是部署到网站的根目录下,或者修改了路由定义,那么这个链接就是一个无效的链接。这个时候,我们就可以通过HTML辅助方法来解决这个问题。HTML辅助方法可以帮我们找到Controller,Action。
@using(Html.BeginForm("Search","Home",FormMethod.Get)){
&&& &input type="text" name="searchName" /&
&&& &input type="submit" value="Search" /&
第一个参数为Action,第二个参数为Controller Name,第三个参数为HTTP请求的方法。当然,他还有许多重载方式。其实这里的工作方式,只是使用了RouteTable中Routes属性名上的GetVirtualPath方法帮我们找到了虚拟路径。
&&& ViewBag.Title = "SearchName";
&&& var context = this.ViewContext.RequestC
&&& var values = new RouteValueDictionary { { "Controller","Home"},{"Action","Search"} };
&&& var path = RouteTable.Routes.GetVirtualPath(context, values);
&form action="@path.VirtualPath" method="get"&
&&& &input type="text" name="searchName" /&
&&& &input type="submit" value="Search" /&
l& HTML辅助方法是可以通过HTML属性调用的方法
l& URL辅助方法是可以通过URL调用的方法
l& AJAX方法是可以通过AJAX调用的方法
他们有一个共同的目标,就是使试图编码变得简单,从上面的代码我们可以看到我们在使用@using(Html.BeginForm("Search","Home",FormMethod.Get))的时候使用了@using。这里使用@using的原因是自动帮我们生成结束标签。因为隐式的调用了Dispose方法。
也可以不使用@using
@{Html.BeginForm("Search","Home",FormMethod.Get)}
&&& &input type="text" name="searchName" /&
&&& &input type="submit" value="Search" /&
@{Html.EndForm();}
不使用using 的代码是没有使用using那么简洁的,这个依照个人习惯。
HTML辅助方法的编码都是经过HTML编码的。
@Html.TextArea("text","Hello &br/& World");这段代码将输出Hello &br/& World
我们可以看一些Html.BeginForm的重载版本
Html.BeginForm("Search","Home",FormMethod.Get,new{ target="blank",@class="EditForm",data_validatable=true});}
我们,可以实例化一个匿名类型,使用name,value的方式设置属性。这里我们有两个地方需要注意
l& Class为C#关键字,所以,使用html的class属性时我们要在前面加上@符号
l& 当有连接符号-时,我们需要用_符号来代替连接符号,原因在AJAX一章里面会讲到
HTML辅助方法的工作原理
每一个Razor试图都继承了各自基类的HTML属性,HTML属性的类型是System.Web.Mvc. HtmlHelper。
有一些方法是扩展方法,既然是扩展方法,那么我们就可以自定义这些辅助方法,自定义辅助方法会在以后讲到。下面我们来一次讲解不同的HTML辅助方法
@Html.ValidationSummary(true):显示ModelState字典中所有验证错误的无序列表,使用布尔值来告知方法排除属性级别的错误。这里只是显示与ModelState中与模型本身有关的错误。而除去那些与具体模型属性相关的错误。
ModelState.AddModelError("", "This is all wrong");
ModelState.AddModelError("Title", "What a terrible name!");
第一个为模型级别的错误,因为代码中没有关联错误与特定的属性的键,因此在试图中的严重区域部会显示这个消息。除非删除第二句代码的Title键或者将@Html.ValidationSummary(true)参数改为false。辅助方法将输出
&div class="validation-summary-errors"&
&li&This is all wrong&/li&
可以在默认的样式表里修改样式
@Html.TextBox:输入框@Html.TextBox("Title", Model.Title);
@Html.TextArea :多行输入框@Html.TextArea("Title", Model.Title,10,80,null);
@Html.Label:显示附加信息。@Html.Label("GenreId", "Genre");
第一个参数为要渲染的文本,第二个参数为要显示的信息,它将生成如下标签
&label for="GenreId"&Genre&/label&
For特性其实指的就是要渲染的下个元素,这里查看[DisplayName]元数据特性自动渲染成我们需要的文本。使用lable的另外一个好处是,如果用户单击lable,浏览器会自动把焦点传送给for特定制定的Model特定相关的输入控件
@Html.DropDownList:@Html.DropDownList("GenreId", String.Empty)返回一个&select/&元素,允许进行单项选择
@Html.ListBox:返回一个&select/&元素,允许进行多项选择,需要将multiple设置为multiple。
由于以上两个辅助方法都是显示的列表,那么在controller里面还需要进行一些额外的设置。
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
第一个参数为集合,第二个参数为值,第三个参数为要显示的文本,第四个参数为选择的值。如果想避免一些反射开销的同时还想自己生成SelectListItem集合。可以使用Linq的SelectListItem对象放在Genres中。
ViewBag.GenreId = db.Genres
&&&&&&&&&&&&&&& .OrderBy(key =& key.Name)
&&&&&&&&&&&&&&& .AsEnumerable()
&&&&&&&&&&&&&&& .Select(g =& new SelectListItem
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& Text=g.Name ,
&&&&&&&&&&&&&&&&&&& Value=g.GenreId.ToString(),
&&&&&&&&&&&&&&&&&&& Selected=album.GenreId==g.GenreId
&&&&&&&&&&&&&&& });
@Html.ValidationMessageFor:显示特定的属性的错误消息。
@Html.ValidationMessage("Title"),我们可以在Controller中故意添加一条错误信息ModelState.AddModelError("Title", "What a terrible name!");这里的Message就会显示我们的错误信息。当然,真正的验证是不会是我们这样的。数据的验证会在下一章来讲解。
辅助方法,模型和视图数据
辅助方法提供了对HTML细粒度的控制的同时,带走了构建UI的乏味工作。辅助方法会检查ViewData对象以获取用于显示的当前的值。我们来看一段很简单的代码。在Controller中有一个方法
&&&&&&& public ActionResult Edit(int id)
&&&&&&&&&&& ViewBag.Price = 10.0;
&&&&&&&&&&& return View();
这个时候,ViewBag中存在了Pirce数据,那么在view中,辅助方法就可以通过name直接去找到Price的数据。
@Html.TextBox("Price");
当辅助方法查看ViewData里面的内容的时候,它们也能看到其中的对象属性。
&&&&&&& public ActionResult Edit(int id)
&&&&&&&&&&& ViewBag.Album = new Album() { Price=10.0M };
&&&&&&&&&&& return View();
@Html.TextBox("Album.Price");
渲染得到的标签为&input id="Album_Price" name="Album.Price" type="text" value="" /&
这时的解析规则是,首先检查ViewData里面有没有"Album.Price" ,如果没有,则开始查找.符号前面的对象,然后检查后面的值。需要注意一点的是,name属性时可以使用.的,但是id属性是不能使用点符号的,我们需要用下划线代替点。
辅助方法也可以通过强类型来构建UI中的数据。
&&&&&&& public ActionResult Edit(int id)
&&&&&&&&&&& Album album= new Album() { Price=10.0M };
&&&&&&&&&&& return View(album);
前端@Html.TextBox("Price");
如果想避免自动的查找值,可以使用显示提供值的方式。
@Html.TextBox("Price",Model.Price);
这里需要注意的一个加载顺序是,辅助方法在查找强类型模型对象之前,会首先查看ViewBag里面的值,如果ViewBag里面存有跟强类型属性一样的值,那么会首先查找ViewBag,所以这里要显示的提供值。
强类型辅助方法
我们之前的辅助方法都不是强类型的,都是通过提供key,然后在ViewBag里面去查找值。我们可以使用强类型的辅助方法。
@Html.LabelFor(model =& model.Price)使用lambda表达式使用类型来指定值。
辅助方法和模型元数据
辅助方法不仅查看ViewData里面的数据,它还可以利用模型元数据。例如,之前的Lable辅助方法就可以获取[DisplayName("Genre")]信息
模板辅助方法
MVC中的模板辅助方法利用元数据和模板构建HTML。其中元数据包括关于模型值的信息和模型元数据(数据注解)。模板辅助方法有Html.Display和Html.Editor。比如我们可以使用Html.TextBoxFor来生成Text来编辑数据。那么我们也可以使用Html.Editor来生成同样的标记,但是Html.Editor还可以获取模型的元数据,比如[DataType(DataType.MultilineText)]。那么获取了元数据,这个时候就会生成一个多行文本框的标记。
其他输入辅助方法
Html.Hidden:用于渲染隐藏的输入元素
Html.Password:渲染密码字段
Html.RadioButton:单选按钮,单选按钮一般都是组合使用,让用户从一组数据中选择一项,这个时候,我们可以给一组单选按钮起一个相同的名称。然后选择的那一项会发送到服务器上。
Html.CheckBox:复选框,这个辅助方法会渲染两个元素。其中会包含一个Hidden元素,因为HTML规范中规定浏览器只提交&开&的复选框的值。这样的话。就可以保证在没有选择复选框的情况下也有值提交
渲染辅助方法
渲染辅助方法可以在应用程序中生成指向其他资源的链接,也可以构建被称为分布视图的可重用UI片段。
Html.ActionLink:渲染一个指向另外一个控制器操作的超链接(会生成锚标签a),ActionLink在后台使用路由API来生成URL。如果是同一个控制器中的操作,只需要提供操作名称,如果是不同的控制器,就需要提供控制器的名称。如果需要参数,也可以提供参数。
@Html.ActionLink("Delete", "Delete", "StoreManager", new { id=10});
Html.RouteLink:与ActionLink差不多,但是RouteLink接收的是路由的值。
URL辅助方法
Url.Action:生成链接,但不生成锚标签a。使用控制器作为参数
Url.RouteUrl:生成链接,但使用的路由名称
Url.Content:将应用程序的相对路径转换为绝对路径
&scriptsrc="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"&&/script&
Html.Partial:将分部视图渲染成字符串,通常情况下,分部视图包含在多个不同视图中可以重复使用的标记。
@Html.Partial("_LogOnPartial")
Html.RenderPartial:与Html.Partial一样,但是它不是返回字符串,而是直接写入响应流。出于这个原因,所以必须把RenderPartial放在代码块中。
@{Html.RenderPartial("_LogOnPartial");}
一般来说Partial比RenderPartial方便,但是RenderPartial比Partial性能好,因为它是直接写入到响应流。
Html.Action:执行单独的控制器操作并显示结果。Action提供了更多的灵活性和重用性,因为控制器操作可以建立不同的模型,可以利用单独的控制器上下文。
[ChildActionOnly]
&&&&&&& public ActionResult Menu()
&&&&&&&&&&& var menu = new { One="Index",Two="Product",Three="ContactUs" };
&&&&&&&&&&& return PartialView(menu);
@model Menu
&&& @foreach (var item in Model)
&&&&&&& &li&@item&/li&
@Html.Action("Menu");
以上使用了ChildActionOnly特性。这个特性防止了运行时直接通过一个URL来调用Menu操作。相反只能通过Action或者RenderAction。这个特性不是必须的,但是通常在进行只操作的时候推荐使用。
Html.RenderAction:与Action一样,不同的是它也是直接写入到流。
我们也可以给RenderAction和Action传递参数。
&&&&&&& [ChildActionOnly]
&&&&&&& public ActionResult Menu(MenuOptions options)
&&&&&&&&&&& return PartialView(options);
@Html.Action("Menu", new { options = new MenuOptions() { Width=400,Height=400 } });
与ActionName特性结合。
&&&&&&& [ChildActionOnly]
&&&&&&& [ActionName("CoolMenu")]
&&&&&&& public ActionResult Menu(MenuOptions options)
&&&&&&&&&&& return PartialView(options);
@Html.Action("CoolMenu", new { options = new MenuOptions() { Width=400,Height=400 } });
阅读(...) 评论() &}

我要回帖

更多关于 vs2013 mvc5 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信