上一篇我讲了ASP.MET MVC的基础概念,我相信从上一篇,咱们能够知道MVC的执行过程。这一篇咱们开始讲解Model。咱们知道,在咱们的应用程序中,大多时候是在遵循业务逻辑经过UI操做数据。因此这里按照咱们上一篇讲的分离关注点的观点。咱们至少能够把程序分为三部分,UI,逻辑和数据。业务逻辑由咱们根据具体的领域来实现,UI其实从根本就是展示数据,收集数据。咱们的业务逻辑,操做的其实也仍是数据。因此这一篇,咱们从数据开始,由于我我的以为,数据是最基本的。html
什么是模型sql
其实咱们从ASP.NET三层时代就开始接触模型的概念,只是那个时候咱们一般称之为实体,这里咱们简单的谈一下实体的概念。在谈到现代软件开发的时候,咱们听的不少的一个词就是面向对象,简单点理解,就是程序模拟现实中或者虚拟物体的数据和行为来达到完成现实中的既定任务。举个例子来讲,咱们人。在现实中,人会有一些特征,好比身高:170cm,年龄:18。会说话,会走路,会跳,会跑。那么在面向对象的应用程序中,咱们能够定义一类物体所能具备的特征。而后根据定义的这些特征去产生具体的物体。就像饼干模具和饼干的关系。饼干模具是定义了咱们能生产出什么形状的饼干。咱们也能够经过饼干模具生产出特定于模具的饼干。在程序中,就是类和对象。类是定义了对象能包含什么样的数据和行为。而对象则是实际包含这些数据和行为。而不一样的领域所关注的特性是不同的。人这个物体,在银行领域可能会关注你的信誉,存款等等特性。而在公司员工管理领域,可能关心的是你的年龄,技术方向等等。因此面对不一样的领域,咱们须要抽象出不一样的类型特征。简单归纳,模型就是面对特定领域抽象出的具备某些领域特定的对象。咱们能够增长对象,修改对象,删除对象,查看对象,最后持久化对象。说到底,咱们的对象是包含数据的,因此,咱们操做的也就是数据。数据库
Model数据结构
在ASP.NET中有两种模型,一种是对象领域的,一种是面向视图的。面向领域即数据根据特定的业务逻辑建模,面向视图则是根据UI须要的数据来建模。这两种模式各有好坏。咱们能够混合使用,后面咱们会介绍到。app
这里,咱们经过一个功能来详细讲解Model在MVC中怎么使用。咱们要实现的功能是用户登陆,登录以后用户能够看到一些新闻和产品在首页。这里根据咱们的业务逻辑,咱们至少须要三个模型:用户,新闻,产品。咱们建立一个MVC项目。具体步骤见上一篇MVC基础。接着,咱们在Model文件夹中添加如下三个类。ide
UserInfosqlserver
NewsInfo布局
1 public class NewsInfo 2 { 3 private int _newsInfoId; 4 5 public virtual int NewsInfoId 6 { 7 get { return _newsInfoId; } 8 set { _newsInfoId = value; } 9 } 10 11 private string _newsTitle; 12 13 public virtual string NewsTitle 14 { 15 get { return _newsTitle; } 16 set { _newsTitle = value; } 17 } 18 19 private string _newsContent; 20 21 public virtual string NewsContent 22 { 23 get { return _newsContent; } 24 set { _newsContent = value; } 25 } 26 27 private DateTime _createTime; 28 29 public virtual DateTime CreateTime 30 { 31 get { return _createTime; } 32 set { _createTime = value; } 33 } 34 }
ProductInfopost
1 public class ProductInfo 2 { 3 private int _productInfoId; 4 5 public int ProductInfoId 6 { 7 get { return _productInfoId; } 8 set { _productInfoId = value; } 9 } 10 11 private string _productName; 12 13 public string ProductName 14 { 15 get { return _productName; } 16 set { _productName = value; } 17 } 18 19 private string _productDescription; 20 21 public string ProductDescription 22 { 23 get { return _productDescription; } 24 set { _productDescription = value; } 25 } 26 27 private DateTime _createTime; 28 29 public DateTime CreateTime 30 { 31 get { return _createTime; } 32 set { _createTime = value; } 33 } 34 }
当咱们建好了模型,如今就须要考虑怎么操做这些模型。通常来讲咱们有以下几种方式网站
咱们这里不介绍第一种方式,咱们使用第二,三种方式来结合model操做数据。这里咱们额外介绍一个概念叫作基架,基价能够为咱们的程序生成样板代码,好比增长,修改,删除,查找,基架会按照咱们定义的模板生成代码,固然,这个模板咱们是能够修改的。这里,咱们就先使用基架来完成咱们上面的第二种方式,使用EF。
使用基架的方式是咱们添加一个控制器,咱们能够看到模板项,在模板项里面有以下几个选项
咱们使用第二种来生成数据操做代码。
这里我就不过多的概述EF的使用方式,由于咱们这里主要的思想是操做数据的方式,而不是特定于一种详细的数据操做方式。若是对EF有兴趣,请参考园子里EF的文章。第一步,咱们须要一个数据上下文。所谓的数据上下文,其实就是一个数据的进口与入口。咱们经过这个上下文来操做数据。由于这里咱们使用的是EF4.1.这个版本的EF是随MVC3一块儿发布的。因此,这里咱们新建一个数据上下文,新建数据上下文的方式是继承一个名为DbContext的类。
1 public class WebSiteContext:DbContext 2 { 3 public DbSet<UserInfo> Users { get; set; } 4 public DbSet<NewsInfo> News { get; set; } 5 public DbSet<ProductInfo> Products { get; set; } 6 }
这个类很简单,咱们声明了3个类型为DbSet的属性。咱们来看看这个类型的解释
其实摘要里面已经说得很清楚,执行增,删,改,查询就是获得这个集合。这里顺便多提一个概念,就是EF的使用方式
这里咱们使用的就是代码优先,另外还有一点咱们须要注意,就是代码优先约定。咱们前面提到过这个概念,MVC遵循了许多约定优先于配置的概念,这里也是同样,假设咱们有一个UseInfo类,那么EF就会假设把数据存储到在数据库中一个名为UserInfo的表中。若是要存储的对象有一个名为ID的属性,那么EF就假这个值为主键值,并把这个值赋给sqlserver中对应的自动递增标识列。这里是EF的概念,若是想了解更多,请参考其余关于EF的文章。好了,前面咱们已经建立了咱们的数据上下文。如今咱们来应用这个数据上下文生成咱们的控制。由于前面咱们已经完成了必要的前期工做,完成了咱们的Model,完成了咱们的数据操做方法(数据上下文)。下面咱们就开始完成咱们的Controller。
这里咱们使用了基架是包含读写和视图,并使用EF。咱们为咱们的Controller选定一个模型类,这里咱们新建的是一个Login控制器,因此,模型是咱们的UserInfo。数据上下文为咱们刚刚新建的数据上下文。须要注意的一点是,这里是彻底限定名。即咱们须要加上命名空间。在高级选项里有一些关于View的设置,好比自动添加脚本引用,是否引用布局。这里根据我的程序的设定。点击添加就完成了咱们Controller的建立,并且,咱们能够看到,咱们不只建立了Controller,而且,VS还帮助咱们建立了对应的View和Action。
咱们实际上是能够删除生成的View,由于在不少时候,VS帮咱们生成的View是不符合咱们本身程序的要求。因此,这里咱们删除Login下的全部的View。固然,除了Index View。由于这个View是咱们须要使用的。咱们再来看看Controller的代码。
1 public class LoginController : Controller 2 { 3 private WebSiteContext db = new WebSiteContext(); 4 5 // 6 // GET: /Login/ 7 8 public ActionResult Index() 9 { 10 return View(db.Users.ToList()); 11 } 12 13 public ActionResult Details(int id = 0) 14 { 15 UserInfo userinfo = db.Users.Find(id); 16 if (userinfo == null) 17 { 18 return HttpNotFound(); 19 } 20 return View(userinfo); 21 } 22 23 public ActionResult Create() 24 { 25 return View(); 26 } 27 28 [HttpPost] 29 public ActionResult Create(UserInfo userinfo) 30 { 31 if (ModelState.IsValid) 32 { 33 db.Users.Add(userinfo); 34 db.SaveChanges(); 35 return RedirectToAction("Index"); 36 } 37 38 return View(userinfo); 39 } 40 41 public ActionResult Edit(int id = 0) 42 { 43 UserInfo userinfo = db.Users.Find(id); 44 if (userinfo == null) 45 { 46 return HttpNotFound(); 47 } 48 return View(userinfo); 49 } 50 51 [HttpPost] 52 public ActionResult Edit(UserInfo userinfo) 53 { 54 if (ModelState.IsValid) 55 { 56 db.Entry(userinfo).State = EntityState.Modified; 57 db.SaveChanges(); 58 return RedirectToAction("Index"); 59 } 60 return View(userinfo); 61 } 62 63 public ActionResult Delete(int id = 0) 64 { 65 UserInfo userinfo = db.Users.Find(id); 66 if (userinfo == null) 67 { 68 return HttpNotFound(); 69 } 70 return View(userinfo); 71 } 72 73 [HttpPost, ActionName("Delete")] 74 public ActionResult DeleteConfirmed(int id) 75 { 76 UserInfo userinfo = db.Users.Find(id); 77 db.Users.Remove(userinfo); 78 db.SaveChanges(); 79 return RedirectToAction("Index"); 80 } 81 82 protected override void Dispose(bool disposing) 83 { 84 db.Dispose(); 85 base.Dispose(disposing); 86 } 87 }
这里帮咱们生成了对应的增,删,改,查的方法。在Login操做中,咱们是不须要增长,删除,和修改的。咱们须要的是经过用户名,密码,和登录状态来验证用户的登录。因此,这里咱们删除没必要要的代码而后增长咱们须要的方法。
1 public class LoginController : Controller 2 { 3 private WebSiteContext db = new WebSiteContext(); 4 5 public ActionResult Index() 6 { 7 return View(); 8 } 9 10 [HttpPost] 11 public ActionResult Login(UserInfo userInfo) 12 { 13 if (ModelState.IsValid) 14 { 15 UserInfo user = db.Users.FirstOrDefault(u=>u.UserName==userInfo.UserName&&u.Password==userInfo.Password); 16 if (user!=null) 17 { 18 if (user.IsLogin == false) 19 { 20 return RedirectToAction("Index","Home"); 21 } 22 ModelState.AddModelError("","用户已经登录"); 23 return View("Index",userInfo); 24 } 25 } 26 ModelState.AddModelError("", "该用户没有注册,请注册用户"); 27 return View("Index", userInfo); 28 } 29 30 protected override void Dispose(bool disposing) 31 { 32 db.Dispose(); 33 base.Dispose(disposing); 34 } 35 }
接着,咱们须要替换咱们的IndexView,由于。咱们这里须要的方法是登录,而默认基架生成的代码并无给咱们提供这个方法。因此,这里咱们须要本身定义咱们的Index View
1 @model ModelInMVC.Models.UserInfo 2 3 @{ 4 ViewBag.Title = "Login"; 5 } 6 7 <h2>Login</h2> 8 @using(Html.BeginForm("Login","Login",FormMethod.Post)) 9 { 10 @Html.ValidationSummary(true) 11 <fieldset> 12 <legend>UserInfo</legend> 13 @Html.LabelFor(m=>m.UserName) 14 @Html.TextBoxFor(m=>m.UserName) 15 16 @Html.LabelFor(m => m.Password) 17 @Html.PasswordFor(m=>m.Password) 18 19 <input type="submit" value="登录" /> 20 </fieldset> 21 }
而后,咱们须要更改咱们的路由设置,把默认页改成咱们的登录页
1 public class RouteConfig 2 { 3 public static void RegisterRoutes(RouteCollection routes) 4 { 5 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 6 7 routes.MapRoute( 8 name: "Default", 9 url: "{controller}/{action}/{id}", 10 defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional } 11 ); 12 } 13 }
添加一个HomeController,也就是咱们的主页的控制器
1 public class HomeController : Controller 2 { 3 // 4 // GET: /Home/ 5 6 public ActionResult Index() 7 { 8 return View(); 9 } 10 11 }
代码很简单,就只有一个Index Action,接着添加一个Home下的Index View。
1 @{ 2 ViewBag.Title = "Index"; 3 Layout = "~/Views/Shared/_Layout.cshtml"; 4 } 5 6 <h2>HomeIndex</h2>
一样也是很简单的,由于,咱们所要作的,仅仅是登录成功的一个跳转,固然,后续里面还会有别的操做。这个时候,其实就能够运行咱们的程序了,可是这是没有意义的,由于咱们没有任何的数据。因此,下面咱们来给咱们的程序添加数据。
初始化数据
咱们可使用EF的数据初始化策略初始化咱们程序须要的数据,首先,在config文件中,有咱们的链接字符串。
1 <add name="WebSiteContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=WebSiteContext-20131204232726; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|WebSiteContext-20131204232726.mdf" 2 providerName="System.Data.SqlClient" />
这里我使用的是本地数据库。你们能够根据本身的配置去完成数据库的配置,若是是本地数据库,会在appData中生成对应的数据库文件。在EF中,咱们可使用Database.SetInitializer方法初始化咱们的数据库
// // 摘要: // 获取或设置数据库初始化策略。在从 System.Data.Entity.Infrastructure.DbCompiledModel 初始化 System.Data.Entity.DbContext // 实例时,调用数据库初始化策略。 // // 参数: // strategy: // 策略。 // // 类型参数: // TContext: // 上下文的类型。
咱们能够看到参数为IDatabaseInitializer<TContext>。EF中两个类实现了这个接口
这里我使用的是DropCreateDatabaseIfModelChanges,泛型类型为咱们的Context
1 public class WebSiteDbInitializer:DropCreateDatabaseIfModelChanges<WebSiteContext> 2 { 3 protected override void Seed(WebSiteContext context) 4 { 5 context.Users.Add(new UserInfo() { UserInfoId=0, UserName="admin", Password="admin", IsLogin=false }); 6 context.Users.Add(new UserInfo() { UserInfoId=1,UserName="edrick", Password="123", IsLogin=true}); 7 8 context.News.Add(new NewsInfo() { NewsInfoId = 0, NewsTitle = "今天雾霾", NewsContent = "今天雾霾", CreateTime = DateTime.Now }); 9 context.News.Add(new NewsInfo() { NewsInfoId = 1, NewsTitle = "今天雾霾很严重", NewsContent = "今天雾霾很严重", CreateTime = DateTime.Now }); 10 context.News.Add(new NewsInfo() { NewsInfoId = 2, NewsTitle = "今天雾霾特别严重", NewsContent = "今天雾霾特别严重", CreateTime = DateTime.Now }); 11 12 context.Products.Add(new ProductInfo() { ProductInfoId = 0, ProductName = "雾霾口罩", ProductDescription = "雾霾口罩", CreateTime = DateTime.Now }); 13 context.Products.Add(new ProductInfo() { ProductInfoId = 1, ProductName = "口罩", ProductDescription = "口罩", CreateTime = DateTime.Now }); 14 context.Products.Add(new ProductInfo() { ProductInfoId = 2, ProductName = "医用口罩", ProductDescription = "医用口罩", CreateTime = DateTime.Now }); 15 base.Seed(context); 16 } 17 }
重写基类的Seed方法能够将新对象保存到数据库中。而后咱们须要在应用程序启动的时候注册Database.SetInitializer方法
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected void Application_Start() 4 { 5 Database.SetInitializer(new WebSiteDbInitializer()); 6 AreaRegistration.RegisterAllAreas(); 7 8 WebApiConfig.Register(GlobalConfiguration.Configuration); 9 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 10 RouteConfig.RegisterRoutes(RouteTable.Routes); 11 BundleConfig.RegisterBundles(BundleTable.Bundles); 12 } 13 }
这样咱们就完成了数据库的设置与数据的初始化。好了,咱们能够运行咱们的应用程序了。到这里,咱们完成了咱们的登录。可是,前面咱们说了,咱们要完成的是,登录以后能够看到新闻和产品。有了上面的基础咱们能够很快的建立新闻和产品的增删改操做了。
咱们开始建立咱们的新闻Controller。跟上面的步骤同样,基架选择读写/视图和EF。这里之因此使用基架,是由于它能帮咱们生成大部分咱们须要的方法和前段标签。
接着,咱们新建产品Controller
所有完成以后,咱们会看到咱们的新闻和产品都有了对应Controller和View。如今咱们要作的就是更改这些代码。由于前面咱们说了,咱们须要的只是基架帮咱们生成的一部分代码,可是它并不符合咱们的要求。这里咱们的要求就是咱们在主页上要能看到新闻和产品,因此咱们须要把这两个页面的主页合并到Home主页里面。这里就有了一个新的问题,home主页的数据从哪里来。从第一篇,MVC基础中咱们能够知道。我能够把数据封装到Viewbag中,而后传递给View,固然,这是一种解决办法。这里介绍另一种解决办法,ViewModel。
ViewModel
这里须要注意一点的事,这里的ViewModel不等于MVVM中的ViewModel。这里的ViewModel指的是咱们为特定的页面制定页面模型。好比通常的门户网站的首页,咱们须要显示少许的新闻,少许的案例,少许的产品的信息。可是这些数据又都是不一样的模型。因此,咱们就能够给这个页面创建一个特定于这个特面的模型。这里咱们就是IndexViewModel。它包含的就是新闻和产品的集合。它给Index页面提供数据。
1 public class IndexViewModel 2 { 3 public NewsInfo News { get; set; } 4 5 public ProductInfo Product { get;set;} 6 7 public IEnumerable<NewsInfo> NewsInfoList { get; set; } 8 9 public IEnumerable<ProductInfo> ProductInfoList { get; set; } 10 }
有了数据,咱们就须要整合咱们刚刚VS帮咱们生成的代码,咱们须要把News Index的代码和Product Index的代码整合到Home Index下。可是,直接复制代码是运行不了的,由于新闻和产品的Index绑定的模型的是IEnumerable<ModelInMVC.Models.NewsInfo>和IEnumerable<ModelInMVC.Models.ProductInfo>而咱们的Home Index是没有绑定这两个模型。因此咱们就要使用咱们上面定义的ViewModel。让IndexViewModel绑定到Index。修改HomeController的Index方法。
1 public class HomeController : Controller 2 { 3 WebSiteContext context = new WebSiteContext(); 4 // 5 // GET: /Home/ 6 7 public ActionResult Index() 8 { 9 IndexViewModel viewModel = new IndexViewModel(); 10 viewModel.NewsInfoList = context.News.ToList(); 11 viewModel.ProductInfoList = context.Products.ToList(); 12 return View(viewModel); 13 } 14 }
好了,数据准备好了,咱们如今把Home Index修改成以下代码
1 @model ModelInMVC.Models.IndexViewModel 2 @{ 3 ViewBag.Title = "Index"; 4 Layout = "~/Views/Shared/_Layout.cshtml"; 5 } 6 7 <h2>HomeIndex</h2> 8 <div> 9 <div style="width:49%;float:left;"> 10 <fieldset> 11 <legend>News</legend> 12 <p> 13 @Html.ActionLink("Create New", "Create") 14 </p> 15 <table> 16 <tr> 17 <th> 18 @Html.DisplayNameFor(model => model.News.NewsTitle) 19 </th> 20 <th> 21 @Html.DisplayNameFor(model => model.News.NewsContent) 22 </th> 23 <th> 24 @Html.DisplayNameFor(model => model.News.CreateTime) 25 </th> 26 <th></th> 27 </tr> 28 29 @foreach (var item in Model.NewsInfoList) 30 { 31 <tr> 32 <td> 33 @Html.DisplayFor(modelItem => item.NewsTitle) 34 </td> 35 <td> 36 @Html.DisplayFor(modelItem => item.NewsContent) 37 </td> 38 <td> 39 @Html.DisplayFor(modelItem => item.CreateTime) 40 </td> 41 <td> 42 @Html.ActionLink("Edit", "Edit", new { id = item.NewsInfoId }) | 43 @Html.ActionLink("Details", "Details", new { id = item.NewsInfoId }) | 44 @Html.ActionLink("Delete", "Delete", new { id = item.NewsInfoId }) 45 </td> 46 </tr> 47 } 48 </table> 49 </fieldset> 50 </div> 51 <div style="width:49%;float:right"> 52 <fieldset> 53 <legend>Product</legend> 54 <p> 55 @Html.ActionLink("Create New", "Create") 56 </p> 57 <table> 58 <tr> 59 <th> 60 @Html.DisplayNameFor(model => model.Product.ProductName) 61 </th> 62 <th> 63 @Html.DisplayNameFor(model => model.Product.ProductDescription) 64 </th> 65 <th> 66 @Html.DisplayNameFor(model => model.Product.CreateTime) 67 </th> 68 <th></th> 69 </tr> 70 71 @foreach (var item in Model.ProductInfoList) 72 { 73 <tr> 74 <td> 75 @Html.DisplayFor(modelItem => item.ProductName) 76 </td> 77 <td> 78 @Html.DisplayFor(modelItem => item.ProductDescription) 79 </td> 80 <td> 81 @Html.DisplayFor(modelItem => item.CreateTime) 82 </td> 83 <td> 84 @Html.ActionLink("Edit", "Edit", new { id = item.ProductInfoId }) | 85 @Html.ActionLink("Details", "Details", new { id = item.ProductInfoId }) | 86 @Html.ActionLink("Delete", "Delete", new { id = item.ProductInfoId }) 87 </td> 88 </tr> 89 } 90 </table> 91 </fieldset> 92 </div> 93 </div>
运行程序,咱们能够看到以下的显示
当咱们点击增长,修改,详细,删除的时候出现404.由于基架生成的Controller路由不符合咱们修改后的路径。因此,下一步,咱们要修改页面的连接和Contrller的部分代码。
修改以后的页面代码
1 @model ModelInMVC.Models.IndexViewModel 2 @{ 3 ViewBag.Title = "Index"; 4 Layout = "~/Views/Shared/_Layout.cshtml"; 5 } 6 7 <h2>HomeIndex</h2> 8 <div> 9 <div style="width:49%;float:left;"> 10 <fieldset> 11 <legend>News</legend> 12 <p> 13 @Html.ActionLink("Create New", "Create","News") 14 </p> 15 <table> 16 <tr> 17 <th> 18 @Html.DisplayNameFor(model => model.News.NewsTitle) 19 </th> 20 <th> 21 @Html.DisplayNameFor(model => model.News.NewsContent) 22 </th> 23 <th> 24 @Html.DisplayNameFor(model => model.News.CreateTime) 25 </th> 26 <th></th> 27 </tr> 28 29 @foreach (var item in Model.NewsInfoList) 30 { 31 <tr> 32 <td> 33 @Html.DisplayFor(modelItem => item.NewsTitle) 34 </td> 35 <td> 36 @Html.DisplayFor(modelItem => item.NewsContent) 37 </td> 38 <td> 39 @Html.DisplayFor(modelItem => item.CreateTime) 40 </td> 41 <td> 42 @Html.ActionLink("Edit", "Edit","News",new { id = item.NewsInfoId },null) | 43 @Html.ActionLink("Details", "Details", "News", new { id = item.NewsInfoId }, null) | 44 @Html.ActionLink("Delete", "Delete", "News", new { id = item.NewsInfoId }, null) 45 </td> 46 </tr> 47 } 48 </table> 49 </fieldset> 50 </div> 51 <div style="width:49%;float:right"> 52 <fieldset> 53 <legend>Product</legend> 54 <p> 55 @Html.ActionLink("Create New", "Create", "Product") 56 </p> 57 <table> 58 <tr> 59 <th> 60 @Html.DisplayNameFor(model => model.Product.ProductName) 61 </th> 62 <th> 63 @Html.DisplayNameFor(model => model.Product.ProductDescription) 64 </th> 65 <th> 66 @Html.DisplayNameFor(model => model.Product.CreateTime) 67 </th> 68 <th></th> 69 </tr> 70 71 @foreach (var item in Model.ProductInfoList) 72 { 73 <tr> 74 <td> 75 @Html.DisplayFor(modelItem => item.ProductName) 76 </td> 77 <td> 78 @Html.DisplayFor(modelItem => item.ProductDescription) 79 </td> 80 <td> 81 @Html.DisplayFor(modelItem => item.CreateTime) 82 </td> 83 <td> 84 @Html.ActionLink("Edit", "Edit", "Product", new { id = item.ProductInfoId },null) | 85 @Html.ActionLink("Details", "Details","Product", new { id = item.ProductInfoId },null) | 86 @Html.ActionLink("Delete", "Delete", "Product",new { id = item.ProductInfoId },null) 87 </td> 88 </tr> 89 } 90 </table> 91 </fieldset> 92 </div> 93 </div>
这样,咱们页面的连接均可以生效。咱们看看以前的页面连接@Html.ActionLink("Create New", "Create")。咱们简单的提一下这个方法,这个方法是连接到一个Action。若是不提供Controller名称,就会在当前contrller中寻找Action,可是这里咱们的HomeContrller中是没有Create这个Action的,因此咱们须要提供Controller名称。@Html.ActionLink("Create New", "Create","News")。这样咱们全部的页面均可以生效。可是这里咱们尚未修改对应的Controller。因此不保证程序能正确的执行。接下来,咱们来修改Contrller,这里咱们只修改NewsContrller。由于ProductController的修改方法跟NewsContrller是同样的。
CreateNew会连接到NewsController的Create Action。咱们在NewsController里面能够看到两个Create方法,可是有区别的是一个Create方法标注了[HttpPost]属性。这里HttpPost指的是,当http的请求方式为post的时候调用这个方法。没有标注则为get方式。
1 public ActionResult Create() 2 { 3 return View(); 4 } 5 6 // 7 // POST: /News/Create 8 9 [HttpPost] 10 [ValidateAntiForgeryToken] 11 public ActionResult Create(NewsInfo newsinfo) 12 { 13 if (ModelState.IsValid) 14 { 15 db.News.Add(newsinfo); 16 db.SaveChanges(); 17 return RedirectToAction("Index"); 18 } 19 20 return View(newsinfo); 21 }
能够看到,没有标注post的action仅仅只是返回一个view,也就是咱们的编辑界面。而标注了post则是具体的提交数据库。为post请求的Create在CreatView中被调用。也就是form的提交。修改和删除也是跟添加是同样的道理,这里咱们只是了解便可。之后会详细讲解。代码这里我也不列出因此,文章的最后会提供正代码的下载。
到这里,全部的代码就完成了。可是咱们还有两个很重要的概念没有解释
强类型视图
前面咱们提到,咱们有两种方式给视图传递数据,一种是使用ViewBag。一种是咱们上面使用的方式。咱们称之为强类型视图。那么这两种方式有什么区别呢?ViewBag从原理上来讲实际上是一个数据字典,只不过所存储的是动态类型。那么在前段使用的时候咱们是得不到数据的类型的。因此必要的时候咱们须要进行类型转换。
ViewBag.ViewModel = viewModel;
在使用的时候,咱们须要进行转换,由于咱们拿不到类型信息
ViewBag.ViewModel.ProductInfoList as IEnumerable<ModelInMVC.Models.ProductInfo>这种方式咱们经过转换拿到了数据类型。这对于咱们写代码来讲是比较繁琐的。咱们也可使用动态类型来迭代。可是又不能使用智能感应。@foreach (dynamic item in ViewBag.ViewModel)。有没有在一种方式可以不使用转换,又能智能感应。那就是强类型视图。
所谓强类型视图,不过是指视图与某类型进行关联,从根本上讲,就是在页面声明了某一个类型的对象。那么咱们在页面的任何地方均可以使用这个对象,这样就在页面里面创建了强类型的对象。也称之为强类型视图。对象的实例化是在返回View的时候进行的return View(viewModel);这样就完成了对象在页面的声明,赋值。以后咱们就可使用了。
模型绑定
在ASP.NET时代,咱们若是要获取http请求的参数或者窗体提交的值须要使用QueryString或者Request.Form[“”]这样的方式来获取值,这种编码方式是很乏味的,在MVC中模型绑定机制帮咱们解决了这种乏味的编码方式。咱们以前一直在说约定优先于配置。模型绑定又是一个约定优先于配置的例子。若是有一种方式让程序帮咱们自动从url或者form中取得数据,而且自动帮咱们构建对象。那么那个乏味的过程就被取代了。怎么样才能让程序帮咱们构建对象呢?可不能够,咱们遵循一种约定,那么程序就会依照这种约定帮咱们构建对象。这种约定就是命名约定,咱们不论是get请求或者post请求。只要按照一种约定去命名咱们的参数,命名咱们的数据。那么就是可能的。
若是不是复杂类型,原理其实也是同样的。这里模型绑定组件会在请求中查找数据,这里的请求能够是路由数据,查询字符串和表单集合。
好比public ActionResult Details(int id = 0)这个方法,模型绑定组件,会在请求中寻找name为id的参数,而后传递给方法。若是在操做是有参数的状况下,模型绑定会隐式的工做。可是咱们也能够显式的调用模型绑定
NewsInfo newsinfo = new NewsInfo(); TryUpdateModel(newsinfo);
这时,模型绑定会显式的工做,会从请求中查找而且构建对象。模型绑定的副产品就是模型状态。模型绑定器构建的模型的每个值都会在模型状态中有一条相应的记录,来查看模型绑定是否成功。这里涉及到验证机制,也就是说,若是完成了全部的验证,那么模型验证就会经过,若是某个值完成不了验证。那么模型绑定就会失败。这也是对提交的值的验证。ModelState.IsValid验证模型状态。若是验证失败,那么模型状态将包含致使绑定失败的属性名,尝试的值以及错误消息。下一篇,我会详细的讲解模型状态和MVC中的验证。