原文: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。然后当我们调用SaveChanges时,EF将会产生只更新被改变了字段的SQL。

  但是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)的更多相关文章

  1. MVC3+EF4.1学习系列(二)-------基础的增删改查和持久对象的生命周期变化

    上篇文章中 我们已经创建了EF4.1基于code first的例子  有了数据库 并初始化了一些数据  今天这里写基础的增删改查和持久对象的生命周期变化 学习下原文先把运行好的原图贴来上~~ 一.创建 ...

  2. mvc模式jsp+servel+dbutils oracle基本增删改查demo

    mvc模式jsp+servel+dbutils oracle基本增删改查demo 下载地址

  3. mvc模式jsp+servel+jdbc oracle基本增删改查demo

    mvc模式jsp+servel+jdbc oracle基本增删改查demo 下载地址

  4. week_one-python基础 列表 增删改查

    # Author:larlly #列表增删改查#定义列表name = ["wo","ni","ta","wo"] #定义 ...

  5. ADO.NET基础(增删改查)

    ADO.NET是一种数据访问技术,就是将C#和MSSQL连接起来的一个纽带.可以通过ADO.NET将内存中的临时数据写入到数据库中,也可以将数据库中的数据提取到内存中供程序调用. ADO.NET是所有 ...

  6. EF 5.0 帮助类 增删改查

    原文地址:http://www.cnblogs.com/luomingui/p/3362813.html EF 5.0 帮助类 加入命名空间: using System; using System.D ...

  7. 使用vue+elementUI+springboot创建基础后台增删改查的管理页面--(1)

    目前这家公司前端用的是vue框架,由于在之前的公司很少涉及到前端内容,对其的了解也只是会使用js和jquery,所以..慢慢来吧. 在此之前需要先了解vue的大致语法和规则,可先前往官方文档进行学习h ...

  8. 【ArcGIS for SivlerLight api(3)】基础图层增删改查

    1.基础底图通常使用TiledLayer或者ArcGISDynamicLayer. 本质上都是在本地加载栅格图片.后台生成策略不同而已.从Vs2010的控件栏上拖过来的Map控件默认添加的底图是Esr ...

  9. PHP学习之[第08讲]数据库MySQL基础之增删改查

    一.工具: 1.phpMyAdmin (http://www.phpmyadmin.net/) 2.Navicat (http://www.navicat.com/) 3.MySQL GUI Tool ...

随机推荐

  1. glance image cache

    The Glance Image Cache The Glance API server may be configured to have an optional local image cache ...

  2. python —print

    今天开始学python了,“装X”安装了最新版本python 3.4.1 然后,print “hello world!" 就出错了... 一搜原来... python v3.0以后的版本pr ...

  3. Ⅷ.spring的点点滴滴--抽象对象和子对象

    承接上文 抽象对象和子对象 .net篇(环境为vs2012+Spring.Core.dll v1.31) public class parent { public string Name { get; ...

  4. BZOJ 3280: 小R的烦恼 & BZOJ 1221: [HNOI2001] 软件开发

    3280: 小R的烦恼 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 399  Solved: 200[Submit][Status][Discuss ...

  5. 【转】bootbox自定义dialog、confirm、alert样式,以及基本设置方法setDefaults中可用参数

    <html> <head> <meta charset="utf-8"> <meta name="viewport" ...

  6. Kinect for Windows SDK开发入门(15):进阶指引 下

    Kinect for Windows SDK开发入门(十五):进阶指引 下 上一篇文章介绍了Kinect for Windows SDK进阶开发需要了解的一些内容,包括影像处理Coding4Fun K ...

  7. PHP class_exists 检查类是否已定义

    (PHP 4, PHP 5)  class_exists — 检查类是否已定义 bool class_exists ( string $class_name [, bool $autoload ] ) ...

  8. VS2005配置CPPUnit进行单元測试

    一. 编译CPPUnitproject 1.  解压文件cppunit-1.12.1.tar.gz. 2.  编译$BASE/src/cppunit/CppUnitLibraries.dsw中的cpp ...

  9. 在Linux下查看系统版本信息命令总结

    每次在想查看系统是多少位的时候.总是记不清究竟用哪个命令.所以做个总结. vonzhou@de16-C6100:~$ lsb_release -a No LSB modules are availab ...

  10. cocoapods使用指南

    什么是cocoapods cocoapods是库管理工具. cocoapods的用途 解决库之间的依赖关系.如前文所述: 一个开源的项目可能是另一个项目的基础, A依赖B, B依赖C和D, D又依赖E ...