原文: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. 15个实用的Linux find命令示例

    妈咪,我找到了! -- 15个实用的Linux find命令示例 http://www.oschina.net/translate/15-practical-linux-find-command-ex ...

  2. 局域网yum服务器创建

    yum createrepo createrepo dir 配置httpd发布yum-repo; 在客户端添加yum.rep配置文件;

  3. Redis压缩列表原理与应用分析

    摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...

  4. 问题-delphi在某电脑(win7)上是界面超乱 DPL

    问题现象:本机运行正常,但是在WIN7或个别的XP上,界面显示控件大小不一,界面超乱 问题原因:可以百度“delphi dpi”或者“delphi 控件自适应分辨率” 问题处理:将窗体的Scaled设 ...

  5. js关键字

    这已经是我第二次遇到这个问题了..关于关键字,用来做函数的命名,,,在高清上没问题,标清上秒死...页面都出不来...wtf...做个记录.. js关键字http://www.itxueyuan.or ...

  6. C++中随机函数

    #include <iostream> using namespace std; #include <stdlib.h> #include <time.h> int ...

  7. [Android]Fragment源代码分析(二) 状态

    我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是怎样构造Fragment中的View參数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管 ...

  8. js select onchange事件

    <select id='a' name='a' onchange="javascript:alert('测试');">

  9. 使用ThinkPHP框架高速开发站点(多图)

    使用ThinkPHP框架高速搭建站点 这一周一直忙于做实验室的站点,基本功能算是完毕了.比較有收获的是大概了解了ThinkPHP框架.写一些东西留作纪念吧.假设对于相同是Web方面新手的你有一丝丝帮助 ...

  10. Offer是否具有法律效力?

    版权声明:原创作品,同意转载,转载时请务必以超链接形式标明文章原始出版.作者信息和本声明.否则将追究法律责任.本文地址: http://blog.csdn.net/jobchanceleo/archi ...