1. 为MVC Music Store建模

Models文件夹(右击) --> 添加 --> 类

为类添加对应的属性:

     public class Album
     {
         public virtual int AlbumId { get; set; }
         public virtual int GenreId { get; set; }
         public virtual int ArtistId { get; set; }
         public virtual string Title { get; set; }
         public virtual decimal Price { get; set; }
         public virtual string AlbumArtUrl { get; set; }
         public virtual Genre Genre { get; set; }
         public virtual Artist Artist { get; set; }
     }

类Genre:

     public class Genre
     {
         public virtual int GenreId { get; set; }
         public virtual string Name { get; set; }
         public virtual string Description { get; set; }
         public virtual List<Album> Albums { get; set; }
     }

类Artist:

     public class Artist
     {
         public virtual int ArtistId { get; set; }
         public virtual string Name { get; set; }
     }

注意:此处的属性都是virtual

构建完类之后,需要对整个项目进行编译

2. 基架

ASP.NET MVC中的基架可以为应用程序的创建、读取、更新和删除(CRUB)功能生成所需要的样板代码。基架模版检测模型类的定义,然后生成控制器以及与该控制器关联的视图,有些情况下还会生成数据访问类。基架知道如何命名控制器、命名视图以及每个组件需要执行什么代码,也知道在应用程序中如何放置这些项以使应用程序正常工作。

2.1 基架选项:

像MVC框架的所有其他项一样,如果不喜欢默认的基架,就可以根据自己的需要自定义基架或替换现有基架的代码生成机制。也可以通过NuGet(搜索scaffolding)查找可替代的基架模版。

2.2 常用的基架模版:

(1)MVC5 Controller——Empty

该会向Controllers文件夹中添加一个具有指定名称且派生自Controller的类(控制器)。这个控制器带有的唯一操作就是Index操作,且在内部除了返回一个默认ViewResult实例的代码之外,没有其他任何代码。这个模版不会生成任何视图。

(2)MVC5 Controller with read/write Actions

该模版会向项目中添加一个带有Index、Details、Create、Edit和Delete操作的控制器。虽然控制器内部的操作不是完全空白,但不会执行任何有实际意义的操作,除非向其中添加自己的代码并为他们创建试图。

(3)Web API 2 API Controller Scaffolders

有几个模版向项目中添加一个继承自基类ApiController的控制器。可以使用这些模版为应用程序创建Web API

(4)MVC5 Controller with Views,Using Entity Framework

该模版不仅生成了带有整套Index、Details、Create、Edit和Delete操作的控制器及其需要的所有相关视图,并且还生成了与数据库交互(持久保存数据到数据库或从数据库中读取数据)的代码。

2.3 基架和实体框架:

新建的ASP.NET MVC5项目会自动包含对实体框架(EF)的引用。EF是一个对象关系映射(object-relational mapping,ORM)框架,它不但知道如何在关系型数据库中保存.NET对象,而且还可以利用LINQ查询语句检索那些保存在关系型数据库中的.net对象。

EF支持数据库优先、模型优先和代码优先的开发风格;MVC基架采用代码优先代码优先的风格。

代码优先是指可以在不创建数据库模式、也不打开Visula Studio设计器的情况下,向SQL Server中存储或检索信息。

模型对象中的属性如果设置为虚拟的,可以给EF提供一个指向C#类集的钩子(hook),并未EF启用了一些特性,如高效的修改跟踪机制(efficient change tracking mechanism)。EF需要知道模型属性值的修改时刻,因为需要在这一刻生成并执行一个SQL UPDATE语句,使这些改变和数据库保持一致。

2.4 DbContext类

当使用EF的代码优先方法时,需要使用从EF的DbContext类派生出的一个类来访问数据库。该派生类具有一个或多个DbSet<T>类型的属性,类型DbSet<T>中的每一个T代表一个想要持久保存的对象。可以把DbSet<T>想象成一个特殊的、可以感知数据的泛型列表,它知道如何在父上下文中加载和保存数据。

例如,下面的类(数据上下文类)就可以用来存储和检索Albums、Artist和Genre的信息:

     public class MusicStoreDB : DbContext
     {
         public MusicStoreDB() : base("name=MusicStoreDB")
         {
         }

         public System.Data.Entity.DbSet<MvcMusicStore.Models.Album> Albums { get; set; }

         public System.Data.Entity.DbSet<MvcMusicStore.Models.Artist> Artists { get; set; }

         public System.Data.Entity.DbSet<MvcMusicStore.Models.Genre> Genres { get; set; }

     }

使用先前的数据上下文,可以通过使用Linq查询,按字母顺序检索出所有专辑,代码如下:

 var db = new MusicStoreDB();
 var allAlbums = from album in db.Albums
                 orderby album.Title ascending
                 select album;

3. 执行基架模版

(1)右击Controllers文件夹 --> 添加 --> 控制器

(2)添加基架 --> 包含视图的MVC5 控制器(使用EF) --> 添加

(3)在“添加控制器”对话框中,选择模型类、数据上下文类,修改控制器名称。

(4)修改数据上下文类,修改为MvcMusicStoreDB

数据上下文会根据选择的模型,自动生成数据上下文类,如下所示。

      public class MusicStoreDB : DbContext
      {
          public MusicStoreDB() : base("name=MusicStoreDB")
          {
          }

          public System.Data.Entity.DbSet<MvcMusicStore.Models.Album> Albums { get; set; }

          public System.Data.Entity.DbSet<MvcMusicStore.Models.Artist> Artists { get; set; }

         public System.Data.Entity.DbSet<MvcMusicStore.Models.Genre> Genres { get; set; }

     }

注意MusicStoreDB() :  base("name=MusicStoreDB")中,MusicStoreDB是配置的数据库连接

   <connectionStrings>
     <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-MvcMusicStore-20130928021208.mdf;Initial Catalog=aspnet-MvcMusicStore-20130928021208;Integrated Security=True"
       providerName="System.Data.SqlClient" />
     <add name="MusicStoreDB" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=MusicStoreDB-20130929160340; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|MusicStoreDB-20130929160340.mdf"
       providerName="System.Data.SqlClient" />
   </connectionStrings>

如果不配置具体的连接,EF将尝试连接SQL Server的LocalDB实例,并且查找与DbContext派生类名相同的数据库。如果EF能够连接上数据库服务器,但找不到数据库,那么框架会自动创建一个数据库。

(5)视图

一旦基架运行完成,就将在新的视图文件夹Views/Album中出现一个视图集。视图提供了罗列、编辑和删除专辑的功能。

4.执行基架代码

4.1用实体框架创建数据库

4.2使用数据库初始化器

保持数据库和模型变化同步的一个简单方法是允许实体框架重新创建一个现有的数据库。可以告知EF在应用程序每次启动时重新创建数据库或者仅当检测到模型变化时重建数据库。当调用EF的Database类中的静态方法SetInitializer时,可以选择这两种策略中的任意一个。

框架中带有两个IDatabaseInitializer对象:DropCreateDatabaseAlways和DropCreateDatabaseIfModelChanges。可以根据这两个类的名称来辨别每个类所代表的策略。两个初始化器都需要一个泛型类型的参数,并且这个参数必须是DbContext的派生类。

在文件global.asax.cs中,可以在应用程序启动过程中设置一个初始化器:

源代码:

     protected void Application_Start()
     {
         Database.SetInitializer(new MusicStoreDbInitializer());

         AreaRegistration.RegisterAllAreas();
         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
         RouteConfig.RegisterRoutes(RouteTable.Routes);
         BundleConfig.RegisterBundles(BundleTable.Bundles);
     }

修改为:

     protected void Application_Start()
     {
         Database.SetInitializer(new DropCreateDatabaseAlways<MusicStoreDB>());

         AreaRegistration.RegisterAllAreas();
         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
         RouteConfig.RegisterRoutes(RouteTable.Routes);
         BundleConfig.RegisterBundles(BundleTable.Bundles);
     }

4.3播种数据库

Seed方法可以为应用程序创建一些初始化的数据。

     protected void Application_Start()
     {
         Database.SetInitializer(new MusicStoreDbInitializer());

         AreaRegistration.RegisterAllAreas();
         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
         RouteConfig.RegisterRoutes(RouteTable.Routes);
         BundleConfig.RegisterBundles(BundleTable.Bundles);
     }

播种数据:

 public class MusicStoreDbInitializer
        : System.Data.Entity.DropCreateDatabaseAlways<MusicStoreDB>
     {
         protected override void Seed(MusicStoreDB 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.99m,
                 Title = "Caravan"
             });
             base.Seed(context);
         }
     }

这样,每次重新生成音乐商店数据库时,都会有两种流派(Jazz和Rock)、两个艺术家(Al Di Meola和Rush)和一个专辑。代码会在程序启动时注册这个初始化器。

5 编辑专辑

5.1 创建编辑专辑的资源

默认的MVC路由规则是将HTTP GET请求中的 /StoreManager/Edit/5 传递到StoreManager控制器的Edit操作中,代码如下:

     public ActionResult Edit(int? id)
     {
         if (id == null)
         {
             return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
         }
         Album album = db.Albums.Find(id);
         if (album == null)
         {
             return HttpNotFound();
         }
         ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
         ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
         return View(album);
     }

下面是商店管理器的Edit视图中用来为流派创建下拉列表的代码:

     <div class="form-group">
         @Html.LabelFor(model => model.GenreId, "GenreId", new { @class = "control-label col-md-2" })
         <div class="col-md-10">
             @Html.DropDownList("GenreId", String.Empty)
             @Html.ValidationMessageFor(model => model.GenreId)
         </div>
     </div>

在视图中使用DropDownList辅助方法,Edit中的两行代码就是为了构建从数据库中所有可得到的流派和艺术家的列表,并将这些列表

存储在ViewBag中以方便以后让DropDownList辅助方法检索。

ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
  • 第1个参数指定了将要放在列表中的项
  • 第2个参数是一个属性名称,该属性包含当用户选择一个指定项时使用的值(键值 ,像52或2)
  • 第3个参数是每一项要显示的文本
  • 第4个参数包含了最初选定项的值

5.2 模型和视图模型终极版

针对专辑的编辑情形,模型对象(Album对象)并没有包含编辑专辑视图所需要的全部信息,因为另外还需要所有可能的流派和艺术家列表。针对这种问题,有两种解决方案。

基架生成代码展示了第一种解决方案:将额外的信息传递到ViewBag结构中。这个方案完全合理而且还便于实现。

第二种解决方案:强类型模型,创建一个视图特定模型的对象,将专辑信息、流派和艺术家信息传递给一个视图。这个模型可能如下定义:

 public class AlbumEditViewModel
 {
    public Album AlbumToEdit {get; set;}
    public SelectList Genres {get; set;}
    public SelectList Artists {get; set;}
 }

这样Edit操作就不需要将信息放进ViewBag,而需要实例化AlbumEditViewModel类,设置所有的对象属性,并将视图模型传递给视图。

5.3 Edit视图

当用户单击页面上的Save按钮时,HTML将发送一个HTTP POST请求,请求回到 /StoreManager/Edit/1 页面。这时浏览器会自动收集用户在表单输入中的所有信息并将这些值(及其相关的name属性值)放在请求中一起发送。这里注意input和select元素的name属性,需要和Album模型中的属性匹配。

5.4 响应编辑时的POST请求

接受HTTP POST请求来编辑信息的操作的名称也是Edit,但不同于前面看到的Edit操作,因为它有一个HttpPost操作选择器特性:

     [HttpPost]
     [ValidateAntiForgeryToken]
     public ActionResult Edit([Bind(Include="AlbumId,GenreId,ArtistId,Title,Price,AlbumArtUrl")] Album album)
     {
         if (ModelState.IsValid)
         {
             db.Entry(album).State = EntityState.Modified;
             db.SaveChanges();
             return RedirectToAction("Index");
         }
         ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
         ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
         return View(album);
     }

这个操作的作用就是接收含有用户所有编辑项的Album模型对象,并将这个对象保存到数据库中。

(1)编辑happy path

happy path就是当模型处于有效状态并可以将对象保存到数据库时执行的代码路径。操作通过Model.IsValid属性来检查模型对象的有效性。这个属性可以看作一个信号,来确保用户输入有用的专辑特性值。

如果模型处于有效状态,则执行以下的代码:

db.Entry(album).State = EntityState.Modified;

这行代码告知数据上下文该对象在数据库中已经存在,所以框架应该对现有的专辑应用数据库中的值而不要再创建一个新的专辑记录。

db.SaveChanges();

上下文生成一条SQL UPDATE命令更新对应的字段值以保留新值。

(2)编辑sad path

sad path就是当模型无效时操作采用的路径。在sad path中,控制器操作需要重新创建Edit视图,以便用户更改自身产生的错误

ASP.NET MVC5高级编程 之 模型的更多相关文章

  1. ASP.NET MVC5 高级编程 第5章 表单和HTML辅助方法

    参考资料<ASP.NET MVC5 高级编程>第5版 第5章 表单和HTML辅助方法 5.1 表单的使用 5.1.1 action 和 method 特性 默认情况下,表单发送的是 HTT ...

  2. ASP.NET MVC5 高级编程 第3章 视图

    参考资料<ASP.NET MVC5 高级编程>第5版 第3章 视图 3.1 视图的作用 视图的职责是向用户提供界面. 不像基于文件的框架,ASP.NET Web Forms 和PHP ,视 ...

  3. ASP.NET MVC5 高级编程 第2章 控制器

    参考资料<ASP.NET MVC5 高级编程>第5版 第2章 控制器 控制器:响应用户的HTTP 请求,并将处理的信息返回给浏览器. 2.1 ASP.NET MVC 简介 MVC 模式中的 ...

  4. ASP.NET MVC5 高级编程-学习日记-第一章 入门

    1.1 ASP.NET MVC 简介 ASP.NET是一种构建Web应用程序的框架,它将一般的MVC(Model-View-Controller)模式应用于ASP.NET框架. 1.1.1 MVC模式 ...

  5. ASP.NET MVC5高级编程 之 Ajax

    jQuery不仅支持所有现代浏览器,包括IE.Firefox.Safari.Opera和Chrome等,还可以在编写代码和浏览器API冲突时隐藏不一致性(和错误). 1. jQuery jQuery擅 ...

  6. 学习《ASP.NET MVC5高级编程》——基架

    基架--代码生成的模板.我姑且这么去定义它,在我学习微软向编程之前从未听说过,比如php代码,大部分情况下是我用vim去手写而成,重复使用的代码需要复制粘贴,即使后来我在使用eclipse这样的IDE ...

  7. ASP.NET MVC5高级编程 之 视图

    1.1理解视图约定 当创建一个项目模版时,可以注意到,项目以一种非常具体的方式包含了一个结构化的Views目录.在每一个控制器的View文件夹中,每一个操作方法都有一个同名的视图文件与其对应.这就提供 ...

  8. ASP.NET MVC5高级编程 之 数据注解和验证

    客户端验证逻辑会对用户向表单输入的数据给出一个即时反馈.而之所以需要服务器端验证,是因为来自网络的信息都是不能被信任的. 当在ASP.NET MVC设计模式上下文中谈论验证时,主要关注的是验证模型的值 ...

  9. ASP.NET MVC5高级编程 之 HTML辅助方法

    Html属性调用HTML辅助方法,Url属性调用URL辅助方法,Ajax属性调用Ajax辅助方法. HTML辅助方法 1.Html.BeginForm @using (Html.BeginForm(& ...

随机推荐

  1. JAVA中的NIO(二)

    一.内存文件映射 内存文件映射允许我们创建和修改那些因为太大而不能放入内存中的文件.有了内存文件映射,我们就可以假定整个文件都在内存中,而且可以完全把文件当作数组来访问. package com.dy ...

  2. 设计模式(4)建造者模式/生成器模式(Builder)

    设计模式(0)简单工厂模式 设计模式(1)单例模式(Singleton) 设计模式(2)工厂方法模式(Factory Method) 设计模式(3)抽象工厂模式(Abstract Factory) 源 ...

  3. springboot webmvc初始化:一个登陆异常引出的话题

    1.场景复现: 一个登陆异常引出的问题. 我们通过精心组织组件扫描的方式,来装配不同的子模块,形成一个可运行的应用: 在载入某个子模块后,我们发现应用虽然正常启动,但尝试登陆的时候,出现一个很异常的异 ...

  4. Webapi创建和使用 以及填坑(一)

    Webapi创建和使用 前言:在开发的过程中接口的用途有很多,而且是非常必要.通过接口我们可以跨语言的进行开发web与app或者其他程序,比如http接口.api接口..Rpc接口.rmi.webse ...

  5. nginx并发模型与traffic_server并发模型简单比较

    ginx并发模型: nginx 的进程模型采用的是prefork方式,预先分配的worker子进程数量由配置文件指定,默认为1,不超过1024.master主进程创建监听套接口,fork子进程以后,由 ...

  6. 多线程2.md

    # 多线程  VS 多进程 - 程序:一堆代码以文本形式存入一个文档 - 进程: 程序运行的一个状态 - 包含地址空间.内存.数据栈等 - 每个进程由自己完全独立的运行环境,多进程共享数据是一个问题 ...

  7. 一台Windows下配置多个Tomcat服务器

    上一篇博客<Windows下配置Tomcat服务器>讲了,如何在一台Windows机器上配置一个Tomcat服务器.这篇介绍一下如何在一台Windows机器上配置多个Tomcat. 第一步 ...

  8. 做iphone5适配时,一定要做好图片大小的调整。

  9. js获取json对象中的key和value,并组成新数组

    //比如有一个json var json = {"name" : "Tom", "age" : 18}; //想分别获取它的key 和 va ...

  10. git设置免密码登录

    设置用户名和邮箱 git config --global user.name "<username>" git config --global user.email & ...