[翻译][MVC 5 + EF 6] 2:基础的增删改查(CRUD)
原文:Implementing Basic CRUD Functionality with the Entity Framework in ASP.NET MVC Application
1.修改Views\Student\Details.cshtml:
- @model ContosoUniversity.Models.Student
- @{
- ViewBag.Title = "Details";
- }
- <h2>Details</h2>
- <div>
- <h4>Student</h4>
- <hr />
- <dl class="dl-horizontal">
- <dt>
- @Html.DisplayNameFor(model => model.LastName)
- </dt>
- <dd>
- @Html.DisplayFor(model => model.LastName)
- </dd>
- <dt>
- @Html.DisplayNameFor(model => model.FirstMidName)
- </dt>
- <dd>
- @Html.DisplayFor(model => model.FirstMidName)
- </dd>
- <dt>
- @Html.DisplayNameFor(model => model.EnrollmentDate)
- </dt>
- <dd>
- @Html.DisplayFor(model => model.EnrollmentDate)
- </dd>
- <dt>
- @Html.DisplayNameFor(model => model.Enrollments)
- </dt>
- <dd>
- <table class="table">
- <tr>
- <th>Course Title</th>
- <th>Grade</th>
- </tr>
- @foreach (var item in Model.Enrollments)
- {
- <tr>
- <td>
- @Html.DisplayFor(modelItem => item.Course.Title)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Grade)
- </td>
- </tr>
- }
- </table>
- </dd>
- </dl>
- </div>
- <p>
- @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
- @Html.ActionLink("Back to List", "Index")
- </p>
2.修改Controllers\StudentController.cs的Create:
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Create([Bind(Include = "LastName, FirstMidName, EnrollmentDate")]Student student)
- {
- try
- {
- if (ModelState.IsValid)
- {
- db.Students.Add(student);
- db.SaveChanges();
- return RedirectToAction("Index");
- }
- }
- catch (DataException /* dex */)
- {
- //Log the error (uncomment dex variable name and add a line here to write a log.
- ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
- }
- return View(student);
- }
ValidateAntiForgeryToken
属性用于防止跨站点请求伪造攻击(cross-site request forgery),在视图中需要同时添加@Html.AntiForgeryToken()来实现此功能。
Bind属性用于防止传入多余的字段。在Bind属性中Include
参数列出字段白名单(whitelist),Exclude参数列出字段黑名单(blacklist)。使用Include参数会更安全,因为当我们给实体添加新字段的时候,新字段不会被自动加入黑名单。
在编辑页面,我们可以首先从数据库中读取实体然后调用TryUpdateModel,传入明确被允许的字段列表来防止传入多余的字段。
防止传入多余字段的另一种方法是使用视图模型(view model),在视图模型中只包含我们想要更新的字段。当MVC模型绑定完成后,我们可以随意使用一种工具(比如AutoMapper)将视图模型中的值拷贝到实体实例中。然后对实体实例使用db.Entry,将它的状态改为Unchanged,然后将包含在视图模型中的每个字段(Property("PropertyName").IsModified)设置为true。这种方法可以用于创建和编辑页面。
Views\Student\Create.cshtml页面的代码与Details.cshtml页面类似,不同的是EditorFor和ValidationMessageFor取代了DisplayFor:
- <div class="form-group">
- @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.LastName)
- @Html.ValidationMessageFor(model => model.LastName)
- </div>
- </div>
运行项目:
错误信息来自默认的服务端(server-side)验证:
- if (ModelState.IsValid)
- {
- db.Students.Add(student);
- db.SaveChanges();
- return RedirectToAction("Index");
- }
3.修改Controllers\StudentController.cs的Edit:
- [HttpPost, ActionName("Edit")]
- [ValidateAntiForgeryToken]
- public ActionResult EditPost(int? id)
- {
- if (id == null)
- {
- return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
- }
- var studentToUpdate = db.Students.Find(id);
- if (TryUpdateModel(studentToUpdate, "",
- new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
- {
- try
- {
- db.SaveChanges();
- return RedirectToAction("Index");
- }
- catch (DataException /* dex */)
- {
- //Log the error (uncomment dex variable name and add a line here to write a log.
- ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
- }
- }
- return View(studentToUpdate);
- }
修改后的代码是阻止传入多余字段的最佳实践。原先的代码是脚手架自动添加的,添加了Bind属性并且为模型绑定的实体添加Modified标记,这样的代码不再被推荐,因为Bind属性会自动清理掉Include参数没有列出的字段原来的值。将来MVC脚手架将会被更新,而不再为Edit方法产生Bind属性。
修改后的代码获取已经存在的实体并根据用户输入的数据调用TryUpdateModel
更新该实体。EF自动为需要跟新的实体添加Modified标记。当SaveChanges方法被调用的时候,Modified标记使EF产生Sql来更新数据库。该方法忽略了并发冲突,并且所有的列包括值没有改变的列都将会被更新(后续的教程将会展示如何处理并发冲突,如果我们如果只想更新指定的列可以设置实体为Unchanged,并且设置需要改变的列为Modified)。
数据上下文会一直跟踪内存中实体的状态是否和数据库中对应行的状态保持一致,跟踪信息将会决定当SaveChanges被调用时产生什么样的动作。例如,当我们通过Add方法添加一个实体时,这个实体的状态将会标记为Added,当调用
SaveChanges
放方式,数据库上下文将会调用INSERT命令。实体的状态列表:
Added
:实体在数据库中还不存在,SaveChanges
方法产生INSERT语句。Unchanged:
SaveChanges
方法将不会产生任何动作。当我们从数据库中获取一个实体时,这是该实体的初始状态。Modified
:实体的部分或所有列被改变,SaveChanges
方法 产生UPDATE语句。Deleted
:实体被标记为删除,SaveChanges
方法产生DELETE语句。Detached
:该实体没有被数据库上下文跟踪。
在桌面应用程序中,实体状态的改变是自动的。在桌面性质的程序中,改变一个实体的部分字段的值,实体的状态会自动被标记为Modified
。然后当我们调用
时,EF将会产生只更新被改变了字段的SQL。SaveChanges
但是web应用程序的断开连接(disconnected)的性质不允许这种连续序列(continuous sequence)。当页面渲染完成后,DbContext读取的实体将会被释放掉。当HttpPost
Edit
被调用的时候,新的请求创建了新的DbContext实例,因此我们必须手动设置实体的状态为Modified
。当调用SaveChanges
时,EF将会更新所有列,数据库上下文无法知道我们更新的是哪些列。
如果我们只想要更新实际被改变的列,我们需要通过一些方式(比如隐藏域)保存原始的值,然后调用Attach
方法,将实体的值更改为新值,然后调用SaveChanges
。
4.修改Controllers\StudentController.cs的Delete:
- [HttpPost, ActionName("Delete")]
- [ValidateAntiForgeryToken]
- public ActionResult DeleteConfirmed(int id)
- {
- try
- {
- Student student = db.Students.Find(id);
- db.Students.Remove(student);
- db.SaveChanges();
- }
- catch (DataException/* dex */)
- {
- //Log the error (uncomment dex variable name and add a line here to write a log.
- return RedirectToAction("Delete", new { id = id, saveChangesError = true });
- }
- return RedirectToAction("Index");
- }
为了提升高并发程序的性能,我们需要避免不必要的查询,将上面的Find和Remove方法用下面的代码代替:
- Student studentToDelete = new Student() { ID = id };
- db.Entry(studentToDelete).State = EntityState.Deleted;
5.关闭数据库连接:
- protected override void Dispose(bool disposing)
- {
- db.Dispose();
- base.Dispose(disposing);
- }
Controller基类实现了IDisposable
接口,因此可以重写Dispose方法来释放上下文实例。
6.事务:
EF默认支持事务,如果同时修改多个行或者多个表并且调用SaveChanges,EF会保证这些修改同时执行成功或失败。
[翻译][MVC 5 + EF 6] 2:基础的增删改查(CRUD)的更多相关文章
- MVC3+EF4.1学习系列(二)-------基础的增删改查和持久对象的生命周期变化
上篇文章中 我们已经创建了EF4.1基于code first的例子 有了数据库 并初始化了一些数据 今天这里写基础的增删改查和持久对象的生命周期变化 学习下原文先把运行好的原图贴来上~~ 一.创建 ...
- mvc模式jsp+servel+dbutils oracle基本增删改查demo
mvc模式jsp+servel+dbutils oracle基本增删改查demo 下载地址
- mvc模式jsp+servel+jdbc oracle基本增删改查demo
mvc模式jsp+servel+jdbc oracle基本增删改查demo 下载地址
- week_one-python基础 列表 增删改查
# Author:larlly #列表增删改查#定义列表name = ["wo","ni","ta","wo"] #定义 ...
- ADO.NET基础(增删改查)
ADO.NET是一种数据访问技术,就是将C#和MSSQL连接起来的一个纽带.可以通过ADO.NET将内存中的临时数据写入到数据库中,也可以将数据库中的数据提取到内存中供程序调用. ADO.NET是所有 ...
- EF 5.0 帮助类 增删改查
原文地址:http://www.cnblogs.com/luomingui/p/3362813.html EF 5.0 帮助类 加入命名空间: using System; using System.D ...
- 使用vue+elementUI+springboot创建基础后台增删改查的管理页面--(1)
目前这家公司前端用的是vue框架,由于在之前的公司很少涉及到前端内容,对其的了解也只是会使用js和jquery,所以..慢慢来吧. 在此之前需要先了解vue的大致语法和规则,可先前往官方文档进行学习h ...
- 【ArcGIS for SivlerLight api(3)】基础图层增删改查
1.基础底图通常使用TiledLayer或者ArcGISDynamicLayer. 本质上都是在本地加载栅格图片.后台生成策略不同而已.从Vs2010的控件栏上拖过来的Map控件默认添加的底图是Esr ...
- PHP学习之[第08讲]数据库MySQL基础之增删改查
一.工具: 1.phpMyAdmin (http://www.phpmyadmin.net/) 2.Navicat (http://www.navicat.com/) 3.MySQL GUI Tool ...
随机推荐
- Nexus5如何手动OTA更新系统到4.4.3、4.4.4及常见问题回答
这里将记录一套行之有效的Nexus5手动升级方法,以帮助看见这篇文章的朋友成功将手头的Nexus5升级到4.4.4. 因为谷歌服务器的事,我的这次OTA更新起来走了很多弯路.我试过挂VPN等待系统来更 ...
- 教程-EhLib70的安装方法
1.下载EhLib 5.2包2.将Common文件下的文件全部复制到DELPHI7目录下.3.打开DELPHI7.0程序:打开DclEhLib70.dpk文件包点“编译”->“安装”打开EhLi ...
- 查看当前正在被执行的sql
由于在公司服务器上在某个时段查询某个sql执行比较慢,由来查询当前正在被执行的sql Select t.text,SUBSTRING(t.text, (r.statement_start_offset ...
- SQLite 入门教程(二)创建、修改、删除表 (转)
转于 SQLite 入门教程(二)创建.修改.删除表 一.数据库定义语言 DDL 在关系型数据库中,数据库中的表 Table.视图 View.索引 Index.关系 Relationship 和触发器 ...
- IOS 10适配https 包含对于一些http的一些兼容配置
iOS10 从2017年1月1日起苹果提出所有新提交的App默认不允许使用NSAllowsArbitraryLoads来绕过ATS的限制,也就是说强制我们用HTTPS,如果不这样的话提交App可能会被 ...
- 【转】C++中继承中的同名成员问题
C++中,子类若有与父类同名的成员变量和成员函数,则同名的成员变量相互独立,但同名的子类成员函数重载父类的同名成员函数.举例如下: #include <iostream> using na ...
- FZOJ2110 star(DFS)
Overpower often go to the playground with classmates. They play and chat on the playground. One day, ...
- hi3531 SDK 编译 uboot, 改动PHY地址, 改动 uboot 參数 .
一,编译uboot SDK文档写得比較清楚了,写一下须要注意的地方吧. 1. 之前用SDK里和别人给的已经编译好的uboot,使用fastboot工具都刷不到板子上.最后自己用SDK里uboot源代码 ...
- WordPress公布新文章Email通知注冊用户
非常多WordPress博客都开放了用户注冊的功能,用户能够參与到博客的内容建设其中来.也就是一个博客由多个用户来写.如今有这种需求,怎样实如今某一个用户发表文章后.其它用户都能收到Email通知?以 ...
- 垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
Delphi 是一个基本上被我遗忘的工具, 要不是在使用RapidSql , 我是收不到Embarcadero 公司发出的邀请来參加Delphi XE5的公布会的. 有人可能要问为什么是Embarca ...