第 4 章 模型

官方网站:Professional ASP.NET MVC 5

本章官方代码下载:Chapter 4 Code

第3章在讨论强类型视图时, 提到了模型。 本章将详细学习模型。

模型这个词在软件开发领域被多次引用,代表数百种不同的概念,如成熟度模型、设计模型、威胁模型。很少有开发会议公自始自终都不谈一两种模型的。即便把“模型”这个术语的范围限定在MVC设计模式的上下文中, 也仍然可以探讨面向业务的模型对象和面向视图的模型对象哪个更具优势。

    本章要讨论的是那些发送信息到数据库, 执行业务计算并在视图中渲染的模型对象。换句话说,这些对象代表着应用程序关注的域,模型就是要显示、保存、创建、更新和删除的对象。

    为了仅使用模型对象的定义就能构建出应用程序的特性,ASP.NET MVC5提供了许多工具和特性。现在就应该坐下来好好想一想要解决的问题(比如怎样让一个用户购买音乐),然后为了呈现涉及的主要对象,就要编写一些简单的C#类,比如: Album类、ShoppingCart类和User类。准备好了上面的工作,接下来就可以使用MVC提供的工具来为每个模型对象的标准索引、创建、编辑和删除功能构建控制器和视图。这个构建工作称为基架,在讨论基架之前,需要首先了解一些模型。

4.1 为MVC Music Store 建模

    为了让本章可独立阅读, 新建一个项目, 新建项目的过程可以参照第1章。 

    MVC模板提供了启动应用程序需要的所有项:一个基本的布局视图、一个带有用户登录链接的默认首页、一个初始的样式表和一个相对较空的Models文件夹。Models文件夹下有两个文件:AccountViewModels.cs 和 IdentityModel.cs 文件。 这两个文件都和用户账户管理有关。 现在不需要担心它们, 第7章在讨论身份验证和身份时将详细对它们进行介绍。不过, 我们在构建应用程序的其它部分时, 使用的是与ASP.NET MVC中的账户管理系统相同的标准视图、模型和控制器, 这是一个好消息。


    为什么Models文件夹几乎是空的呢?这是因为项目模板不知道我们在哪个域中工作, 也不知道我们想要解决什么样的问题。 

    这个时候, 可能连我们自己也不明确究竟需要解决什么问题!这就需要与客户和业务负责人进行沟通交流, 做一些初步的原型设计或者使用测试驱动开发来充实设计。ASP.NET MVC框架并没有规定初始阶段的过程和方法。

    最终可能决定要构建的音乐商店首先应具有列举、创建、编辑和删除专辑信息的功能。要在Models文件夹中添加一个新的Album类, 可以右击Models文件夹, 选择Add...Class, 并将新类命名为 Album 。 保留现有的using和namespace语句不变, 并在新建的Album类中输入程序清单4-1中所示的属性:

    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 和 Artist 类还未被定义, 所以这个类还不能通过编译。 不过这不是问题, 接下来就定义它们。

注意: Visual Studio 有一段很有用的代码, 用于创建自动实现的属性。 先键入 prop , 然后按 Tab 键两次, 展开VS提供的属性代码。改完类型, 按两次Tab切换到属性名, 改完属性名按Enter可以前进到该行末尾。 创建新的模型类时, 非常实用方便。

专辑模型的主要目的是模拟音乐专辑的特性, 如标题和价格。 每一个专辑也有一个与之相关的艺术家, 使用新的Artist类建模。 为此, 在Models文件夹中添加一个新的Artist类。

    public class Artist
    {
        public virtual int ArtistId { get; set; }
        public virtual string Name { get; set; }
    }
    从上面的代码中可能会注意到, 每个Album 都有Artist 和ArtistId 两个属性来管理与之相关的的艺术家。 这里, Artist属性称为导航属性, 主要是对于一个专辑, 可以通过点操作符来找到与之相关的艺术家。

    这里称ArtistId为外键属性, 如果了解一点数据库知识的话, 就会知道艺术家和专辑会保存在两个不同的表中, 并且一个艺术家可能与多个专辑有关联。 因为艺术家记录表和专辑记录表存在着外键关系, 所以这里就将艺术家的外键值嵌入到了专辑的模型中。

    模型关系:因为外键是关系型数据库管理的实现细节, 所以一些读者可能不喜欢在模型中利用外键属性。在模型对象中并不是必须使用外键属性,所以可以不考虑它。因为外键可以为将要使用的工具提供很多便利,所以在本章利用了外键属性。

    一个专辑还会有一个相关的流派, 一种流派也会对应一个相关专辑列表。 在Models文件夹中创建一个Genre类,并添加如程序清单4-3所示的属性:

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

这里可能会注意到所有的属性都是virtual 类型的, 本章后面将会讨论这个问题。 到目前为止, 这三个简单类的定义就是建立模型的开端,其中包含了利用基架生成控制器和一些视图,甚至创建数据库需要的所有内容。

现在已经为三个模型类添加完了代码, 可以编译应用程序了。 在VS中, 既可以使用Build 菜单项, 也可以使用 Ctrl+Shift+B来编译应用程序。编译新添加的模型类很重要,这有两个原因:

可以快速检查代码中存在的简单语法错误;

同样重要的是, 在编译应用程序之前, 下一节介绍的VS基架对话框中不会显示新添加的类。 在使用基架系统前编译应用程序不只是一种良好实践,对于基架对话框中显示任何新模型或修改后的模型, 这是必要操作。

4.2 为商店管理器构造基架

    创建了模型类之后,就可以创建商店管理器了。 商店管理器是一个可用来编辑专辑信息的控制器。 可以选择的一种做法是像第2章那样手动编写控制器代码,然后为每个控制器操作创建所有必要的视图。几次之后,就会发现这种方法的重复性很强。那么,是不是可以在一定程度上自动完成这个过程呢?幸运的是答案是肯定的,我们只需要使用接下来介绍的“ 基架”,其用途并没有局限于创建视图。

4.2.1 基架的含义    

    在第3章的“添加视图”一节中,我们看到Add View对话框允许选择一个用来创建视图代码的模板。这种代码生成过程就叫做“基架”, 其用途并没有局限于创建视图。

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

    基架选项

    像MVC框架的所有其他项一样,如果不喜欢默认的基架,就可以根据自己的需要自定义基架或替换现有基架的代码生成机制。也可以通过NuGet (搜索scaffolding) 查找可替代的基架模板。NuGet库中全是运用特定设计模式和技术来生成代码的基架。

    如果确实不喜欢基架,也可以从零开始手工设计所有内容。基架对于创建应用程序来说不是不可或缺的, 但是利用基架会为应用程序开发节省很多时间。

    虽然不要期望基架能够创建整个应用程序,但是基架可以让开发人员从琐碎繁杂的工作中解脱出来, 例如,基架可以代劳在正确位置创建文件的操作,避免了开发人员完全手动来编写程序代码。可以调整和编辑基架生成的代码来创建自己的应用程序。基架只有在允许运行的时候才会运行,所以不必担心代码生成器会覆盖对输出文件的修改。

    MVC5中包含有各种基架模板。不同基架模板的代码生成量不同,所以选择不同的基架模板就会有不同的基架代码生成量。下面介绍一些常用的模板。

    1. MVC 5 Controller —— Empty

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

    2. MVC 5 Controller with read/write Actions

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

    3. Web API Controller Scaffolders

    有几个模板向项目中添加一个继承自基类ApiController的控制器。可以使用这些模板为应用程序创建Web API。 第11章将详细介绍 Web API.

    4. MVC5 Controller with Views, Using Entity Framework

    这个模板就是下面创建商店 控制器时将要选择的模板, 因为它不仅生成了带有整套 Index , Details, Create , Edit , Delete 操作的控制器及其需要的所有相关视图, 而且还生成了与数据库交互(持久保存数据到数据库或从数据库中读取数据)的代码。

    为了让模板产生合适的代码,需要选择一个模型类(这里选择的是Album类)。基架会检测所选择模型的所有属性,然后利用这些信息来创建控制器、视图、和数据访问代码。

    为了生成数据访问代码,基架需要一个数据上下文对象的名称。这里可以为基架指定一个现在的数据上下文,也可以根据需要创建一个新的数据上下文。什么是数据上下文呢?要说明这个问题,就必须首先了解一下实体框架。

4.2.2 基架和实体框架

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

    灵活的数据选项

    如果不想在ASP.NET MVC应用程序中使用实体框架,也是可以的。框架中没有强制要求与EF建立依赖关系的机制,我们可以使用自己喜欢的任何ORM或数据访问库。事实上, 框架中也没有强制必须使用数据库(不管是不是关系型的数据库)。可以使用任何数据访问技术或数据源来构建应用程序,比如,使用用逗号的文本文件或者采用使用了全套WS-*协议组件的Web服务。

    本章使用的是EF, 但是涉及的许多主题都可以广泛应用于任何数据源或ORM。

    EF支持数据库优先、模型优先和代码优先的开发风格; MVC基架采用代码优先的风格。代码优先是指可以在不创建数据库模式,也不打开VS设计器的情况下,向SQL Server中存储或检索信息。可以编写纯C#类, 因为EF知道如何将这些类的实例存储到正确的位置。

    还记得模型对象中的所有属性都是虚拟的吗?虚拟属性不是必需的,但是它们给EF提供了一个指向纯C#类集的钩子(hook),并为EF启用了一些特性,如高效的修改跟踪机制。EF需要知道模型属性值的修改时刻,因为它要在这一时刻生成并执行一个SQL UPDATE语句,使这些改变和数据库保持一致。

    谁应该放在第一位, 代码还是数据库?

    如果我们已经熟悉了实体框架,并且还在使用模型优先或者数据库优先的方法进行开发,那我们是幸运的,因为MVC基架也将支持这样做。实体框架团队设计的代码优先方案为我们提供了无障碍的环境来处理重复的编码和数据库工作。

    1. 代码优先约定

    为了使开发生活更轻松,EF像ASP.NET MVC一样, 遵照了很多的约定。例如, 如果想把一个Album类型的对象存储在数据库中, 那么EF就假设是把数据存储在数据库中一个名为Album的表中; 如果要存储的对象中有一个名为ID的属性,EF就假设这个属性值就是主键值,并把这个值赋给SQL Server中对应的自动递增(标识)键列。

    自定义约定

    如果EF中的默认约定与想要建立数据模型的方式不一致,应该怎么办?在以前的EF版本中, 解决方法是使用数据注解或者Fluent API,或者无奈地使用默认约定,因为手动配置所有选项太乏味了。

    EF6通过添中自定义约定的支持改进了这一点。使用自定义的约定可以覆盖主键定义。或者改变默认的表映射,以满足自己的团队命名约定。更好的是, 可以创建可重用的约定类和特性,应用到任何模型或属性上。这样便可以同时得到两种方法的好处:既可以按自己的需要精确配置,又可以像标准的EF开发一样轻松。

    关于EF6自定义约定的更多信息,请阅读这篇MSDN文章:http://msdn.microsoft.com/en-us/data/jj819164

    2. DbContent 类

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

    public class MusicStoreDB : DbContext
    {
        public DbSet<Album> Albums { get; set; }

        public DbSet<Artist> Artists { get; set; }

        public DbSet<Genre> Genres { get; set; }
    }

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

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

    现在了解了一点关于内置机架模板的技术,下面将继续讲解基架生成代码的过程。

    选择数据访问策略

    到目前为止,已经出现有很多种不同的数据访问方法,并且方法的选用不仅依赖于所要创建的应用程序类型,而且依赖于开发人员的个性(或者说开发团队的修改)。事实上,没有一种数据访问策略适用于所有应用程序和所有开发团队。

    本章的方法使用VS工具, 以便快速启动和运行应用程序。这样做对于代码没有什么明显的错误。然而,对于一些开发人员和项目,这种方法就太简单了。本章中使用的基架假设我们创建的应用程序需要实现基本的create, read, update, delete (CRUD)功能。现在的很多应用程序只提供CRUD功能和基本的验证功能,以及少量的工作流程和业务规则,对于这样的应用程序,基架能发挥很好的作用。

    对于更复杂的应用程序,我们想探讨不同的架构和设计模式来满足我们的需求。领域驱动设计(DDD)是一种团队使用的方法,可用来处理复杂的应用程序。命令查询职责分离(CQRS)也是一种团队开发模式,它在复杂的应用程序开发中占有主要份额。

    DDD和CQRS中使用的流行设计模式包括库和工作单元模式。如果想更多地了解这些设计模式,可参阅 http://msdn.microsoft.com/en-us/library/ff714955.aspx 。库设计模式的优势之一是, 我们可在数据访问代码和程序其他部分之间创建一个正常边界。这个边界可以提高代码单元测试的能力,这不是默认基架生成代码的优势之一,因为硬编码依赖于实体框架。

4.2.3 执行基架模板
介绍完必要的理论基础后, 现在是时候使用基架构建一个控制器了!执行下面的步骤即可:
1. 右击Controllers文件夹, 选择 Add|Controller. Add Scaffold 对话框将会打开, 如下图所示。该对话框列出了前面讨论过的基架模板。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值