《ASP.NET MVC 5 网站开发之美》
========== ========== ==========
[作者] (台) demo (台) 小朱 (台) 陈传兴 (台) 王育民 (台) 陈仕杰
[出版] 清华大学出版社
[版次] 2015年09月 第1版
[印次] 2017年03月 第2次 印刷
[定价] 128.00元
========== ========== ==========
【第01章】
(P004)
目前网页前端技术非常多元化,开发者一定要选择容易自定义与修改的框架作为主要开发框架。
(P005)
Model 可翻译为数据模型, “数据模型” 用于封装与应用程序在商业逻辑上相关的数据,以及对其数据操作的处理方法。
Model 并不依赖 View 或 Controller ,也就是说 Model 不需要知道它会如何被显示或如何被应用,只需要专注于做好数据访问、定义、验证的责任即可。
在 ASP.NET MVC 中推荐使用强类型的方式设计 Model 层,才可以有效地利用内建的模型绑定 (Model Binding) 与模型验证机制。
(P007)
在项目初期设计的时候建议将用户接口层和商业逻辑层明确地分离开,后续在开发上也会比较顺手。
(P010)
开发人员可以在同一项目中增加许多核心引用。
(P011)
Single Page Application 模板只使用单一页面来呈现各种各样的内容,单一页面的数据交换依赖 JavaScript (Client) 调用 Web API (Server) 获取,所以当开发人员选择使用 Single Page Application 模板时会发现默认同时勾选 MVC 与 Web API 。
(P012)
T4 为 “Text Template Transformation Toolkit” 的缩写,是一种文字模板转换的工具,利用 T4 可以很容易地生成程序代码。
(P017)
现在网络交换数据的格式,从以往的 XML 已经渐渐转换到更省流量的 JSON 。
(P022)
NuGet 会将此项目用到的程序包记录在 packages.config 内,因此程序包还原必须依赖此配置文件,此配置文件请务必加入版本管理。
(P037)
MVC 的设置方式是后端会将指定的数据传输到前端,不管后端开发者是使用 ViewBag 、 ViewData 、 TempData 或是直接传一个 Model 到前端,默认的 DefaultModelBinder 类都会自动将名称相同的部分自动绑定。
【第02章】
(P060)
程序是由数据和算法组成的,在 MVC 应用程序中,算法由 Controller 提供,而数据当然就是由 Model 来提供。
(P061)
既然 Model 是程序外部提供的数据,也就代表了 Model 并不限于特定技术 (例如 EF) 。
(P062)
Model 不会主动与数据源沟通,而是利用一个中介层,这个中介层规定了数据源要怎么处理 Model ,包括数据源的访问和 Model 间的数据转换等,这个中介层称为 Repository (主数据资源库) , Repositpry 负责 Model 和数据源间的协调合作。
(P065)
泛型的表示法由类与类型参数 (type parameter) 所组成,以 List
泛型也可以用在属性与方法上。
(P068)
如果一个泛型类要使用两个以上的类型参数时,可以针对各个不同的类型参数设置 where 的限制,以确保类型参数是必要的。
(P074)
SQL 指令一定要用参数化查询法,否则就会出现 SQL Injection 的漏洞。
(P082)
参数化查询是目前唯一可预防 SQL Injection 漏洞的方法。
【第03章】
(P085)
LINQ 真正的威力其实并不只是 SQL 指令的终结,而是它隐含对集合对象的强大访问能力,以及为了实现 LINQ 技术而在 .NET Framework 内所添加的各种扩展能力,这些能力才是 LINQ 真正的价值所在。
(P089)
LINQ 本身的基础建设实现于 System.Linq 命名空间内,若没有使用 using 引入这个命名空间的话,所有 LINQ 功能都无法使用。
(P091)
对象初始化器 (object initializer) 允许在程序中通过声明的方式直接给对象属性进行数值的初始化,而不必刻意建立有参数的构造函数,可降低程序员维护多个构造函数的负担。
(P092)
对象初始化器可和有参数的构造函数并用。
对于数组或集合的赋值,刻意利用相似的语法来实现,不过在初始化器内所初始化的不是集合的属性,而是集合内的元素。
(P093)
匿名类型虽然好用,但它有几个麻烦的限制 :
1. 匿名方法一般只会用在同一个函数内,如果要让它被其他函数共享,则必须要动用到 Reflection ,或是利用 .NET 4.0 提供的动态类型 (dynamic types) 机制;
2. 匿名类型只能有属性,不可以有方法、事件或字段等;
3. 两个匿名类型对象的相等 (equal) ,必须要两个对象的属性值都相等才行;
4. 匿名类型的初始化只能利用对象初始化器来进行,其属性在生成后会变成只读;
基本上,只要是匿名类型就一定得使用 var 来声明,若是现有的类型则要看当时情况而定,全都用 var 来替代也是有副作用的。
(P094)
var 类型虽然好用,但也有如下限制 :
1. 使用 var 类型时赋值语句的右边不可以是 null ,否则编译器无法推断出其类型;
2. var 类型只能用于局部变量的声明,不能用于全局变量、类层级的变量或是函数的返回值;
3. var 类型不可用在匿名委派或是方法群组中;
编译器在程序中看到 foreach 语句时,会将它转换成对 IEnumerable
(P095)
微软提供了一个指令 yield ,它可以让程序员以只传回每个元素的方式来自动生成 IEnumerabel
(P096)
Enumerable 内所包含的 LINQ 扩展方法,都返回了 IEnumerable
Fluent Interface 具有三项特性 :
1. 通过调用方法来定义对象内容;
2. 对象会自我引用 (self-referential) ,且新的对象内容会和最后一个对象内容等价;
3. 通过返回 void 内容 (就是 null 或不返回最后的对象内容) 或非 Fluent Interface 的对象结束;
(P097)
事件 (event) 机制均以委派为主,所以若要在类声明一个事件,则必定要声明一个委派。
委派的声明类似于抽象方法的声明,只是将 abstract 换成 delegate 而已,不需要实现,只要声明参数原型即可。
委派可以保证目标对象的实现方法必定符合委派所声明的参数规格,以及返回的数据值,所以适合在需要由目标对象实现的流程,或是需要通知 (notification) 或是 发行/订阅 (publish/subscrible) 的应用中,亦可应用于观察者模式 (Observer Pattern) 的实现。
(P098)
到了 .NET Framework 3.5 时,微软更将 delegate 指令拿掉,以 “=>” 指令来替代 delegate 。
Lambda 表达式分成三种,一种是表达式型 Lambda (Expression Lambdas) ,一种是语句型 Lambda (Statement Lambdas) ,另一种则是异步型 Lambda (Asynchronous Lambdas) 。
表达式型 Lambda 的语法很简单 : (input parameter) => expression ;
输入参数有或没有都可以,若没有可直接以 “()” 来替代,若只有一个,则可以省去小括号,但若有两个以上参数,就必须保留小括号。
当表达式有多行时,则要选择语句型 Lambda ,它的语法结构和表达式型 Lambda 相似,差别就在大括号 (input parameter) => { statements; } 。
(P099)
若语句只有一行,那么使用表达式型 Lambda 和语句型 Lambda 只有大括号的差别。
当 Lambda 表达式要用于异步操作时,可使用异步型 Lambda ,异步型 Lambda 语法基本上和表达式语句型 Lambda 相同,差异是在声明 Lambda 时要加上 async 指令,若在 Lambda 表达式本体内用到了其他异步方法时,要在调用加上 await 指令。
async 和 await 指令适用于 .NET Framework 4.5 以上的版本。
(P100)
微软在 .NET Framework 里面放入了数个委派类型,用来简化声明委派的工作,像是无事件参数的委派 EventHandler ;有事件参数的委派 EventHandler
Predicate
(P103)
除非是要重新组装数据或是编写 LINQ 语句选取数据,否则基本上用到 Select() 的机率并不高。
(P107)
GroupBy() 本身具有延迟执行的特性,而 ToLookUp() 没有。
(P111)
如果要设置多重排序条件,请务必使用 OrderBy() 加上 ThenBy() 的组合。
(P113)
在数据库查询时,为了达到最佳的性能,在数据量大时要进行分页处理 (paging) 是一件很重要的工作。
若是要在大集合内切出少量数据时,还需要有“美工刀”,这些方法的功能就是扮演着美工刀的角色 : Skip() 、 SkipWhile() 、 Take() 、 TakeWhile() 。
(P114)
Skip() 用来在集合中跳跃,让 LINQ 核心直接将游标跳到指定的位置,而不用通过 “巡航” 来移动,在大型集合中可节省不少时间。
Take() 用来传回集合中特定数量的元素,它会告知 LINQ 核心直接返回它所指定的元素数量,很适合使用于分页的功能。
(P117)
针对远程数据源,使用 Any() 来判断有无数据是较好的选择。
若是针对本地的集合 (数组、列表或字典类型等) ,则 Any() 和 Count() 几乎没有性能上的差异。
【第04章】
(P136)
当数据变动完成时,程序员只要调用 ObjectContext
(P156)
基本上,在 EF 6.0 内所有数据库的操作 (除了 Entity Client 外) 都要经由 DbContext 来进行,也就是说,若想要用程序代码操作 EF ,就必须要从 DbContext 派生自己的 Model 类。
若要定义在 EF 中使用的表格或视图表,则要在 DbContext 的派生类内定义以 DBSet
(P163)
基本上,若可以用关联来解决继承的需求,就不要使用继承。
(P170)
在商用系统中,存储过程和函数的使用十分广泛。
(P172)
无论是使用何种方式建立模型,对程序员来说,只有一个对象要关注,那就是 DbContext 类对象,所有的数据访问动作都要通过它。
DbSet
(P173)
DBContext 的查询是由 DbSet
EF 6 在对象查询的性能上做了许多改进,因此让应用程序的性能会更好。
【第05章】
(P191)
网址在 SEO 中拥有很高的比重,如果能有优秀的设计,那么网址本身就是很好的关键词。
(P206)
只要记住利用空字符串可以回到主层而明确指定 Area 名称,就可以前往各个层的 Area 内。
【第06章】
(P211)
在 ASP.NET MVC 中默认的返回 ActionResult 类型,这个 ActionResult 类型本身是一个抽象类,可以返回任何实现 ActionResult 类的类型内容,换句话说,它封装 Action 方法的结果,是 Action 方法结果的基类。
ASP.NET 目前实现了 9 种派生自 ActionResult 类的结果类型供开发者选择。
(P213)
新添加的 View 会继承 System.Web.Mvc.ViewPage 类, ViewPage 类提供许多有用的属性与方法,以帮助呈现 HTML 的输出,其中包含如 ViewData、 ViewBag、 TempData 等对象字典属性。
(P216)
调用 HTTP POST 的 Action 方法需要额外设置 HttpPost 属性,调用 HTTP GET 的 Action 方法也有 HttpGet 属性。但 ASP.NET MVC 里所有 public 修饰词的 Action 方法默认会套用 HttpGet 属性,一般不会特别设置。
(P228)
ViewData 与 ViewBag 都有一个特性,它们都无法跨 Action 方法访问,只能存在于当次的 HTTP 请求中。
(P229)
ViewData 会在 Action 方法调用 ViewPage 时自动传递过去。
dynamic 对象会略过编译时静态类型检查,而解析工作会改变在运行时进行 (有错误也要此阶段才知道) 。
(P230)
使用 ViewBag.KeyName 并将数据存储于 ViewData 的字典中。效果等同于 ViewData ,不过因为动态的关系,性能会比 ViewData 差,就操作而言,所有特性都和 ViewData 一模一样,但可以得到一个程序代码输入时的优点。
(P231)
ViewBag 属性在输入时使用 “对象.属性” 的输入方法,对于程序代码编写比字典更为直观 (而且输入更少字) 。
ViewBag 会在 Action 方法调用 ViewPage 时自动传递过去。
(P236)
ViewData 与 ViewBag 属性无法跨 Action 方法传递数据,当需要在 Action 方法之间传递数据的,可采用 TempData 属性。
(P245)
一个 ViewPage 只能声明一个 @model 关键字。
(P248)
ViewModel 应该只封装属性, ViewModel 与 View 之间最好只有一对一的关系。
只带属性的 ViewModel 类本质上是一种 Data Transfer Object (DTO , 数据传输对象) 的应用。
(P251)
运用 Tuple 对象编写读取使用的程序代码可读性不好,但可以省下不少项目中定制化的 ViewModel 类。
(P253)
ModelBinding 是一个自动化机制,主要是通过 DefaultModelBinder 类进行自动化数据转换工作。
(P254)
DefaultModelBinder 运行机制非常依赖
DefaultModelBinder 类基本上已经可以应付实际运用中 95% 以上的需求。
(P263)
在 ASP.NET MVC 使用 DataAnnotations 另一个好处是,它是一个集成好用户端与服务器端的验证环境,换句话说,虽然只是在 Model 属性上设置验证规则,但此验证规则会同时在服务器端与用户端 (View) 生效,这省下了非常多的开发时间,同时也为服务器提供了基本的安全数据源环境。
T4 是一种用程序代码生成程序代码的技术。
(P267)
在 ASP.NET MVC 5.1.1 (含 5.1.1 版本) 之后的版本, MaxLength 与 MinLength 属性支持用户端验证,之前的版本只支持服务器端验证。
(P272)
目前 HTML 5 的 forms 的 input 类型支持 color 、 date 、 detetime 、 datetime-local 、 email 、 month 、 number 、 range 、 search 、 tel 、 time 、 url 、 week 等,具体实现的程度各浏览器有所差异。
(P275)
CustomValidation 属性类只在服务器端有效果。
(P287)
ModelState 是服务器端一道重要的防线,可以不用了解它的工作原理,使用起来就是简单的判断式。
(P288)
如果在某些情况下,不希望 ModelState 对象被传递至 View ,使用 Clear 方法将 ModelState 字典清除即可。
(P291)
UpdateModel 方法 (MVC 5) 内部其实是调用 TryUpdateModel 方法 (MVC 5) 来处理, TryUpdateModel 方法 (MVC 5) 执行之后会返回一个 bool 值,而 UpdateModel 方法 (MVC 5) 则多做了一层弹出例外的动作。
(P297)
每一个 Controller 的 Action 方法操作完成后,最终结果一定要返回一个实现 ActionResult 抽象类的类型。
(P301)
CSV 简单说就是使用逗号来分割每一个字段数据。
(P304)
ASP.NET MVC 为避免 JSON Hijacking 攻击,默认不允许 HTTP GET 请求。
(P317)
一个可下载的文件必须提供两个必要信息与一个选择性信息。第一个是要下载的文件内容,文件内容的来源由 3 个 FileResult 子类来提供。第二个是 ContentType ,是一种因特网媒体类型 (Internet Media Type) ,当在网络上传递数据时,用户端与服务器端会沟通所要使用的 ContentType ,通常是多对一的选择,意思是,用户端会提供多个选项让服务器来选择,服务器端会从用户端提供的选项中选择其中一种支持的选项来进行响应。
(P320)
HttpPostedFileBase (System.Web 命名空间) 是一个抽象类,它可以获取用户端上传的单个文件。
(P333)
ASP.NET MVC 默认执行 WebFormViewEngine 再执行 RazorViewEngine 。
(P334)
PartialViewResult 的结果不合适直接输出,适合拿来组合或重复组合。
(P335)
名称按照习惯, MasterPager 会在最前方加 “_ (底线)” , PartialView 会在最前方加 “_ (底线)” 后面加上 Partial 以便区分。
(P337)
HostingEnvironment 类 (比 System.Web.Hosting 命名空间) 和 Server.MapPath 方法效果一样,不过未来的执行 (常驻) 环境会比现在灵活许多。
(P340)
新 Authentication Filter 的执行顺序优先于以往所有的 Filters 。
在信息安全上 AAA 是一种标准做法,它是 Authentication (验证) 、 Authorization (授权) 、 Accounting (账户) 的缩写。
Authentication : 确认用户的唯一身份;
Authorization : 用户能做什么或不能做什么;
(P347)
Authorization Filter (授权过滤器) 在 ASP.NET MVC 4 (含) 之前是最早执行的 Filter ,从 ASP.NET MVC 5 会在 Authentication Filter 之后执行。
(P356)
OutputCache 属性是 ASP.NET MVC 高速缓存机制的实现,基本上是移植 Web Forms 的 @OutputCache 输出高速缓存架构, OutputCache 属性 (ASP.NET MVC) 唯一不支持的是 @OutputCache 的 VarByControl 属性。
【第07章】
(P380)
Windows 进程 (Precess) 是一个执行的程序,正式地说,进程是一个系统层级用来描述一组资源和程序执行所需的内存份分配。对于每一个被加载到内存的 .exe ,在它的生命周期中操作系统会为它建立一个单独且隔离的进程。
(P381)
每个 Windows 进程都有用于进入程序的进入点 (entry point) 的主线程 (Main Thread) 。线程是进程中的基本执行单元,换句话说 : 进程的进入点建立的第一个线程被称为主线程。
(P382)
线程是 Windows 进程中的独立执行单元,每个线程都有一个主线程 (在执行进入点时建立) ,并且每个线程还可以以程序方式建立新的线程。
(P388)
Task.Run 方法是 .NET Framework 4.5 提供的新方法,在 .NET Framework 4.0 可以调用 Task.Factory.StartNew 方法,可以实现相同的效果。 Task.Run 方法相当于 Task.Factory.StartNew 方法的缩写方式。
(P393)
加入了 async 修饰词后必须了解以下内容 :
1. async 修饰词指示编译器将 await 运算符视为一个关键字;
2. async 修饰词只能使用在返回 void 、 Task 与 Task
有 async 修饰词不一定有 await 运算符,但有 await 运算符一定有 async 修饰词。
按照微软的建议,开发人员自己编写的异步方法最好也采用 “*Async” 结尾。
【第08章】
(P412)
执行阶段通过 ViewResult 执行 View 之前都会先执行 ViewStart.cshtml ,所有开发者可以在这个文件中设置属于全站 View 的共同默认特征,如此一来就不必在每个 View 中各自指定。
(P413)
在 Views 中会发现名为 Shared 的目录,如同其名,这个目录是用来存放全站通用的共享型文件的,如 _Layout.cshtml 以及 Error.cshtml 。
在 View Engine 搜索 View 的算法中,定义了多个搜索路径, ~/Views/Shared 目录是搜索可用 View 的最后一个尝试。
(P414)
View 文件的配置惯例,事实上是 ASP.NET MVC 运行阶段 View Engines 搜索文件的方式。
(P420)
在执行顺序上, View 会被优先执行,然后被 View 引用的 Layout 执行。
(P425)
需要注意 ViewData 、 ViewBag 两者之间并不相同,设置在 ViewBag 中的数据不会出现在 ViewData 中。
(P426)
当 ViewResult 的 ExecuteResult 被执行后会启动 View Engine 工作,完成以下的事情 :
1. 让系统中注册的 View Engine (s) 根据 context 获取应该使用的 View ;
2. 让 View (模板程序) 与 Model (数据) 结合;
3. 产生的运行结果 (HTML) 写到 Response 中;
(P429)
这里所说的 Razor 内容转换,都是调用对象的 ToString() 方法获取结果,即便对象是一个 string 类型对象也是如此。
(P430)
开发者可以使用 “@@ (连续两个@)” 的标记方式来让 Razor 直接输出为 “@” 。
(P433)
在 View 里定义预期的 Model ,使用的是 @model 指示词。
(P434)
Model 只能有一个类型,所以 @model 指示词也只能在 View 中出现一次。
在 View 里面使用到类库时,与编写一般类时相同,必须引入该类库的名称空间,在 View 里面使用 @using 来导入该名称空间,指示词不必以 “; (分号)” 结束。
Razor View Engine 提供定义全站 View 引用 namespace 的地方,它默认存在 ~/Views/web.config 中。
Razor 使用 @section 来将内容 “填入” 到 Layout 额外提供的保留区段。
(P443)
Helpers 在 ASP.NET MVC 中的协助开发角色占据重要位置,并且不同用途的 Helper 由 ASP.NET MVC Framework 中直接提供,主要有三个 Helper : UrlHelper 、 HtmlHelper 、 AjaxHelper 。
在开发 View 时可分别使用 Url 、 Html 、 Ajax 等属性来获取 UrlHelper 、 HtmlHelper 、 AjaxHelper 的实例。
UrlHelper 的主要功能就是用来生成与特定 Controller / Action 及其参数匹配的网址。
UrlHelper 的实例 (instance) 在 WebViewPage 及 Controller 中以只读的 Url 属性来提供,开发人员可以直接取用 Url 属性获得,不必自行生成。
(P445)
HtmlHelper 是在 View 中最常见到的 Helper ,可分为三大类型 : 常规类、窗体类及功能类。
HtmlHelper 的实例 (instance) 在 WebViewPage 中以一个只读的 Html 属性来提供,开发人员可以直接取用 Html 属性获得,不必自行生成。
使用 HtmlHelper 时,有很大一部分方法依赖 Model 与 Lambda Expression ,这也是 HtmlHelper 中功能强大的部分,方法名称中结尾为 For 的这一系列方法都是以 Lambda Expression 的方式来取代以字符串来声明对象 (Model) 的属性或字段名。
(P448)
ActionLink 方法为开发者提供了最简便的方式生成 超链接标签。
(P450)
Raw 方法将输入的字符串参数直接包装为 MvcHtmlString 对象后返回,这样就避免了在 Razor 的 @Expression 中会隐含调用 Html.Encode 的情况。
(P451)
由于 using { } 区块在关闭时会调用 BeginForm 返回 MvcForm 实例的 Dispose 方法,内含的 EndForm 被调用,所以不需要自行调用 EndForm 方法。
(P465)
AjaxHelper 的实例 (instance) 在 WebViewPage 中以只读的 Ajax 属性来提供,开发人员可以直接取用 ajax 属性获得,不必自行生成。
【第10章】
(P525)
Elmah 管理外来因素造成的错误收集与管理, NLog 管理内部错误的收集与管理。
【第11章】
(P552)
在密码存储上,应当采取哈希的方式,在存储与更新之前,明文密码一律使用哈希加密,然后才可以存储进数据库。
(P553)
ASP.NET 加密算法绝大部分集中在 System.Security.Cryptography 命名空间。
(P554)
AES 类是 ASP.NET 官方所推荐的对称加密算法。
哈希算法是单向函数,主要用于验证数据的完整性,无法逆向解出原始明文数据。
哈希函数可接受任意长度的明文数据,但会生成固定长度的加密文。
【第13章】
(P623)
在 JSON 里表示一个 Object 需符合以下条件 :
1. 以 { (左大括号) 开始,以 } (右大括号) 结尾;
2. 每个名称后跟着一个 : (冒号) ;
3. 多个 “名称 : 值” 之间用 “,” (逗号) 分隔;
在 JSON 里表示一个 Array 需符合以下条件 :
1. 以 [ (左括号) 开始,以 ] (右括号) 结尾;
2. 值与值之间使用 “,” 分隔;
【第14章】
(P637)
敏捷开发期望的是,每一次的需求变动,都是软件开发的动力,而每一次的变动,都是质量的累积,并使得产品更符合用户的需求。