MVC3+EF4.1学习系列(六)-----导航属性数据更新的处理

文章索引和简介
通过上一篇的学习 我们已经知道怎么查询关系 这篇就来说说怎么导航属性数据更新时的处理 以及EF又会为我们生成哪些SQL~
老规矩 先看下今天的图
MVC3+EF4.1学习系列(六)-----导航属性数据更新的处理
MVC3+EF4.1学习系列(六)-----导航属性数据更新的处理
添加和修改页面基本就是这样
这节的内容相对简单~~
主要就是讲 一对一 一对多 多对多时的增删改 以及MVC的一些小东西
一. 一对多的的处理
看第一张图 院系和课程是一对多的关系
1.添加
一对多的添加非常简单 遇到一对多的情况 我们一般考虑dropdownlist来展示 只要把这个展示出来就容易了
mvc绑定dropdownlist 一般是 控制器用 viewstate存一个SelectList 或者viewbag 然后视图绑定 上代码
MVC3+EF4.1学习系列(六)-----导航属性数据更新的处理MVC3+EF4.1学习系列(六)-----导航属性数据更新的处理课程添加控制器 public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); } // // POST: /Course/Create [HttpPost] public ActionResult Create(Course course) { try { // TODO: Add insert logic here if (ModelState.IsValid) { db.Courses.Add(course); db.SaveChanges(); // PopulateDepartmentsDropDownList(course.DepartmentID); } return RedirectToAction("Index"); } catch { ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); return View(); } } /// /// 处理下拉列表 /// /// 选择的项
private void PopulateDepartmentsDropDownList(object selected = null) { var departmentsQuery = from d in db.Departments orderby d.Name select d; ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selected); }
可以看到 非常简单下面是视图的
视图 @model ContosoUniversity.Models.Course @{ ViewBag.Title = "Create"; Layout = "~/Views/Shared/_Layout.cshtml"; }

Create

@using (Html.BeginForm()) { @Html.ValidationSummary(true)
Course
@Html.LabelFor(model => model.CourseID)
@Html.EditorFor(model => model.CourseID) @Html.ValidationMessageFor(model => model.CourseID)
@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title)
@Html.LabelFor(model => model.Credits)
@Html.EditorFor(model => model.Credits) @Html.ValidationMessageFor(model => model.Credits)
@Html.LabelFor(model => model.DepartmentID, "Department")
@Html.DropDownList("DepartmentID","请选择") @Html.ValidationMessageFor(model => model.DepartmentID)

}
@Html.ActionLink("Back to List", "Index")

视图的核心其实就这句
Html.DropDownList("DepartmentID","请选择")
DepartmentID 对应 你的viewbag 就能让他们对应上去了 就这么简单神奇~~ 由于生成的html name 为 DepartmentID 当你保存的时候 通过ModelBinder 会自动对应到实体类上 直接保存即可 生成的SQL插入语句也简单 而且是参数化的~
exec sp_executesql N'insert [dbo].[Course]([CourseID], [Title], [Credits], [DepartmentID]) values (@0, @1, @2, @3) ',N'@0 int,@1 nvarchar(50),@2 int,@3 int',@0=4444,@1=N'试试插入',@2=4,@3=1
2.修改
这个没啥好说的了~~ 基本和添加一样
修改控制器 public ActionResult Edit(int id) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } // // POST: /Course/Edit/5 [HttpPost] public ActionResult Edit(Course course) { try { if (ModelState.IsValid) { db.Entry(course).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); }
生成的SQL语句如下
exec sp_executesql N'update [dbo].[Course] set [Title] = @0, [Credits] = @1, [DepartmentID] = @2 where ([CourseID] = @3) ',N'@0 nvarchar(50),@1 int,@2 int,@3 int',@0=N'试试插入',@1=4,@2=2,@3=4444
这有个问题 就是把每个字段都做了更新 而我们实际上只更新了一个 如果能按需更新改多好 dudu给了我们好的思路 可以参考下 -----文章连接
3.删除
这个简单....没啥好说的了 具体参考第二篇 简单的增删改查~~
二.一对一和多对多
一对多比较简单 因为涉及的都是一张表里的东西 所以没什么难的 而一对一,多对多 则是处理 多张表的情况 所以这里重点说下这部分~~
先看第二个图 分析下关系 这是修改老师的信息 跟他关联的有一对一的地址 和多对多的课程 也就是说 当我们插入一条记录时
应该插入三张表 教师表 办公地址表 和 教师课程关系表 那让我们来印证下吧
1.添加
首先 我们要先把添加页面显示出来 先来说下 下面那个复选框选择课程 控制器很简单 就是读取出来所有课程存放到viewbag里即可
public ActionResult Create() { ViewBag.SelectCourse = db.Courses.ToList(); return View(); }
视图的显示 有两种方法 一种是原文提供的 添加一个viewmodel 专门存放课程 以及是否选中 然后通过如下代码实现三列换行
原文的方法
@{ int cnt = 0; List courses = ViewBag.Courses; foreach (var course in courses) { if (cnt++ % 3 == 0) { @: } @: } @: }
@course.CourseID @:  @course.Title @:

个人觉得麻烦了些 这里我说下我的方法
@{ System.Text.StringBuilder sb = new System.Text.StringBuilder(""); List list= ViewBag.SelectCourse as List; for (int i = 0; i < list.Count; i++) { if (i!=0&&i % 3 == 0) { sb.Append("
>
"); } sb.Append(""); } sb.Append("
"+list[i].Title+"
"); } @MvcHtmlString.Create(sb.ToString())

这里要说的就是 MvcHtmlString.Create 如果不使用这个 直接输入 则会把 这些也直接输入出来 而不是转换成HTML 记住,如果想生成想要的结果 请使用MvcHtmlString.Create
好了 添加页面有了 现在就是添加了
添加 [HttpPost] public ActionResult Create(Instructor Model, string[] selectedCourses) { try { // TODO: Add insert logic here if (ModelState.IsValid) { Model.Courses = new List(); foreach (var item in db.Courses.ToList()) { if (selectedCourses.Contains(item.CourseID.ToString())) { Model.Courses.Add(item); } } db.Instructors.Add(Model); db.SaveChanges(); } return RedirectToAction("Index"); } catch { return View(); } }
这里提下 第二个参数 就是我们选中的checkbox 的value 的集合 这个参数的名字 与 checkbox名字的name一样
然后我们遍历所有的课程 把符合条件的加入进来 我选了一个 让我们看下EF为我们生成的SQL
exec sp_executesql N'insert [dbo].[Instructor]([LastName], [FirstName], [HireDate]) values (@0, @1, @2) select [InstructorID] from [dbo].[Instructor] where @@ROWCOUNT > 0 and [InstructorID] = scope_identity()',N'@0 nvarchar(50),@1 nvarchar(50),@2 datetime',@0=N'W',@1=N'LF',@2='02 3 2011 12:00:00:000AM' exec sp_executesql N'insert [dbo].[CourseInstructor]([CourseID], [InstructorID]) values (@0, @1) ',N'@0 int,@1 int',@0=2042,@1=10 exec sp_executesql N'insert [dbo].[OfficeAssignment]([InstructorID], [Location]) values (@0, @1) ',N'@0 int,@1 nvarchar(50)',@0=10,@1=N'天朝'
看 确实是生成了三条SQL语句 符合我们的要求 而我们的代码 却写了很少 就轻松完成了添加~~
2.修改
修改要比添加麻烦 首先显示视图时 要显示哪些被选中了 还有就是修改时 要在关系表里删除原来的 课程教师关系 还要添加新的进去
如何让EF帮我们完成这一对一 多对多的复杂的 三张表之间的关系处理呢
还是先从视图开始 先解决让以前被选中的显示出来
public ActionResult Edit(int id) { ViewBag.SelectCourse = db.Courses.ToList(); Instructor model= db.Instructors.Include(i => i.Courses).Include(i => i.OfficeAssignment).Where(i => i.InstructorID == id).SingleOrDefault(); return View(model); }
先把要修改的这条加载出来返回给视图 用贪婪加载 把地点和课程都加载出来
修改视图
@{ System.Text.StringBuilder sb = new System.Text.StringBuilder(""); List list= ViewBag.SelectCourse as List; for (int i = 0; i < list.Count; i++) { if (i!=0&&i % 3 == 0) { sb.Append("
>
"); } string IsSelect = string.Empty; if (Model.Courses.Contains(list[i])) { IsSelect = "checked='checked'"; } sb.Append(""); } sb.Append("
"+list[i].Title+"
"); } @MvcHtmlString.Create(sb.ToString())

在视图上加上判断 遍历到的这个课程 是否在选中的课程里 如果在 则加上选中属性
string IsSelect = string.Empty; if (Model.Courses.Contains(list[i])) { IsSelect = "checked='checked'"; }
这样就解决了视图修改时的显示问题了 下面是点击修改时 让我们先大概想想 有哪些操作
1.修改 教师表信息
2.修改 地址表信息
3. 删除以前的课程关系表 和添加新的课程关系表记录
这里比较负责的是第三部 我们来想想怎么做 现在 我们能知道 我们新选择了哪些 和 以前选择了哪些 比如 新选择的是 {1,2,3} 这次选的是 {3,4,5} 那么我们应该
删除{4,5} 添加{1,2} 即可 这其实就转换为一个简单的算法题了~~ 原文给出了一种方法 我自己也写了一种 大家可以比较下看~先上原文的
原文修改核心代码 private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate) { if (selectedCourses == null) { instructorToUpdate.Courses = new List(); return; } var selectedCoursesHS = new HashSet(selectedCourses); var instructorCourses = new HashSet (instructorToUpdate.Courses.Select(c => c.CourseID)); foreach (var course in db.Courses) { if (selectedCoursesHS.Contains(course.CourseID.ToString())) { if (!instructorCourses.Contains(course.CourseID)) { instructorToUpdate.Courses.Add(course); } } else { if (instructorCourses.Contains(course.CourseID)) { instructorToUpdate.Courses.Remove(course); } } } }
原文利用HashSet 不能存重复项 实现 不过判断逻辑多了些 但效率应该高 hashset 散列算法
下面是我的
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructor) { if (selectedCourses == null) { instructor.Courses = new List(); return; } //思路 比较以前选择的和现在选择的 先取出他们的差集 那么这部分就是可以删除的项 和添加的部分 string[] beforeSelect=instructor.Courses.Select(i => i.CourseID.ToString()).ToArray();//得到以前选择的 beforeSelect.Except(selectedCourses).ToList().ForEach(n => instructor.Courses.Remove(db.Courses.Find(Convert.ToInt32(n)))); selectedCourses.Except(beforeSelect).ToList().ForEach(n => instructor.Courses.Add(db.Courses.Find(Convert.ToInt32(n)))); }
这里 参数selectedCourses 为现在选中的项 利用linq 内置的方法 Except取出差集 然后 ForEach遍历移除和添加 一句话搞定 但是效率估计不会太高~~
下面是完整的修改方法
完整修改方法 [HttpPost] public ActionResult Edit(int id, FormCollection collection, string[] selectedCourses) { try { // TODO: Add update logic here var instructor = db.Instructors.Include(i => i.Courses).Where(i => i.InstructorID == id).SingleOrDefault(); UpdateModel(instructor, "", null, new string[] {"Courses"}); UpdateInstructorCourses(selectedCourses, instructor); if (string.IsNullOrWhiteSpace(instructor.OfficeAssignment.Location)) { instructor.OfficeAssignment = null; } db.Entry(instructor).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch { return View(); } } private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructor) { if (selectedCourses == null) { instructor.Courses = new List(); return; } //思路 比较以前选择的和现在选择的 先取出他们的差集 那么这部分就是可以删除的项 和添加的部分 string[] beforeSelect=instructor.Courses.Select(i => i.CourseID.ToString()).ToArray();//得到以前选择的 beforeSelect.Except(selectedCourses).ToList().ForEach(n => instructor.Courses.Remove(db.Courses.Find(Convert.ToInt32(n)))); selectedCourses.Except(beforeSelect).ToList().ForEach(n => instructor.Courses.Add(db.Courses.Find(Convert.ToInt32(n)))); }

好了 让我们来看下执行的SQL
exec sp_executesql N'update [dbo].[Instructor] set [LastName] = @0, [FirstName] = @1, [HireDate] = @2 where ([InstructorID] = @3) ',N'@0 nvarchar(50),@1 nvarchar(50),@2 datetime,@3 int',@0=N'W',@1=N'LF',@2='02 3 2011 12:00:00:000AM',@3=10 exec sp_executesql N'update [dbo].[OfficeAssignment] set [Location] = @0 where ([InstructorID] = @1) ',N'@0 nvarchar(50),@1 int',@0=N'超',@1=10 exec sp_executesql N'delete [dbo].[CourseInstructor] where (([CourseID] = @0) and ([InstructorID] = @1))',N'@0 int,@1 int',@0=2042,@1=10 exec sp_executesql N'insert [dbo].[CourseInstructor]([CourseID], [InstructorID]) values (@0, @1) ',N'@0 int,@1 int',@0=0,@1=10
看 和我们想要的效果一样~~
3.删除
简单的说下删除, 当我们删除一条数据时 直接看生成的SQL语句 预计应该是 要删3个表
exec sp_executesql N'insert [dbo].[CourseInstructor]([CourseID], [InstructorID]) values (@0, @1) ',N'@0 int,@1 int',@0=0,@1=10 exec sp_executesql N'delete [dbo].[OfficeAssignment] where ([InstructorID] = @0)',N'@0 int',@0=10
结果是删除了 2个表的 一对一的删除了 可是多对多的为什么没删除 这其实是一个简单的数据库知识了~~
MVC3+EF4.1学习系列(六)-----导航属性数据更新的处理
EF为我们创建数据库时 这个删除规则是层叠 就是级联删除 所以这个关系表的就会被直接删除了
三.总结
导航属性的更新操作等 结束了 现在EF的已经基本操作已经结束了 下一节讲EF处理并发的策略
Tags: 

延伸阅读

最新评论

发表评论