目录
[第一篇] ASP.NET MVC 快速入门之数据库操作(MVC5+EF6) http://www.cnblogs.com/sanshi/p/6210695.html
[第二篇] ASP.NET MVC 快速入门之数据注解(MVC5+EF6) http://www.cnblogs.com/sanshi/p/6211164.html
[第三篇] ASP.NET MVC 快速入门之安全策略(MVC5+EF6) http://www.cnblogs.com/sanshi/p/6211226.html
[第四篇] ASP.NET MVC 快速入门之完整示例(MVC5+EF6) http://www.cnblogs.com/sanshi/p/6211259.html
[番外篇] ASP.NET MVC 快速入门之免费 jQuery 控件库(MVC5+EF6) http://www.cnblogs.com/sanshi/p/6223164.html
请关注三石的博客: http://cnblogs.com/sanshi
新建项目
打开 VS2015, 找到菜单项 [文件 -> 新建 ->项目], 打开向导对话框:
注意我们的选择项:
运行平台:.NET FrameWork 4.5
项目模板: ASP.NET web Application (.NET Framework)
项目名称: AspNetMvc.QuickStart, 如果你在跟着本教程练习, 建议起相同的项目名称, 方便直接拷贝代码到你的项目中.
点击 [确定] 按钮, 向导会带我们到另一个选择对话框:
由于本教程是快速入门, 所以我们从最简单的入手, 只勾选必需的选项:
不进行身份验证. ASP.NET MVC 提供了完善的身份验证方案, 我们会有单独的文章讲解.
仅勾选 MVC 引用.
点击[确定],VS2015 会创建一个可直接运行的项目, 按下快捷键[Ctrl+F5], 不调试直接运行:
默认的目录结构如下:
如果你之前在 WebForms 下进行开发, 对其中的一些文件夹和文件应该很熟悉了:
Web.config: 项目配置文件, 里面保存项目配置参数以及数据库连接字符串.
packages.config:Nuget 配置文件
Global.asax: 全局代码文件, 提供应用程序级别以及会话级别的事件处理函数, 可以在 Application_Start 中注册全局变量.
favicon.ico: 浏览器地址栏图标, 在 HTML 的 head 标签中引用.
App_Data: 放置本地数据库文件, 比如 LocalDB 生成的数据库文件.
下面几个文件夹, 用来放置静态文件, 从名称就可以方便的猜出其用途:
Scripts: 放置静态脚本文件, 比如 jQuery 等.
fonts: 放置图标字体文件, 比如流行的 FontAwesome 字体等.
Content: 放置静态文件, 比如 xml 文件, Bootstrap 的 CSS 库.
下面几个文件是 ASP.NET MVC 新引入的:
App_Start: 用来放置应用初始化类, 这个是 MVC4 引入的一个命名约定, 其实这就是一个普通的文件夹, 没有特殊的含义.
Controllers: 控制器类.
Models: 模型类, 比如 EF CodeFirst 的模型定义.
Views: 视图文件, 最初的视图引擎是 WebForms View Engine, 使用和 ASPX 文件相同的语法, 而现在用的 Razor 视图引擎是 MVC3 引入的, 以 cshtml 为后缀.
页面流程
首先看下 [About]页面:
这个页面之所以能够呈现在我们眼前, 经历了三个主要流程:
MVC 的路由引擎根据 URL 查找相应的控制器(HomeController.cs).
控制器的操作方法 About 准备数据, 然后传入视图 Home/About.cshtml.
视图准备 HTML 片段, 放入布局页面并返回浏览器.
路由引擎 ->控制器
一切还得从 Global.asax 中说起, 在其中的应用程序启用事件中, 我们需要注册路由处理器:
- protected void Application_Start()
- {
- AreaRegistration.RegisterAllAreas();
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- BundleConfig.RegisterBundles(BundleTable.Bundles);
- }
RouteConfig.cs 类位于 App_Start 文件夹中, 我们来看下内容:
- public class RouteConfig
- {
- public static void RegisterRoutes(RouteCollection routes)
- {
- routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
- routes.MapRoute(
- name: "Default",
- url: "{controller}/{action}/{id}",
- defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
- );
- }
- }
这里注册一个名为 Default 的路由规则, 对应的 URL 是{controller}/{action}/{id}, 这里三个占位符分别表示:
{controller}: 控制器, 默认是 Home, 对应的控制器类是 HomeController.cs.
{action}: 控制器里面的方法, 默认是 Index. 所以如果用户直接通过 http://localhost / 访问系统时, 默认调用 Home 控制器中的 Index 方法处理.
{id}: 参数 ID, 可选项, 这个参数对应于操作方法中的 id 参数.
控制器方法 ->视图
通过上面的介绍, 我们就知道了 http://localhost:55654/Home/About 网址对应于 Home 控制器的 About 方法.
我们在 Controllers/HomeController.cs 中找到相应的方法:
- public ActionResult About()
- {
- ViewBag.Message = "Your application description page.";
- return View();
- }
ViewBag 是一个动态对象(dynamic), 可以用来存储任意参数, 用来从控制器向视图传递数据.
从控制器向视图传递数据一般有两种方法:
传入模型, 然后在视图中通过 Model 对象访问, 这是一种强类型的方式, 也是推荐的做法. 其局限性就是只能传入一个模型, 如果需要传入多个模型对象, 就需要自定义类来包含多个模型, 另一种方法就是 ViewBag.
ViewBag, 视图包传递数据非常方便, 但是在视图中可能需要进行强制类型转换. 在常见的传入一个主模型和多个次模型时, 可以把多次模型放到 ViewBag 中, 从而避免自定义类的麻烦.
作为命名约定, 这个操作方法会自动调用相应名称的视图文件 About.cshtml.
视图 ->浏览器
下面来看 About.cshtml 视图文件:
- @{
- ViewBag.Title = "About";
- }
- <h2>@ViewBag.Title.</h2>
- <h3>@ViewBag.Message</h3>
- <p>Use this area to provide additional information.</p>
以 @开头用来输出 C# 代码的运行结果, MVC 会自动判断于何处结束 C# 代码, 并转入 HTML 代码.
需要注意, 页面第一行的 @{ }用来执行一段 C# 代码, 不会输出内容, 这里定义了一个 ViewBag.Title 的变量, 并在下面的代码中使用 @ViewBag.Title 输出到页面中.
很多初学者可能有些疑惑, 为啥控制器中定义了 ViewBag.Message, 而在视图中定义了 ViewBag.Title, 这两者有啥区别?
一般来说是没有功能的区别, 仅仅是语义的区别. 在视图中定义的变量仅在视图中使用, 比如这里定义的 ViewBag.Title 不仅在 About.cshtml 中使用, 而且在布局视图 Shared/-_Layout.cshtml 中也用到了.
布局视图
布局视图类似于 WebForms 中的母版页, 具体的视图页面会作为一部分嵌入到布局视图中, 然后返回到浏览器形成一个完整的页面.
每一个视图页面默认会使用 Views/_ViewStart.cshtml 中的定义的内容:
- @{
- Layout = "~/Views/Shared/_Layout.cshtml";
- }
这里面指定了布局视图的位置, 我们来简单看下布局视图的内容:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>@ViewBag.Title - My ASP.NET Application</title>
- @Styles.Render("~/Content/css")
- @Scripts.Render("~/bundles/modernizr")
- </head>
- <body>
- <div class="navbar navbar-inverse navbar-fixed-top">
- <div class="container">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- @Html.ActionLink("Application name", "Index", "Home", new { area = ""}, new { @class ="navbar-brand" })
- </div>
- <div class="navbar-collapse collapse">
- <ul class="nav navbar-nav">
- <li>@Html.ActionLink("Home", "Index", "Home")</li>
- <li>@Html.ActionLink("About", "About", "Home")</li>
- <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
- </ul>
- </div>
- </div>
- </div>
- <div class="container body-content">
- @RenderBody()
- <hr />
- <footer>
- <p>© @DateTime.Now.Year - My ASP.NET Application</p>
- </footer>
- </div>
- @Scripts.Render("~/bundles/jquery")
- @Scripts.Render("~/bundles/bootstrap")
- @RenderSection("scripts", required: false)
- </body>
- </html>
其中 head 标签下面的 title 中使用了在 About 视图中定义的 ViewBag.Title 属性.
这个布局视图使用 Bootstrap 库定义的 CSS 样式来完成, 包含标题栏, 导航菜单, 以及页脚的定义, 具体的内容会嵌入 @RenderBody()的地方, 最终形成完整的 HTML 页面返回.
数据库操作
上面从控制器传入视图的数据是硬编码的一个字符串, 实际项目中则经常需要从数据库中读取数据, 我们使用微软推荐的 Entity Framework CodeFirst 开发模式来创建和使用数据库.
安装 Entity Framework
首先需要安装 EF, 在 VS2015 中找到 [工具] 菜单, 然后找到 NuGet 包管理器:
转到 [浏览] 选项卡, 可以搜索 Entity Framework, 安装其最新稳定版到项目中:
安装后, 会自动更改 Web.config 添加相应的配置信息.
创建模型
我们计划完成一个简单的学生管理系统, 包含基本的增删改查(CRUD).
首先在 Models 文件, 创建学生 (Student) 的模型类:
- public class Student
- {
- public int ID { get; set; }
- public string Name { get; set; }
- public int Gender { get; set; }
- public string Major { get; set; }
- public DateTime EntranceDate { get; set; }
- }
然后创建数据库操作上下文, EF 需要这个文件来创建和访问数据库:
- public class StudentDbContext : DbContext
- {
- public DbSet<Student> Students { get; set; }
- }
由于这个类继承自 EF 的 DbContext 基类, 因此需要在文件头部添加如下引用:
using System.Data.Entity;
创建完这两个文件, 需要重新编译项目(快捷键 Ctrl+Shift+B), 否则下面添加控制器时会出错.
添加控制器
在 Controllers 目录上点击右键, 添加控制器, 弹出向导对话框:
这里选择 MVC 5 Controller with views, using Entity Framework, 然后进入设置对话框:
在这个对话框中, 我们需要指定刚才创建的模型类 (Student) 和数据访问上下文类(StudentDbContext), 然后 VS 不仅可以自动创建视图, 而且使用 EF 自动创建 CRUD 的全部代码, 是不是很酷!
全部功能完成了!
是不是很惊奇, 我们甚至没来得及写视图代码, 没有配置数据库, 没有写 CRUD 的逻辑代码, VS 模板帮我们生成了一切, 现在运行一下(Ctrl+F5), 并在浏览器中输入 / Students:
表格页面
表格页面对应于 Students 控制器下的 Index 操作方法:
- public class StudentsController : Controller
- {
- private StudentDbContext db = new StudentDbContext();
- // GET: Students
- public ActionResult Index()
- {
- return View(db.Students.ToList());
- }
- }
首先, 我们看到控制器内部定义了一个私有变量 db, 并进行初始化. 这是数据库操作上下文实例, 所有的 CRUD 操作都讲依赖于这个实例.
在 Index 方法中, 通过向 View 方法传递学生列表的方式, 把模型数据传递到了视图, 在 Views/Students/Index.cshtml 视图文件中, 我们声明了传入模型的类型:
@model IEnumerable<AspNetMvc.QuickStart.Models.Student>
在视图中, Model 属性的类型就确定为强类型 IEnumrable<Student>, 配合 VS 提供的智能感知, 不仅可以快速编写代码, 并且在编译时还检查代码的有效性.
完整的 Index.cshtml 代码:
- @model IEnumerable<AspNetMvc.QuickStart.Models.Student>
- @{
- ViewBag.Title = "Index";
- }
- <h2>Index</h2>
- <p>
- @Html.ActionLink("Create New", "Create")
- </p>
- <table class="table">
- <tr>
- <th>
- @Html.DisplayNameFor(model => model.Name)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.Gender)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.Major)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.EntranceDate)
- </th>
- <th></th>
- </tr>
- @foreach (var item in Model) {
- <tr>
- <td>
- @Html.DisplayFor(modelItem => item.Name)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Gender)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Major)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.EntranceDate)
- </td>
- <td>
- @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
- @Html.ActionLink("Details", "Details", new { id=item.ID }) |
- @Html.ActionLink("Delete", "Delete", new { id=item.ID })
- </td>
- </tr>
- }
- </table>
看着很有古老的 ASP 的感觉吧, 不过这里的 Model 属性是强类型的, 因此在 foreach 循环中, VS 明确知道 item 类型是 Student, 从而方便代码编写:
@Html 里面都是 MVC 提供的辅助方法, 用来辅助生成 HTML 代码:
ActionLink: 用来生成超链接, 链接到本控制器内的某个操作方法(也可以是其他控制器的方法, 有重载函数), 可以指定路由参数, 通过对象初始化语法来创建, 比如 new {id=item.ID}.
DisplayNameFor: 显示模型属性的名称. 强类型辅助方法, 允许我们使用一个 lambda 表达式来指定某个模型属性, 而不用写字符串. 好处不仅有智能感知, 编译时检查, 而且也方便代码重构, 比如我们在更改模型的属性名称时, 视图中的相应代码也会改变.
DisplayFor: 显示模型属性的值.
新增页面
新增页面对应于 Students 控制器下的 Create 操作方法:
- // GET: Students/Create
- public ActionResult Create()
- {
- return View();
- }
对应的视图文件:
- @model AspNetMvc.QuickStart.Models.Student
- @{
- ViewBag.Title = "Create";
- }
- <h2>Create</h2>
- @using (Html.BeginForm())
- {
- @Html.AntiForgeryToken()
- <div class="form-horizontal">
- <h4>Student</h4>
- <hr />
- @Html.ValidationSummary(true, "", new { @class ="text-danger" })
- <div class="form-group">
- @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Name, "", new { @class ="text-danger" })
- </div>
- </div>
@* 省略 Gender Major EntranceDate *@
- <div class="form-group">
- <div class="col-md-offset-2 col-md-10">
- <input type="submit" value="Create" class="btn btn-default" />
- </div>
- </div>
- </div>
- }
- <div>
- @Html.ActionLink("Back to List", "Index")
- </div>
- @section Scripts {
- @Scripts.Render("~/bundles/jqueryval")
- }
首先定义了视图中使用的模型类型是 Student, 这样 LabelFor 强类型辅助方法就可以从模型元数据中获取需要显示的文本.
页面打开时, 由于并未传入任何模型对象, 所以 Model 为空对象, 如下所示:
所以页面上默认的输入框都是空的, 截图中是作者输入值后的效果.
Html.BeginForm()会在页面上生成一个 form 标签, 默认的提交地址还是当前页面(action=/Students/Create), 默认的请求方法是 post, 如下所示:
因此, 点击 [Create] 按钮时, 会发出一个 POST 请求到后台, 对应于 Students 控制器的 Create 方法.
保存数据与模型绑定
下面我们来看下拥有 [HttpPost] 元数据的 Create 方法:
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Create([Bind(Include = "ID,Name,Gender,Major,EntranceDate")] Student student)
- {
- if (ModelState.IsValid)
- {
- db.Students.Add(student);
- db.SaveChanges();
- return RedirectToAction("Index");
- }
- return View(student);
- }
这里面有两个安全措施:
ValidateAntiForgeryToken: 用来阻止 CSRF(跨站请求伪造).
Bind: 用来阻止 Over-Posting(过多提交攻击).
这两个安全手段我们会在以后的文章中详细介绍, 这里就先略过.
我们先看下本次请求的 POST 参数:
但是 Create 方法中只有一个 Student 对象参数, 是不是很神奇, 其实这是一个重要的概念模型绑定.
如果在 WebForms 中, 我们可以会写一堆代码来从 Request.Form 中获取参数, 并重建 Student 对象, 类似如下代码:
- Student student = new Student();
- student.Name = Request.Form["Name"];
- student.Gender = Convert.ToInt32(Request.Form["Gender"]);
- ....
在 MVC 中, 这一过程是自动完成, 简单来说这就是模型绑定.
但是实际的模型绑定过程, 不仅在请求的表单数据中查找, 还会在路由参数, URL 查询字符串, 以及 Cookie 中查找.
如果模型绑定失败(比如模型参数不符合验证规则), 则 ModelState.IsValid 就为 false, 这时会直接返回页面内容, 此时模型对象 student 中保存的是用户输入的值, 前端也会有错误提示, 这个过程我们会在下一篇文章中讲解.
如果模型绑定成功, 则保存新增数据, 然后通过 RedirectToAction 来重定向到表格页面:
小结
这篇文章首先介绍了 VS2015 下 MVC 项目的创建过程; 然后简要概述页面执行的流程, 从路由引擎到控制器, 再由控制器到视图, 最后由视图返回到浏览器, 而模型是作为控制器传入视图的参数, 这样清晰明了; 最后使用 VS 提供的模板, 创建了一个带 CRUD 操作的数据访问实例.
EF CodeFirst 让我们的关注点从数据库转移到了模型, 而模型又是 MVC 的核心所在, 对模型进行恰当的数据注解, 不仅会影响数据库的表结构, 而且会影响浏览器端的数据验证和服务端的数据验证, 因此下一篇文章我们会详细介绍一下数据注解.
下载示例源代码 http://fineui.com/bbs/forum.php?mod=viewthread&tid=9101
文章出处: https://www.cnblogs.com/sanshi/p/6210695.html
[第一篇] ASP.NET MVC 快速入门之数据库操作(MVC5+EF6)
来源: http://www.bubuko.com/infodetail-2661999.html