文档章节

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

短短的歼击机
 短短的歼击机
发布于 2014/02/08 17:53
字数 2652
阅读 87
收藏 0

通过上一篇的学习 我们已经知道怎么查询关系 这篇就来说说怎么导航属性数据更新时的处理 以及EF又会为我们生成哪些SQL~

老规矩 先看下今天的图

添加和修改页面基本就是这样

这节的内容相对简单~~

主要就是讲  一对一 一对多 多对多时的增删改 以及MVC的一些小东西

一. 一对多的的处理

看第一张图   院系和课程是一对多的关系  

1.添加

一对多的添加非常简单  遇到一对多的情况  我们一般考虑dropdownlist来展示 只要把这个展示出来就容易了

mvc绑定dropdownlist 一般是 控制器用 viewstate存一个SelectList  或者viewbag  然后视图绑定 上代码

复制代码

        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();
           }
         

       }

       
/// <summary>
       
/// 处理下拉列表
       
/// </summary>
       
/// <param name="selected">选择的项</param>
       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";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
   @Html.ValidationSummary(true)
   
<fieldset>
       
<legend>Course</legend>
       
<div class="editor-label">
           @Html.LabelFor(model => model.CourseID)
       
</div>
       
<div class="editor-field">
           @Html.EditorFor(model => model.CourseID)
           @Html.ValidationMessageFor(model => model.CourseID)
       
</div>


       
<div class="editor-label">
           @Html.LabelFor(model => model.Title)
       
</div>
       
<div class="editor-field">
           @Html.EditorFor(model => model.Title)
           @Html.ValidationMessageFor(model => model.Title)
       
</div>

       
<div class="editor-label">
           @Html.LabelFor(model => model.Credits)
       
</div>
       
<div class="editor-field">
           @Html.EditorFor(model => model.Credits)
           @Html.ValidationMessageFor(model => model.Credits)
       
</div>

     
<div class="editor-label">
           @Html.LabelFor(model => model.DepartmentID, "Department")
       
</div>
       
<div class="editor-field">
         @Html.DropDownList("DepartmentID","请选择")
           @Html.ValidationMessageFor(model => model.DepartmentID)
       
</div>

       
<p>
           
<input type="submit" value="Create" />
       
</p>
   
</fieldset>
}

<div>
   @Html.ActionLink("Back to List", "Index")
</div>

复制代码

视图的核心其实就这句

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  专门存放课程 以及是否选中  然后通过如下代码实现三列换行

复制代码

<div class="editor-field">
   
<table style="width: 100%">
       
<tr>
           @{
               int cnt = 0;
               List
<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses;

               foreach (var course in courses) {
                   if (cnt++ % 3 == 0) {
                       @:  
</tr> <tr>
                   }
                   @:
<td>
                       
<input type="checkbox"
                              name
="selectedCourses"
                              value
="@course.CourseID"
                              @(Html.Raw(course.Assigned ? "checked
=\"checked\"" : "")) />
                       @course.CourseID @:
&nbsp; @course.Title
                   @:
</td>
               }
               @:
</tr>
           }
   
</table>
</div>

复制代码

个人觉得麻烦了些  这里我说下我的方法

复制代码

   <div class="editor-field">
       @{
            System.Text.StringBuilder sb = new System.Text.StringBuilder("
<table style='width:100%'><tr>");
            List
<ContosoUniversity.Models.Course> list= ViewBag.SelectCourse as List<ContosoUniversity.Models.Course>;
           for (int i = 0; i
< list.Count; i++)
           {
               if (i!
=0&&i % 3 == 0)
               {
                   sb.Append("</tr
><tr>");
               }
               sb.Append("
<td><input type='checkbox' value='"+list[i].CourseID+"' name='selectedCourses'/>"+list[i].Title+"</td>");
           }
           sb.Append("
</tr></table> ");
           }
       @MvcHtmlString.Create(sb.ToString())    
       
</div>

复制代码

这里要说的就是 MvcHtmlString.Create   如果不使用这个 直接输入  则会把<tr><td> 这些也直接输入出来 而不是转换成HTML   记住,如果想生成想要的结果 请使用MvcHtmlString.Create


好了 添加页面有了 现在就是添加了 

复制代码

[HttpPost]
       
public ActionResult Create(Instructor Model, string[] selectedCourses)
       {
           
try
           {
               
// TODO: Add insert logic here
               if (ModelState.IsValid)
               {

                   Model.Courses
= new List<Course>();

                   
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);
       }

先把要修改的这条加载出来返回给视图   用贪婪加载 把地点和课程都加载出来

复制代码

      <div class="editor-field">
       @{
            System.Text.StringBuilder sb = new System.Text.StringBuilder("
<table style='width:100%'><tr>");
            List
<ContosoUniversity.Models.Course> list= ViewBag.SelectCourse as List<ContosoUniversity.Models.Course>;
           for (int i = 0; i
< list.Count; i++)
           {
               if (i!
=0&&i % 3 == 0)
               {
                   sb.Append("</tr
><tr>");
               }

               string IsSelect = string.Empty;
               if (Model.Courses.Contains(list[i]))
               {
                   IsSelect = "checked='checked'";
               }
               sb.Append("
<td><input type='checkbox' value='"+list[i].CourseID+"'  "+IsSelect+"    name='selectedCourses'/>"+list[i].Title+"</td>");
           }
           sb.Append("
</tr></table> ");
           }
       @MvcHtmlString.Create(sb.ToString())    
       
</div>

复制代码

在视图上加上判断 遍历到的这个课程 是否在选中的课程里 如果在 则加上选中属性

 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<Course>();
       
return;
   }

   var selectedCoursesHS
= new HashSet<string>(selectedCourses);
   var instructorCourses
= new HashSet<int>
       (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<Course>();
               
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<Course>();
               
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个表的  一对一的删除了  可是多对多的为什么没删除 这其实是一个简单的数据库知识了~~

EF为我们创建数据库时  这个删除规则是层叠  就是级联删除  所以这个关系表的就会被直接删除了

三.总结

导航属性的更新操作等 结束了   现在EF的已经基本操作已经结束了  下一节讲EF处理并发的策略


© 著作权归作者所有

短短的歼击机

短短的歼击机

粉丝 82
博文 268
码字总数 269797
作品 0
武汉
高级程序员
私信 提问
MVC3+EF4.1学习系列(五)----- EF查找导航属性的几种方式

通过上一篇的学习 我们把demo的各种关系终于搭建里起来 以及处理好了如何映射到数据库等问题 但是 只是搭建好了关系 问题还远没有解决 这篇就来写如何查找导航属性 和查找导航属性的几种方式...

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

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

postdep
2014/02/08
89
0
MVC3+EF4.1学习系列(一)-------创建EF4.1 code first的第一个实例

基于EF4.1 code first 简单的CRUD 园子中已经有很多了 ~~ 真不想再写这个了 可是为了做一个完整的小demo 从开始 到后面的一些简单重构 还是决定认真把这个写出来 争取写些别人没写到的东西 ...

postdep
2014/02/08
164
0
jquery合集--从零开始学习jQuery系列- 转载

从零开始学习jQuery系列 作者: ziqiu.zhang 来源: 博客园 发布时间: 2009-12-22 11:37 阅读: 3452 次 [收藏] 本系列文章导航 从零开始学习jQuery (一) 开天辟地入门篇 从零开始学习jQuery (二...

nkbai
2010/04/06
0
0
MVC3+EF4.1学习系列(十)----MVC+EF处理树形结构

通过前几篇文章 我们处理了 一对一, 一对多,多对多关系 很好的发挥了ORM框架的做用 但是 少说了一种 树形结构的处理, 而这种树形关系 我们也经常遇到,常见的N级类别的处理, 以及经常有数据与...

postdep
2014/02/08
2.1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Replugin借助“UI进程”来快速释放Dex

public static boolean preload(PluginInfo pi) { if (pi == null) { return false; } // 借助“UI进程”来快速释放Dex(见PluginFastInstallProviderProxy的说明) return PluginFastInsta......

Gemini-Lin
43分钟前
3
0
Hibernate 5 的模块/包(modules/artifacts)

Hibernate 的功能被拆分成一系列的模块/包(modules/artifacts),其目的是为了对依赖进行独立(模块化)。 模块名称 说明 hibernate-core 这个是 Hibernate 的主要(main (core))模块。定义...

honeymoose
今天
4
0
CSS--属性

一、溢出 当内容多,元素区域小的时候,就会产生溢出效果,默认是纵向溢出 横向溢出:在内容和容器之间再套一层容器,并且内部容器要比外部容器宽 属性:overflow/overflow-x/overflow-y 取值...

wytao1995
今天
4
0
精华帖

第一章 jQuery简介 jQuery是一个JavaScript库 jQuery具备简洁的语法和跨平台的兼容性 简化了JavaScript的操作。 在页面中引入jQuery jQuery是一个JavaScript脚本库,不需要特别的安装,只需要...

流川偑
今天
7
0
语音对话英语翻译在线翻译成中文哪个方法好用

想要进行将中文翻译成英文,或者将英文翻译成中文的操作,其实有一个非常简单的工具就能够帮助完成将语音进行翻译转换的软件。 在应用市场或者百度手机助手等各大应用渠道里面就能够找到一款...

401恶户
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部