graphic instructorr set箱子怎么得

翻译:Contoso 大学 - 6 – 更新关联数据 - 冠军 - 博客园
随笔 - 321
评论 - 2047
By&Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's Web Platform & Tools Content Team.
原文地址:
全文目录:
在上一次的课程中,你已经学习了如何显示关联的数据,我们将要更新关联的数据。大多数情况下,可能就是更新表的外键字段。对于多对多的关系来说,由于 EF 并没有直接将表与表之间的连接关系暴露出来,你就必须通过显式对相关的导航属性进行添加或者删除实体来完成。
下面的截图展示了我们马上要完成的工作。
6-1& 定制课程的创建和编辑页面
当新的课程实体创建的时候,必须包含相关的 Department。为了达到这个目的,脚手架创建的代码,包括创建和编辑的控制器以及视图,都包含了对 Department 的下拉列表的支持。下拉列表设置 Course.DepartmentId 外键属性,这对于 EF 通过 Department 导航属性来加载 Department 实体来说是必须的。下面将要对脚手架生成的代码进行一些小的改动,增加错误的处理以及对列表内容进行排序。
打开 CourseController.cs, 删除原来的 Edit 和 Create 方法,使用下面的代码替换它们。
public ActionResult Create()
PopulateDepartmentsDropDownList();
return View();
[HttpPost]
public ActionResult Create(Course course)
if (ModelState.IsValid)
db.Courses.Add(course);
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);
public ActionResult Edit(int id)
Course course = db.Courses.Find(id);
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
[HttpPost]
public ActionResult Edit(Course course)
if (ModelState.IsValid)
db.Entry(course).State = EntityState.M
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);
private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
var departmentsQuery = from d in db.Departments
orderby d.Name
ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment);
PopulateDepartmentsDropDownList 方法获取经过对 Name 进行排序的 Department,然后创建用于下拉列表的 SelectList 集合,通过 ViewBag 传递到视图中。这个方法包含一个参数,允许调用方可选地传递一个初始选中项目的值。
HttpGet Create 方法调用没有设置选中项的 PopulateDepartmentsDropDownList 方法,因为对于新创建的课程来说,还没有确定归属的系。
public ActionResult Create()
PopulateDepartmentsDropDownList();
return View();
HttpGet Edit 方法则设置了当前选中的项目,基于当前被编辑课程的 DepartmentId。
public ActionResult Edit(int id)
Course course = db.Courses.Find(id);
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
对于 Create 和 Edit 的 HttpPost 方法来说,在错误信息处理之后,都包含了设置当前选中项目的代码。
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);
代码用来保证即使在显示错误的页面上,也会保持选中的项目。
在 Views\Course\Create.cshtml, 在 Title 之前增加一个新的字段,允许用户输入课程编号。想之前演示的那样,脚手架没有生成主键字段,因为主键字段对用户没有意义。所以需要添加以便用户能够输入这个值。
&div class="editor-label"&
@Html.LabelFor(model =& model.CourseID)
&div class="editor-field"&
@Html.EditorFor(model =& model.CourseID)
@Html.ValidationMessageFor(model =& model.CourseID)
运行 Create 页面 ( 在课程的 Index 页面,点击 Create New& ),然后输入新的课程。
点击 Create,在 Index 中应该可以看到包含新创建课程的列表。系的名称通过导航属性获取到,显示的数据是正确的。
运行编辑页面 ( 在课程的 Index 页面中在某个课程上选择 Edit )
修改一些数据,然后点击 Save,可以看到更新之后的课程数据。
6-2& 增加教师的编辑页面
在修改教师信息的时候,我们希望也能够修改教师的办公室分配。教师实体 Instructor 和办公室分配 OfficeAssignment存在一对一或者一对零的关系,这意味着你必须处理如下的状态:
如果教师原来存在一个办公室分配,但是用户删除了它,那么,你必须移除并且删除这个办公室分配 OfficeAssignment 实体。
如果教师原来没有办公室分配,但是用户输入了一个,你必须创建一个新的办公室分配。
如果用户修改了原来的办公室分配,你必须修改当前的办公分配 OfficeAssignment 实体。
打开 InstructorController.cs,看一下 HttpGet Edit 方法。
public ActionResult Edit(int id)
Instructor instructor = db.Instructors.Find(id);
ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID);
return View(instructor);
脚手架生成的代码不是我们希望的,它生成了一个下拉列表,但是我们希望是文本框,将这个方法使用下面的代码替换掉。
public ActionResult Edit(int id)
Instructor instructor = db.Instructors
.Include(i =& i.OfficeAssignment)
.Include(i =& i.Courses)
.Where(i =& i.InstructorID == id)
.Single();
return View(instructor);
代码中删除了 ViewBag 语句,增加了使用预先加载的相关 OfficeAssignment 和 Course 实体 ( 现在还不需要课程实体,一会就会用到 )。由于 Find 方法不能使用预先加载,所以这里使用 Where 和 Single 方法来获取教师。
将 HttpPost 的 Edit 方法使用下面的代码替换,这里处理了 OfficeAssignment 更新。
[HttpPost]
public ActionResult Edit(int id, FormCollection formCollection)
var instructorToUpdate = db.Instructors
.Include(i =& i.OfficeAssignment)
.Include(i =& i.Courses)
.Where(i =& i.InstructorID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" }))
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
instructorToUpdate.OfficeAssignment = null;
db.Entry(instructorToUpdate).State = EntityState.M
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.");
return View();
return View(instructorToUpdate);
代码中处理了如下的内容:
从数据库中获取了教师 Instructor 实体,并且预先加载了相关的的办公室分配 OfficeAssignment 和课程 Course 导航属性。如同在 HttpGet 的 Edit 方法一样。
使用通过模型绑定获取的数据,更新 Instructor 实体,除了课程 Course 导航属性之外。
If (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" }))
( 第二和第三个参数在属性名的前面没有前缀,而且没有被包含的属性列表 ),如果验证失败, TryUpdateModel 方法返回 false,程序将直接转到方法最后的 return View 语句。
如果办公位置为空,设置 Instructor.OfficeAssignment 属性为 null,在 OfficeAssignment 表相关的行将会被删除。
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
instructorToUpdate.OfficeAssignment = null;
将修改保存到数据库中。
在 Views\Instructor\Edit.cshtml, 在 Hire Date 字段的 div 元素之后,增加新的字段来编辑办公位置。
&div class="editor-label"&
@Html.LabelFor(model =& model.OfficeAssignment.Location)
&div class="editor-field"&
@Html.EditorFor(model =& model.OfficeAssignment.Location)
@Html.ValidationMessageFor(model =& model.OfficeAssignment.Location)
运行页面 ( 选中教师 Instructors ,点击某个教师的 Edit 链接 )
修改办公室位置的值,然后保存 Save。
新的办公位置出现在 Index 页面上,打开数据库中的 OfficeAssgnment 表,可以看到表中的数据行。
运行编辑页面,将办公位置 Office Location 清除掉,然后保存 Save。在 Index 页面上办公位置将显示为空白,在表中的行被删除了。
6-3& 在教师编辑页面增加课程分配
教师可以教授任意数量的课程。现在你需要扩展教师的编辑页面,增加通过一系列复选框分配可能的能力,如下所示。
在课程 Course 和教师 Instructor 之间存在多对多的关系,这意味着你不需要直接访问表之间的关联。而是通过增加或者删除 Instructor. Course 实体来完成。
在 UI 界面上,与教师相关的课程被显示为一组复选框,在数据库中的每一门课程都有一个对应的复选框,教师当前教授的课程对应的复选框处于被选中状态。用户可以通过选中或者取消选中来改变教师教授的课程。如果课程的数量巨大,你可能需要采取其他的方式来显示这些数据,但是你可以使用类似的方式来控制导航属性以便创建和删除关系。
为了为视图提供复选框数据,你需要使用视图模型 ViewModel,在 ViewModels 文件夹中创建 AssignedCourseData.cs,使用下面的代码替换生成的代码。
using System.Collections.G
using ponentModel.DataA
namespace ContosoUniversity.ViewModels
public class AssignedCourseData
public int CourseID { get; set; }
public string Title { get; set; }
public bool Assigned { get; set; }
在 InstructorController.cs 中,找到 HttpGet Edit 方法,调用通过视图模型为视图中复选框提供数据的新方法,如下所示。
public ActionResult Edit(int id)
Instructor instructor = db.Instructors
.Include(i =& i.OfficeAssignment)
.Include(i =& i.Courses)
.Where(i =& i.InstructorID == id)
.Single();
PopulateAssignedCourseData(instructor);
return View(instructor);
private void PopulateAssignedCourseData(Instructor instructor)
var allCourses = db.C
var instructorCourses = new HashSet&int&(instructor.Courses.Select(c =& c.CourseID));
var viewModel = new List&AssignedCourseData&();
foreach (var course in allCourses)
viewModel.Add(new AssignedCourseData
CourseID = course.CourseID,
Title = course.Title,
Assigned = instructorCourses.Contains(course.CourseID)
ViewBag.Courses = viewM
新创建的方法中,读取所有的课程实体,加载到视图模型中,对于每一个课程,检查这个课程是否存在于教师实体的 Courses 导航集合属性中。为了更加有效地遍历教师讲授的课程,将教师讲授的课程通过 HashSet 集合处理,课程中教师讲授的课程的 Assigned 属性被赋予 true。在视图中通过这个属性来判断复选框是否显示为选中状态。最后,这个集合通过 ViewBag 传递到视图中。
然后,增加当用户点击 Save 后执行的代码。将 HttpPost 中的 Edit 方法使用下面的代码替换掉,这里通过调用一个新的方法将教师 Instructor 的导航属性 Courses 更新。
[HttpPost]
public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses)
var instructorToUpdate = db.Instructors
.Include(i =& i.OfficeAssignment)
.Include(i =& i.Courses)
.Where(i =& i.InstructorID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" }))
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
instructorToUpdate.OfficeAssignment = null;
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
db.Entry(instructorToUpdate).State = EntityState.M
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.");
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
if (selectedCourses == null)
instructorToUpdate.Courses = new List&Course&();
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);
if (instructorCourses.Contains(course.CourseID))
instructorToUpdate.Courses.Remove(course);
如果没有复选框被选中,在 UpdateInstructorCourse 方法中,使用一个空的集合来初始化 Courses 导航属性。
if (selectedCourses == null)
instructorToUpdate.Courses = new List();
然后,代码遍历数据库中所有的课程,如果课程的复选框被选中了,但是没有包含在教师 Insturctor 的 Courses 集合中。这个课程将会被加入到导航属性集合中。
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
if (!instructorCourses.Contains(course.CourseID))
instructorToUpdate.Courses.Add(course);
如果课程没有被选中,但是在教师的导航属性 Courses 集合中,就从集合属性中删除掉。
if (instructorCourses.Contains(course.CourseID))
instructorToUpdate.Courses.Remove(course);
在 Views\Instructor\Edit.cshtml 中,在 OfficeAssignment 区域的 div 之后,增加一个 Courses 区域,通过一组复选框来显示课程的状态。
&div class="editor-field"&
int cnt = 0;
List&ContosoUniversity.ViewModels.AssignedCourseData& courses = ViewBag.C
foreach (var course in courses) {
if (cnt++ % 3 == 0) {
&/tr& &tr&
&input type="checkbox"
name="selectedCourses"
value="@course.CourseID"
@(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) /&
@course.CourseID @:
@course.Title
代码创建一个 HTML 的三列表格,每一列中显示课程的标题和编号,后面跟着一个复选框。复选框的名称是相同的 ( &selectedCourses& ),这样在模型绑定的时候就会被连接成一组。复选框的 value 属性设置为 CourseID。当提交页面的时候,选中的复选框代表的 CourseID 将以数组的形式传递给控制器。
在复选框被初始化的时候,选中课程对应的复选框的 checked 属性被设置为选中状态。
在修改了课程状态之后,当回到 Index 页面的时候,需要验证这些修改。因此,需要在页面的表格中增加一列,在这里不需要使用 ViewBag,因为需要的信息已经通过教师实体 Instructor 的导航属性 Courses 传递到页面了。
在 Views\Instuctor\Index.cshtml 中,在 &th&Office&/th& 之后,增加一个 &th&Course&/th& 的标题列,如下所示。
&th&Last Name&/th&
&th&First Name&/th&
&th&Hire Date&/th&
&th&Office&/th&
&th&Courses&/th&
然后,在办公位置的单元格之后增加一个详细内容的单元格。
foreach (var course in item.Courses)
@course.CourseID @:
@course.Title &br /&
运行 Instructor 的 Index 页面,检查教师的授课情况。
点击 Edit 查看编辑页面。
修改一些课程的授课情况,然后保存 Save,修改的结果在 Index 页面中可以看到。
你已经完成了修改关联数据的工作。通过目前的课程你已经可以完成增、删、改、查所有的操作,但是还没有考虑并发问题。下一次我们将探讨并发问题,展示处理并发的方式,然后对已经完成的实体的增、删、改、查的代码增加并发处理。当前位置: >>
动态多条件查询分页以及排序(一)--MVC与Entity Framework版url分页版
博客园_love.net 您正在查看的源包含频繁更新的内容。 订阅源后, 该源会添加到“常见源列表” 中。该源的更新信息会自动下载到计算机,通过 Internet Explorer 及其他程 序可以查看这些信息。进一步了解源。 订阅该源动态多条件查询分页以及排序(一)--MVC 与 Entity Framework 版 url 分页版2 年1 月4日 ? ?012? ?2? ?? ,?:46:00 | wlf 8一.前言多条件查询分页以及排序 每个系统里都会有这个的代码 做好这块 可以大大提高开发效率 所以博主分享 下自己的 6 个版本的 多条件查询分页以及排序二.目前状况不论是 ado.net 还是 EF 在做多条件搜索时 都有这类似的代码 这样有几个不好的地方1.当增加查询条件,需要改代码,对应去写相应的代码。2.对多表查询以及 or 的支持 不是很好。 而我们很常见的需求不可能是一个表的 查询3. 这样写表示层直接出现 了 SQL 语句 或者 linq 的拉姆达表达式 这是很不 好的 表示层不应该知道数据访问技术4.有的时候 我们的业务逻辑层接口是这样的 IList&***& seach(string name,string age,string classname,int pageindex,int pagesize,string oderby)这个时候 多一个查询条件 对应的还要去修改业务逻辑层 EF 由于传递的是表 达式树,则更是苦不堪言.三.我们接下来应该实现的目标1.当增加条件时 不需要修改代码 只需要在 view 上 增加相应的查询框即可2.我们的多条件查询 应该做到无关表示层技术(是否是 MVC 或 webform)3.应该支持多表查询 以及 OR 的操作 4.应该支持更多的查询 like in 不等于 等操作5.关于分页 不应该与数据访问耦合在一起 个人感觉 分页只需要知道总条数 以及当前页数 和每页多少条 然后生成分页代码即可 不应该与 EF 等耦合到一 起 分页应该是独立出来 可控制的6.客户可以自己添加搜索条件 这是个强大的功能 想怎么查 客户自己添加即可7. 统一查询接口 做到有条件增加 不修改代码8.分页应该支持 url 重写或者 mvc 路由 不应该生成的连接只是?pageindex=值 这种的四.我实现的几个多条件查询分页版本以适应各种需 求(每篇会写一个版本的实现以及代码的提供,有好 意见的欢迎留言)1.url get 提交版 实现 URL 分页多条件查询以及排序的好处是 我们可以把当前 的搜索条件 当前页数 排序等 都在 url 上显示 可以方便的发给好友 以及后退 等浏览器操作(个人给 dudu 老大建议,博客园应该做成这种的)2.post 提交版本的 搜索条件较大 不适合用 url 的3.ajax+mvc 版本的 (关于 AJAX 实现 我认为有两种 1.服务端实现好内容的拼接, 传输给客户端 2 直接传递 json 给客户端 客户端来做拼接) 这个版本会实现服务端拼接内容 好处是 服务端做拼接简单 能做更多的事情 维护服务端代码方便 尤其是强大的 Razor4.ajax+webapi+Knockoutjs 版本这个版本 我实现的是 服务端只是传递 json 这样服务端效率很高 喜欢这样开 发方式的朋友 就是前端拼接字符是很不好的 代码会显得很乱 这个时候 前端 就需要一个模版引擎我用的是 jquery-temp 配合强大的 Knockoutjs5.动态增加查询条件版这个版本我实现的是 客户可以自己添加查询条件 查询条件是动态的6.移植到 webform 版7.EF 应该得到表达式树 让 EF 自己生成 SQL 语句 这样方便扩展 实现其他方法五.url get 提交版开始废话了那么多 今天就写下 url get 版的多条件查询 以及分页 排序先上个丑陋界面的截图 界面虽丑 但是功能齐全 查询 分页 排序齐全 看下控制器的代码 我们这里没有各种条件判断 判断哪个为空 使用哪个排序的判断 我们的业务逻 辑层接口 没有接受表达式树的参数 与数据访问层不是耦合的 而是使用了 Querymodel 对象 来抽象所有查询条件这样 这个对象 可以翻译成 EF 的表达式树 也可以翻译成 SQL 语句 所以我们的 表示层 MVC 不用非要使用 EF 的的底层 而我们的分页 只需要知道当前页数 总数据 以及 每页大小 去自动生成分页关于分页 很多人喜欢把这个扩展 htmlhelper 做成 html.pager 这种做法 这 样很多表示层的展示 都会耦合到 这个里面 举个例子,比如把分页的布局 从 table 变成 ul 这样的 这是纯表示层的本应该修改 view 现在却要去改 htmlhelper 而且你的分页代码越强大 则 htmlhelper 里的内容越多 修改起来越不容易 所以我的意见是 做分页的 最好 使用 html.Partial 然后把分页的逻辑写到部分页里 这样就实现了分页 只关注分页 与其他的一切都没有关系 我们要做 的就是构建 Pager 类 然后传递给模版即可 例如这个版本要注意的就是 分页后要保存查询条件六.url get 提交版实现分析1.先来说下多条件查询吧我们要做的是把各个条件构建成 QueryModel 而如何构建则是关键 我们想下 获得 mvc 提交的内容的是根 据 name 所以我们可以固定一个 name 的格式 如 ID(like 操作): &input type=&text& name=&[Contains]StuId& value=&@StuId& /&这样我们可以通过正则获取想要信息的部分 然后在模型绑定构建出 QueryModel 对象 然后再把这个对象 翻译成 SQL 语句 或者表达式树 就可以用于 ado 或者 EF 操作了顺便重点说下 这个思路 园子里的重典早都实现了 而且实现的很好 大家可以去他的博客看下 以后的各 个版本的分页 都会用这个表达式树构建为基础 这是他文章的链接 重典老哥的实现(ps:这周刚刚见过他本人,聊得甚欢,开心.希望以后还可以多聚聚)不过重典老哥的表达式树构建 我改成自己的实现了 整体思路还是一样的 自恋的说下 ~ 我的可读性更高 些 哈哈 因为重典已经描述的很详细了 具体可以看我的代码和他的文章当然我们得到 通过 QueryModel 得到的表达式树 通过扩展方法 直接调用 Where 方法即可 让我们看下我 们的 EF 的业务逻辑层public class StudentService:IStudentService {/// /// /// /// /// /// /// /// /// ///&summary& 按条件搜索 &/summary& &param name=&query&&搜索条件&/param& &param name=&pageIndex&&当前页数(索引从 1 开始)&/param& &param name=&pageSize&&每页显示条数&/param& &param name=&total&&总条数&/param& &param name=&orderBy&&排序字段&/param& &param name=&ascending&&是否升序&/param& &returns&&/returns& public IList&Student& Search(QueryModel query, int pageIndex, int pageSize, out int total, string orderBy, bool ascending) { IList&Student& stulist = Builder&Student&.CreateListOfSize(321).TheFirst(44).With(x =& x.StuName = &hy&).And(x =& x.Nullint = 1).And(x =& x.LoveGril = &LILI&).And(x =& x.CreateTime = new DateTime()).And(x =& x.Birthday = new DateTime()).And(x =& x.Stuclass = new StuClass() { ClassId = &2&, ClassName = &二班& }).TheNext(33).With(x =& x.StuName = &wlf&).And(x =& x.Nullint = 2).And(x =& x.LoveGril = &MM&).And(x =& x.CreateTime = new DateTime()).And(x =& x.Birthday = new DateTime(0)).And(x =& x.Stuclass = new StuClass() { ClassId = &1&, ClassName = &一班& }).TheNext(244).And(x=&x .Stuclass=new StuClass(){ClassId = &3&, ClassName = & 三班&}).Build(); var dbcontext = stulist.AsQueryable(); //模拟 EF context 假 设数据库里原数据为 200 条 dbcontext = dbcontext.Where(query); total = dbcontext.Count();//执行查询数量 sql dbcontext = dbcontext.OrderBy(orderBy, ascending).Skip(pageSize * (pageIndex - 1)).Take(pageSize); return dbcontext.ToList();//执行分页排序查询 sql } }这里说下 为了大家调试方便 不用真正的数据库 用了测试神奇 NBuilder 来模 拟的 然后转换成 AsQueryable 来模拟 EF 的看上面代码 可以看到 没有了各个分支的条件判断 以及排序的判断 以后多出 查询条件 不需要修改业务逻辑层了~2.接下来说下 url get 提交的思路 上问说过自己的观点 分页最好能放到部分页 而不是扩展 htmlhelper 所以我 的实现方式是把构建 Pager 分页视图类 传递给 部分视图 来做如何展示 一些 分页有关的逻辑封装在 Pager 里下面是 Pager 的代码public class Pager { public Pager(int currentPageIndex, int totalItemCount, int pagesize = 20) { this.TotalItemCount = totalItemC this.PageSize = this.CurrentPageIndex = currentPageIndex & TotalPageCount ? 1 : currentPageI } /// &summary& /// 当前第几页 /// &/summary& public int CurrentPageIndex { } /// &summary& /// 每页显示多少条 /// &/summary& public int PageSize { } /// &summary& /// 总共多少条记录 /// &/summary& public int TotalItemCount { } /// &summary& /// 总共多少页 /// &/summary& public int TotalPageCount { get { double pageCount = (double)TotalItemCount / (double)PageS pageCount = Math.Ceiling(pageCount); return (int)pageC } } /// &summary& /// 是否显示 /// &/summary& public bool IsShow { get { if (TotalPageCount & 0) { } else { } } } /// &summary& /// 是否显示上一页 /// &/summary& public bool HasPreviousPage { get { return (CurrentPageIndex & 1); } } /// &summary& /// 是否显示下一页 /// &/summary& public bool HasNextPage { get { return (CurrentPageIndex & TotalPageCount); } } } 而分页的部分视图 只用根据这个类 去管理如何展示这里分享几个小技巧技巧一.因为我们要实现下面需求1. 我们要保存以前的 URL 信息再里面 不能让以前的消失 2 需要支持 url 路 由后的 分页 比如 控制器/方法/Page1 而不是?pageIndex=1 3.因为很多个页 面都要用到 所以要与方法名解耦实现这个的技巧关键 就是使用 RouteValueDictionaryvar queryString = ViewContext.HttpContext.Request.QueryS var dict = new System.Web.Routing.RouteValueDictionary(ViewContext.RouteData.Values); foreach (string item in queryString.Keys) { dict[item] = queryString[item]; }通过上面的代码 就可以保存住以前的参数了 接着就是dict[&PageIndex&] = 1;//设置页码 @Html.RouteLink(&首页&, dict); 这样就可以设置分页 以及解耦控制器 和方法名了~如果你设置了类似的路由routes.MapRoute(&Page&, &{controller}/{action}/page{PageIndex}&, new { controller = &WLFQuery&, action = &Index&, PageIndex = 1 });那么也是支持的~2.技巧二因为我们的分页 支持通过下拉框选择页码后自动跳页 有点儿像 webform 的 autopostback 而我们又是 get 提交版的 需要解决一个问题 选择后自动跳页 需要带上以前的查询条件 不能跳完页以后 查询条件消失了这里采用的办法 借鉴了一下 aspnetpager 作者的 urlpager 的思路用一个隐藏的 a 标签 这个 a 标签生成的连接 保存了当前的查询条件等 他的 页面数 显示成&*pageindex* 当我们选择下拉框跳页后 用选择的值 替换这个 &*pageindex* 然后跳转到当前 href 则解决了上述问题 上代码&text&跳转至&/text& &select id=&pageselect& onchange=&selectchange()&& @for (int i = 1; i &=@Model.TotalPageC i++) { var selected = &&; if (i==Model.CurrentPageIndex) { selected = &selected='selected'&; } &option value=&@i& @selected&@i&/option& } &/select& { dict[&PageIndex&] = &*pageindex*&; } &a style=&display:none& id=&pagelink& href=&@Url.RouteUrl(dict)& &&/a& // &script type=&text/javascript&& function selectchange() { var pageselect = document.getElementById(&pageselect&); var pageselectValue = pageselect.options[pageselect.selectedIndex]. var linkdom= document.getElementById(&pagelink&); var href = linkdom. href = href.replace(&*pageindex*&, pageselectValue); window.location = } &/script& &text&页&/text&3.点击搜索时 get 提交 并回到第一页&form action=&@Url.Action(&index&, new { PageIndex = 1 })& method=&get&& &/form& 具体代码可以下载源码看~七. URL GET 提交的遗憾上面基本功能已经实现 但是有些遗憾的是 大家知道 mvc 没有 viewstate 机制 而且我们的又是 URL 分页 及时是 webform 也会面临这个问题虽然我们通过 url 保存了搜索的状态 但是没有解决点击分页后 搜索框的内容 还在。 所以会出来一个奇怪的现象 按条件搜索完 比如搜索姓名为 hy 的 然后点 击分页 搜索框的内容没了但是分页后内容却还是 hy 的 因为我们的 url 里存的有搜索结果 但是重新加载 后 文本框的内容却没了 这样虽然无伤大雅 但是还是略感不爽 如果非要解决 也不是没有办法 只是觉得都不是很完美方案 1.ID(like 操作): &input type=&text& name=&[Contains]StuId& value=&@Request.QueryString[&[Contains]StuId&]& /&&br /&因为我们的 url 里保存的有真实的搜索条件 所以我们可以通过 Request.QueryString[&[Contains]StuId&] 直接得到 但是我觉得在 MVC 里 出 现 Request.QueryString 这样的信息 不好看方案 2. ViewContext.Controller.ValueProvider.GetValue(&[Contains]StuId&).RawV alue 通过这样 也可以得到值方案 3.在控制器里ViewBag.Query = 记录搜索条件 然后在 view 里读出来QueryModel query=ViewBag.Query as QueryM string StuId=query.Items.Where(x =& x.Field == &StuId&).Select(x=&x.Value).FirstOrDefault()!=null?query.Items.Where( x =& x.Field == &StuId&).Select(x=&x.Value).FirstOrDefault().ToString():&&;上面三个方式 感觉都有不完美的地方 因为我们还原的内容 不只是 text 有可 能是 多选框 下拉列表 等 所以要做处理 就稍微麻烦了下 比如string[] newarrlovewgril = (string[]) MMischeck= newarrlovewgril.Contains(&MM&) ? &checked='checked'& : &&; LUCIischeck = newarrlovewgril.Contains(&LILI&) ? &checked='checked'& : &&; GAGAischeck = newarrlovewgril.Contains(&GAGA&) ? &checked='checked'& : &&;MM&input type=&checkbox& name=&lovegril& value=&MM& @MMischeck /& LILI&input type=&checkbox& name=&lovegril& value=&LILI& @LUCIischeck /& GAGA&input type=&checkbox& name=&lovegril& value=&GAGA& @GAGAischeck /& 我再想 把这些还原也做成自动化的 不用任何代码的 求集思广益 希望大家给 些建议八.总结EF 实现动态查询以及排序的关键 就在于表达式树的构建。所以学会并理解表达 式树很关键,这个不尽在这里有用,用来代替反射也可以提高效率~不过表达式树 的生成 建议实际项目中 加上缓存。表达式树的学习 可以看下这几篇/Terrylee/archive//custom-linq-provid er-part-1-expression-tree.html/Ninputer/archive//expression_tree1.h tml/en-us/library/bb397951.aspx短短文章 花了半天才写完~ 希望对大家有帮助 或者能给大家一些启示 有问题 可以留言交流 欢迎批评和建议 感谢阅读~ 顺便帮忙宣传下 我在的两个 QQ 群吧 217091 有兴趣的可以进来讨 论过几天介绍另两个版本 POST 提交版和 MVC+EF AJAX 多条件查询 分页 排序最后附上本节代码下载 (代码比较粗糙,主要做演示)转载请说明来自 /wlflovenet/archive//MVC_EntityFramew ork_Query.html本文链接MVC3+EF4.1 学习系列(十一)----EF4.1 常见的问题解 决2 年1 月3 日 ? ?011? ?2? ?0? ,?:40:00 | wlf 8 文章索引和简介博客写了 10 篇了~有很多朋友私信问了一些问题,而且很多问题 大家问的都一 样 这里说说这些常见问题的解决办法.如果大家有更好的解决办法~也希望分享 出来问题大概为这几个一.ef4.1 codeFirst 修改表结构 增加字段等 EF code first 需要重新生成库 导致数据丢失的问题. 二.ef4.1 没有了 edmx 等复杂的东西 变得简单 干净 但如何使用存储过程,存 储过程可以返回表 可以返回数值 也有可能是执行修改 删除 增加等 该怎么 做?三.ef4.1 如何使用数据库视图?每个视图都要去建立对应的实体类么?有简 单的方法么?四.ef4.1 如何执行 SQL 函数等操作?五.ef4.1 如何跨数据库访问?六.ef4.1 执行连接查询?什么时候执行左连接? 什么时候执行内连接? ef 根据什么去判断?七.新手使用 ef4.1 常见的一些报错信息八.ef4.1 返回 datatable 方便做报表等一些复杂操作其实这些问题是比较简单的~ 所以此文送给刚用 ef4.1 的新手~下面开始一一解决这些问题一.ef4.1 codeFirst 修改表结构 增加字段等 EF code first 需要 重新生成库导致数据丢失的问题 说这个问题前 首先先说下 我使用 ef4.1 codefirst 的目的. 是因为可以有更纯 净的 POCO 不再有 EDMX 这些东西 而不是真正的用 code first 先有代码 再生 成数据库.所以 我虽然使用的是 codefirst 但是本质依然是数据库优先.所以这个被问的很多的问题 解决办法其实是非常简单的.只要你的数据库已经 存在了 那么即使你用 code first ef 也不会给你去生成数据库的. 这个时候 你增加表字段 甚至增加表 只要把实体类也相应的修改 则数据库里的数据 是不会被清空的.说下我的开发步骤 先设计数据库 并建立数据库=&通过 EF 工具生成映射和实体 类=&开发代码 当遇到修改时=& 先修改数据库如添加字段或表等=&再修改实体 类=&继续开发这样就不会有重新生成数据的烦恼了 而且项目里也不会出现 edmx~还有 使用 EF4.3 的数据迁移功能 也可以完美解决~二.ef4.1 没有了 edmx 等复杂的东西 变得简单 干净 但如何使用存 储过程,存储过程可以返回表 可以返回数值 也有可能是执行修改 删除 增加等 该怎么做? 说这个问题前 依然先说下我的观点.个人认为 既然使用 orm 框架 就应该把业 务逻辑等 都放到业务逻辑层 而不应该再使用存储过程。 我更偏重重业务逻辑层 轻存储过程这样的开发~再 ef4.0 里 添加存储过程 比较容易 有 edmx 调一调 存储过程就添加上了 但 是在 ef4.1 里 只有干净的 poco 不再有 edmx 了 改怎么办呢?尤其是存储过程 可以是查表 查值 或者执行修改删除.一个一个来解决1.执行返回表类型的存储过程先上存储过程 随手写的一个最简单的Create PROCEDURE [dbo].[ProSelectStu] @StudentID int AS BEGIN Select Student.* from Enrollment,Student where Enrollment.StudentID=Student.StudentID and Enrollment.StudentID=@StudentID END GO执行存储过程的方法 是用直接执行 sql 的方式 我在我的文章第九篇 有过详细 的介绍~大家可以先去看下 执行表的存储过程 其实是非常强大的 延迟加载 等都有体现 博客园的陆老师 已经写了 写的非常清楚了~我这里就不再写了 大家可以去他那看下 提供个连 接~EF 使用存储过程查询表的2.执行返回值的存储过程先上存储过程CREATE PROCEDURE [dbo].[ProSelectCount] @StuId int AS BEGIN select COUNT(*) from Enrollment where StudentID=@StuId END一个简单的查询数量这里用 sqlQuery 执行访问 数据库 因为需要提供返回类型 而我们返回的是 int 所以先得到 int 的类型 3.执行增删改CREATE PROCEDURE [dbo].[ProDel] @stuId int, @courseId int AS BEGIN DELETE FROM [WLFSchool].[dbo].[Enrollment] where StudentID=@stuId and CourseID=@courseId END这个用的是操作数据库 返回受影响行数 三.ef4.1 如何使用数据库视图?每个视图都要去建立对应的实体类 么?有简单的方法么?先说下最传统的方法 只需把视图 当成表 建立对应的实体类 然后加到 dbcontext 里即可。没什么难度。再说一个问题 使用 linq 有个非常美妙的功能 投影映射 和 C#3.0 的 匿名函数 让我们很多情况 不需要视图的from c in classes from s in students where c.ClassID == s.ClassID order by c.CreateTime select new { Name = s.Name, Age = s.Age, ClassName = c.ClassName };再通过 var result 接受上面的值 这样我们就不用去数据库建视图 不用再建 实体类 是不是很省事呢?如果公司强大的 DBA 已经给我们建好了很多视图 是不是就要一个个去写实体 类呢?如果你使用的是 C#4.0 那么可以用动态的 来解决这个问题~像下面这样使用 是不是很爽 这个不仅可以查询视图 普通的表 只要是 SQL 语句 都可以自动生成动态类 让 你用~下面是扩展方法 和 使用 Emit 来动态构建 感谢 ASP.NET f 给的帮助~~public static class DatabaseExtensions { public static IEnumerable SqlQueryForDynamic(this Database db, string sql, params object[] parameters) { IDbConnection defaultConn = new System.Data.SqlClient.SqlConnection(); return SqlQueryForDynamicOtherDB(db, sql, defaultConn, parameters); } public static IEnumerable SqlQueryForDynamicOtherDB(this Database db, string sql, IDbConnection conn, params object[] parameters) { conn.ConnectionString = db.Connection.ConnectionS if (conn.State != ConnectionState.Open) { conn.Open(); } IDbCommand cmd = conn.CreateCommand(); mandText = IDataReader dataReader = cmd.ExecuteReader(); if (!dataReader.Read()) { //无结果返回 Null } #region 构建动态字段 TypeBuilder builder = DatabaseExtensions.CreateTypeBuilder( &EF_DynamicModelAssembly&, &DynamicModule&, &DynamicType&); int fieldCount = dataReader.FieldC for (int i = 0; i & fieldC i++) { //dic.Add(i, dataReader.GetName(i)); //Type type = dataReader.GetFieldType(i); DatabaseExtensions.CreateAutoImplementedProperty( builder, dataReader.GetName(i), dataReader.GetFieldType(i)); } #endregion dataReader.Close(); dataReader.Dispose(); cmd.Dispose(); conn.Close(); conn.Dispose(); Type returnType = builder.CreateType(); if (parameters != null) { return db.SqlQuery(returnType, sql, parameters); } else { return db.SqlQuery(returnType, sql); } } public static TypeBuilder CreateTypeBuilder(string assemblyName, string moduleName, string typeName) { TypeBuilder typeBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName(assemblyName), AssemblyBuilderAccess.Run).DefineDynamicModule(moduleName).DefineType (typeName, TypeAttributes.Public); typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); return typeB } public static void CreateAutoImplementedProperty( TypeBuilder builder, string propertyName, Type propertyType) { const string PrivateFieldPrefix = &m_&; const string GetterPrefix = &get_&; const string SetterPrefix = &set_&; // Generate the field. FieldBuilder fieldBuilder = builder.DefineField( string.Concat( PrivateFieldPrefix, propertyName), propertyType, FieldAttributes.Private); // Generate the property PropertyBuilder propertyBuilder = builder.DefineProperty( propertyName, System.Reflection.PropertyAttributes.HasDefault, propertyType, null); // Property getter and setter attributes. MethodAttributes propertyMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideByS // Define the getter method. MethodBuilder getterMethod = builder.DefineMethod( string.Concat( GetterPrefix, propertyName), propertyMethodAttributes, propertyType, Type.EmptyTypes); // Emit the IL code. // ldarg.0 // ldfld,_field // ret ILGenerator getterILCode = getterMethod.GetILGenerator(); getterILCode.Emit(OpCodes.Ldarg_0); getterILCode.Emit(OpCodes.Ldfld, fieldBuilder); getterILCode.Emit(OpCodes.Ret); // Define the setter method. MethodBuilder setterMethod = builder.DefineMethod( string.Concat(SetterPrefix, propertyName), propertyMethodAttributes, null, new Type[] { propertyType }); // Emit the IL code. // ldarg.0 // ldarg.1 // stfld,_field // ret ILGenerator setterILCode = setterMethod.GetILGenerator(); setterILCode.Emit(OpCodes.Ldarg_0); setterILCode.Emit(OpCodes.Ldarg_1); setterILCode.Emit(OpCodes.Stfld, fieldBuilder); setterILCode.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getterMethod); propertyBuilder.SetSetMethod(setterMethod); } }四.ef4.1 如何执行 SQL 函数等操作?添加引用 System.Data.Objects.SqlClient.SqlFunctions 主要是这个命名空 间使用方法~上一个工作中的例子~var query = from s in student.T_StudentInfo where SqlFunctions.DateDiff(&day&, s.CreateTime, &&) == 0 select s.StudentN使用 SQL 的 datadiff 函数~~五.ef4.1 如何跨数据库访问?每次别人问我这个问题 毫不犹豫的把站长 dudu 的文章发过去~ 他已经很好的 解决了~/dudu/archive//entity_framework_cross _database_query_fact.html 核心思路 欺骗 SQL 利用创建同义词去实现六.ef4.1 执行连接查询?什么时候执行左连接? 什么时候执行内 连接? ef 根据什么去判断?当我们做多表查询时 用 Include 强制加载 或用 select 去查询时 发现生成 的 SQL 语句 有时是左连接 有时是 inner join。其实 EF 是根据我们实体类的连接字段 是否可空来判断的~比如外键 studentIDpublic Nullable&int& StudentID { }是否可空 就会造成 是 left join 还是 inner join~~补充下~~ 有个朋友说 这个设为空了 依然执行的是内连接啊~注意看下你的关系那块 也要设为可空 用这个 HasOptional 而不要用 HasRequired ~~当你的外键可以为空时 用 HasOptional 否则用 HasRequired这块也会决定你是内链接 还是 左连接~~ 七.新手使用 ef4.1 常见的一些报错信息1.执行命令定义时出错出现这个错的原因有很多 数据库语句错误 我们可以先通过监测 SQL 语句是否 发送到数据库 然后执行这条 SQL 语句 看看是否有问题造成这个错的原因 还有可能是 连接对象一直被占用 因为 EF 有延迟加载 只是 select 时 并没有真正去数据库执行我们可以先把前面的查询语句 tolist 等 再去执行下面的操作2.System.Data.Edm.EdmEntityType: : EntityType“Enrollment”未定义键。请 为该 EntityType 定义键。 System.Data.Edm.EdmEntitySet: EntityType: EntitySet ?Enrollments? 基 于未定义键的类型 ?Enrollment?。 遇到这种情况 尝试给主键加上[Key]3.更新条目错误依然检测数据库语句 是否有外键约束导致插入错误等4.LINQ to Entities 不识别方法“System.String ToString(System.String)” 因此该方法无法转换为存储表达式 或者不识别其他方法......类似于这样的错误因为 SQL 里没有这样的方法 所以无法转换成 SQL 语句 SqlClient 和 Linq Provider 没有实现那个方法对应的 SQL,所以会提示不支持解决办法:1. 把要转换的值提前转换好 而不要再 linq 或拉姆达表示里写 这样的转换 语。就是把变量 .ToString() 提到外面声明个变量 然后在拉姆达表达式里 直接使 用这个变量2. 转换成 EnumerableIEnumerable 是直接执行方法 ,而不调用 Provider 来转成其它的方式这样会把数据库里的查询出来 然后在内存里操作 所以数据库量大时 效率会低 ~八.ef4.1 使用 datatabledatatable 在有的时候是非常有用的 例如 做报表等 因为我们不可能为每个报 表建一个 实体类 这样比较麻烦 这个时候返回 datatable 则比较有用写一个扩展方法/// &summary& /// EF SQL 语句返回 dataTable /// &/summary& /// &param name=&db&&&/param& /// &param name=&sql&&&/param& /// &param name=&parameters&&&/param& /// &returns&&/returns& public static DataTable SqlQueryForDataTatable(this Database db, string sql, SqlParameter[] parameters) { SqlConnection conn = new System.Data.SqlClient.SqlConnection(); conn.ConnectionString = db.Connection.ConnectionS if (conn.State != ConnectionState.Open) { conn.Open(); } SqlCommand cmd = new SqlCommand(); cmd.Connection = mandText = if (parameters.Length&0) { foreach (var item in parameters) { cmd.Parameters.Add(item); } }SqlDataAdapter adapter = new SqlDataAdapter(cmd); DataTable table = new DataTable(); adapter.Fill(table);
}调用如下protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { GridView1.DataSource = GetDataTable(); GridView1.DataBind(); } }public DataTable GetDataTable() { GardenHotelContext context = new GardenHotelContext(); int LanType = 0; int state = 0; SqlParameter[] sqlparams=new SqlParameter[2]; sqlparams[0]=new SqlParameter(&LanType&,LanType); sqlparams[1]=new SqlParameter(&state&,state); DataTable DataTable = context.Database.SqlQueryForDataTatable(&select LeaveName,LeaveEmail from LeaveInfo where LanType=@LanType and State=@State&, sqlparams); return DataT }再分享一种方法 先上调用效果 利用返回的 var 匿名类型 这样就无需声明实 体类了public DataTable GetDataTable2() { GardenHotelContext context = new GardenHotelContext(); var list = (from l in context.LeaveInfoes group l by l.LanType into g select new { g.Key, num = g.Count() }).ToList(); return PubClass.ListToDataTable(list); }核心方法 反射调用#region反射 List To DataTable/// &summary& /// 将集合类转换成 DataTable /// &/summary& /// &param name=&list&&集合&/param& /// &returns&&/returns& public static DataTable ListToDataTable(IList list) { DataTable result = new DataTable(); if (list.Count & 0) { PropertyInfo[] propertys = list[0].GetType().GetProperties(); foreach (PropertyInfo pi in propertys) { result.Columns.Add(pi.Name, pi.PropertyType); } for (int i = 0; i & list.C i++) { ArrayList tempList = new ArrayList(); foreach (PropertyInfo pi in propertys) { object obj = pi.GetValue(list[i], null); tempList.Add(obj); } object[] array = tempList.ToArray(); result.LoadDataRow(array, true); } } } #endregion当然 解决这个问题 也可以用 上面的 动态视图的方法去解决~九.总结目前上面这几个问题 被问的比较多~ 写个文章 以后就不用再回答类似的问题 啦~我的解决方法不是最好的 有更好的解决办法 欢迎回复~期待你的精彩回复!如果大家还遇到什么 EF4.1 的问题 或者 mvc3 上的问题 都欢迎留言 ~我尽力帮 大家解决~本文链接 委托与事件以及应用2 年1 月2日 ? ?011? ?1? ?? ,?:54:00 | wlf 8 因为最近再带两个实习的朋友~ 他们在理解委托这块 不明白能用来做什么 对 这块理解的不好 更不用说委托扩展来的 拉姆达表达式 事件等了~ 但是这些很 重要 在 MVC EF4.1 里 都会有很多拉姆达表达式的影子 在做组件开发 用户控 件开发 事件的运用 一定也是少不了的 稍微深入点儿的说 用于两个对象之间 的通讯 用来解耦 用委托事件 是很不错的选择 而设计模式里的观察者模式 也 是基于委托事件的应用 还有做 winform 开发的 里的线程 WPF SL 的异步调用 等 都是有这委托的影子的 所以 这个还是很重要的。也是一定要掌握的~ 希望 通过这篇文章 能让大家更加了解委托以及如何运用~一.委托以及延伸先看下 MSDN 的介绍吧~------ delegate 通俗的说 委托就是可以实现 把方法做为变量来传递 1.先写个最简单的委托的用法 静态的和非静态方法的 结果会输出 您好 wlf Hello wlf 这是最原始的委托 2. 进化为匿名方法 声明完委托后 还要声明方法 是不是很麻烦 如果不声明方法 用匿名方法 可以 帮我们 看 代码减少了很多吧~ 3.再进化为拉姆达表达式 上面的虽然简单了不少 但是 还能更简单的 用拉姆达表达式~看 这就是拉姆达表达式的演变 一定要会这个~ 因为在 EF LINQ 等 有很多用拉 姆达表达式的~ 4.用 Action 和 Func 继续简化 上面的代码以及很简单了 但是还有个很不爽的一点~ 要声明委托! 可以不声明 么?当然可以~ 先看 MSDN 介绍 Action 和 Func 通俗的讲 这两个都是用来帮你声明委托 Action 用于帮你声明没用返回值的委托 Func 则是有返回值的 最后一个参数 为返回值看~ 以前的版本 总是要声明一个 HelloWorld 的委托 现在只有两行代码就可 以了 下面顺便掩饰了有返回值的 func 的例子。第一个参数 是传递 参数的类型 第二个是返回值的类型 ~ 这里说下 这俩函数都有 16 个重载~ 所 以多个参数是没问题的~ 5.说说委托的好处 委托的好处 应用 文章开始已经说了很多了 这里就拿文章开头的例子 体会下 委托的好处当我们再多一种语言来问好时 只需增加一种 Action 即可 而不需要改动 sayHello 方法 否则 这里将充满了 if else 的判断6.工作里的应用再 LINQ 或 EF 里 都有 个很重要的 数据刷选的功能 WHERE 它的参数就是 Func 直接上代码 自己看注释~ 看 可以很轻松的实现 数据过滤~ 喜欢这种函数式风格的编程方式 我们可以点 顺便介绍工作中的 Action嗯 有图有真相~ 这个 ForEach 的参数 正是 Action 以后遇到这几个 新手们就 不用再陌生了~实现把集合里 大于 4 的打印出来的 这个功能 全部都是点儿出来的~ 提示:尽量不要再 ForEach 里进行集合的操作 比如 移除集合里的元素等~ 否 则会出来不希望出现的结果~ 出来的结果为 2 4 6 原因 反编译 我们发现是 for 按索引删除上面的话题扯远了~ 回归主题二 .事件以及应用1.先用委托来实现一个常见的工作场景(委托代替事件) webForm 用户控件 我们在实际工作中 用的都是非常多的 说一个场景 一个用 户控件 里面一个 textbox 一个 button 一个页面引用了 这个用户控件 这个页面里有一个 label 我希望 点击 用户控件里的 button 改 变 把 textbox 里的值 绑定到 label 上 这就是一个典型的委托事件的 应用 1.直接上代码 先上用户控件的声明一个委托 当点击按钮时 调用 2. 引用用户控件的页的代码 当然 我们会了拉姆达表达式 完全可以写成这里主要说的是 委托的注册 记得要写到 !IsPostBack 外面 大概顺序是这样的 先执行 用户控件的外部变量赋值 =& 再执行父页面的用外 部变量赋值(本例子中没有)=& 再执行父页面的 pageload=&用户控件的 pageload 如果不写到 !IsPostBack 外面 点击按钮后 先执行 用户控件的外部变量赋值 (以前被赋值的委托会又为空,所以这时需要在页面 pageload 里 重新注册 而 不能写到!IsPostBack) 里面~ 3 只用委托的缺点 功能虽然实现了 但是我们想 我们可以在 父页面 给 Puchange.onChangevent 赋值为 null 或者篡改为别的 而我希望我们在引用时 只能添加 或移除 这样 就更 完美了 所以 事件就出来了~2.使用事件重新实现上面的代码1.用户控件里的 2.父页面3.winform 或 Silverlight 里尽量使用委托和事件 来通讯 这样可以更好的降低耦合 避免了 子窗体中出现父窗体对象 降低耦合三.委托的其他资料委托的逆变与协变 何时使用委托何时使用接口 正则表达式委托 异步委托编程 基于事件的异步模式概述四.总结文章比较入门 希望新手能对委托事件有更好的了解 平且能把委托扩展来的东 西 熟练运用到工作中 来简化代码~ 并且能把事件熟练运用~文章的目的也达到 了本文链接WCF REST 工作总结(二)2 年1 月3 日 ? ?011? ?0? ?0? ,?1:09:00 | wlf 1 上一篇 我们搭建了 WCF REST 的服务 平且通过 三种方式发送 get 请求 实现了 获取 json 没看过的朋友可以先去看下----REST 工作总结一今天主要写下 POST 等其他方式 发送请求 以及 流方式 文件的上传与下载一.Post 提交数据先来想下 POST 和 Get 的不同 Get 方式 我们直接通过 url 来传递参数 先来 看下我们的 创建方法吧[WebInvoke(UriTemplate = &Tasks/Add&, Method = &POST&, RequestFormat = WebMessageFormat.Json)] void Create(PocoTask pocoTask); 我们需要接受一个实体类 格式为 json 格式 这就再次出现上文的问题 我们的 客户端没有实体类 没法把实体类序列化为 json 难道要自己拼接 json 么? 当 然不用。那简直太痛苦了。这里给大家推荐 Json.Net 这里有两个类 可以帮我们完成 单个实体的转换 以 及集合实体的转换 分别是 JObject 和 JArray我们只需声明这两个类后 .toString 就可得到他的 json 格式了 很方便吧~ 看 代码这里完全可以不用 dynamic 的 像注释那样的写法就行 这样即使服务器不支 持.net4.0 也可以使用的知道了如何构建 POST 提交的数据 就开始实现 POST 提交吧~ 上代码 依然是三 种方式 放一起 对比
这三个方式 个人很推荐 第三种 也是目前最新的 .net4.5 里集成的 访问 REST 非常的方便 因为他支持 直接点出来 PUT 以及 DELETE 请求简直是为 rest 量身定制的~~ 看下 PUT 请求这里需要注意下 因为我们发送的是 json 不论是哪种发送请求的方式 都要记 得 设置 ContentType=application/json否则会抛出 HTTP 400 的错误 400-错误的请求 很多都是客户端请求不正确造成 的 所以一般出来 400 错误 可以重点检查下 客户端请求的问题因为 PUT 与 DELETE 和 POST 基本类似 我就不上代码了二.REST 的文件上传 与下载流文件的上传和下载先来看服务端的接口与实现 我们要实现三个功能1. 展示一张图片2.下载一个文件3.上传一个文件一个一个说 先说展示图片 实现代码这个是在浏览器中直接打开 比较简单 这里记得设置输出的 ContentType 有时 我们想打开的是别的格式 这里有个小方法 根据文件扩展名获得输出的 ContentType也就是上面用到的 GetContentType 方法 在园子的知识库里发现的 GetContentType 客户端 非常简单 直接引用连接即可2.下载上面的是直接把文件显示在浏览器中 如果我们希望下载资源呢 方法也很简单 和以前的方法是一样的 直接上代码public void GetExecl(string execlName) { string folder = System.Web.Hosting.HostingEnvironment.MapPath(&~/Files&); var FullFileName = bine(folder, execlName); if (File.Exists(FullFileName)) { FileInfo DownloadFile = new FileInfo(FullFileName); HttpContext.Current.Response.Clear(); HttpContext.Current.Response.ClearHeaders(); HttpContext.Current.Response.Buffer = HttpContext.Current.Response.ContentType = &application/octet-stream&; HttpContext.Current.Response.AppendHeader(&Content-Disposition&, &filename=& + HttpUtility.UrlEncode(DownloadFile.FullName, System.Text.Encoding.ASCII)); HttpContext.Current.Response.AppendHeader(&Content-Length&, DownloadFile.Length.ToString()); HttpContext.Current.Response.WriteFile(DownloadFile.FullName); HttpContext.Current.Response.Flush(); HttpContext.Current.Response.End(); } }3.上传 这个文件上传 才是重点要说的 折腾了好久~首先要说的是 一定要记得配置文件 如果用 REST 模版 配置方法如下&system.serviceModel& &serviceHostingEnvironment aspNetCompatibilityEnabled=&true&/& &standardEndpoints & &webHttpEndpoint & &!-Configure the WCF REST service base address via the global.asax.cs file and the default endpoint via the attributes on the &standardEndpoint& element below --& &standardEndpoint name=&& maxReceivedMessageSize=&3000000& defaultOutgoingResponseFormat=&Json& helpEnabled=&true& automaticFormatSelectionEnabled=&true&& &readerQuotas maxArrayLength=&300000&/& &/standardEndpoint& &/webHttpEndpoint& &/standardEndpoints& &/system.serviceModel&这样就可以上传大文件了客户端实现上传很简单 借助 HttpClient 本来写到这 以为上传很简单的就能完成了 结果却弄了好久先说下以前的写法 以前这样 都能成功 谁知道这次移到 REST WCF 中 竟然报错 发现不能读取 Stream.Length 在这里问下各位高手 ~为什么读取 Stream.Length 会报错?CSDN 的 fangxinggood 给了解释 非常感谢因为它被封装成了 MessageBodyStream,就像 WCF Streamed 的数据一样,Length 是不支持的。WCF 里是客户端一边传服务端一边收。到达服务端开始写 IO 时, 流并没有全部传输完,所以 MessageBodyStream 不支持 Length。但 REST 时是 Http 协议, 所以其实 Stream 已经全部到达服务端了。 如果你想取长度, 可以 用: var len = WebOperationContext.Current.IncomingRequest.ContentL 来获得。 如图最后就会抛出 Http 400 的错误百思不得其解 只好换了另一种方法这种方法不需要 长度 length 可是却发现在 转化成 byte 这次是报错 输入的不是有效的 Base-64 字符串, 因为它包含非 Base-64 字符、两个以上的填充字符,或者填充字符间包含非空 白字符。再次请问原因? 希望遇到过的朋友告知解决办法后来 通过谷歌 发现另外一种 获得长度的方法 在 WCF REST 里 这样就能用第一 种方法了把最早方法里的long ByteLength=stream.L换成long ByteLength = WebOperationContext.Current.IncomingRequest.ContentL这样就可以得到长度了 上传问题也就解决了~~这里说下 如果上传的是图片 有个很简单的方法 核心代码 简单两行即可~ 三.Session 与 Cookie在 WCF 里 是可以使用 session 的 但是在 REST 的 WCF 里 貌似却不能使用 session 查了些资料 没有发现使用 session 的办法可能是因为使用 session 会让服务器和客户端耦合。 后来找到了 REST 常见误 区 http://www.prescod.net/rest/mistakes/里面提到了一条 session 是无关紧要的。cookie 还是可以正常使用的 这里说下 客户端读取 cookie 的方法 webclient 貌似不能读到 要使用更加灵活的 HttpWebRequest 主要是这两句CookieContainer Cookie = new CookieContainer(); request.CookieContainer = C四.总结在做这方面的工作 把经验分享给大家 也希望会的人 能解答下我问中的问题~~REST WCF 体现了 ROA 面向资源编程 感觉更好了解耦了客户端与服务端 而且调 用 REST 服务 也简单了很多 没有了代理等 效率也提高了 喜欢这种风格的方 式参考:http://blog.csdn.net/fangxinggood/article/details/6235662原文:/wlflovenet/archive//WCFREST2.html本文链接WCF REST 工作总结(一) 2 年1 月2 日 ? ?011? ?0? ?9? ,?0:17:00 | wlf 1 最近工作中学习使用了 WCF REST,REST 有很多好处 高效 简约 面向资源 而客 户端调用 也变得非常简单。 REST 入门的资料等 大家可以去网上找 这里主要分 享下遇到的问题以及解决~一.环境准备使用 WCF REST 可以先下载一个模版 并大致预览下载好这个模版后 新建项目 选择这个模版 会默认帮我们生成一个 CRUD 的代 码 新手朋友可以用这个快速熟悉下 REST 风格的 WCF. 这这里还给我们提供了一个很方便的功能 help 只需在我们的服务后面 加上 help 就能显示服务的详情与介绍了
二.搭建 REST WCF 服务1.接口的实现 当我们的请求为 Get 时 一般用来获取数据时 使用[WebGet] 而 增加 修改 删 除 则使用[WebInvoke] 里面的 Method 来决定使用 POST 或 PUT 等 这里要注意 下 Method 里的一定要为大写 POST PUT 等 否则会出现 HTTP 错误 405 405 不允 许此方法 对于请求所标识的资源,不允许使用请求行中所指定的方法。 我们在这里 还可以通过 ResponseFormat 与 RequestFormat 来指定接受的参 数与返回的参数格式2.类继承接口 这里先贴一部分 最基础的 CRUD
3. Global 里注册路由用过 mvc 的朋友 是不是发现这样写完后 非常的像 mvc 呢~三.实现调用服务1.实现 get 服务很简单 我们可以直接通过浏览器 输入 http://localhost:55914/ITaskService/Tasks/State/1 即可获得 state 为 1 的所有数据 以 json 格式传输过来 rest 风格的 wcf 使得我们不用再像以前一样 客户端 添加 服务引用了 没有了 wsdl 简洁了许多 效率也提高了很多 使得我们访问更容易 REST 由于没有了代理 而是发送请求 我们的客户端可以是 js 访问 如果是 jquery 只需$.get 即可发送请求 这里我主要说下 C#的访问方式 通过 WebClient 方式 这个类封装的比较好 调用起来简单 但由于封装的过多 实现一些功能时则不太容易 通过 WebRequest 方式 这个则比较灵活 上面两个是经常用到的两个类 这里再推荐给大家一个 是 wcf 工具里的其中一 个 专门用来访问 rest 服务 也非常的简单 通过 HttpClient 这个也是微软开发的工具类 而且在最新的 .net 4.5 里 集成 了进去 通过上面三个任意其中一个就可以实现提交了 但是 这里有一个问题 我们以前有代理类 通过调用方法 得到代理类 可是现在 呢 我们没有类 我们得到的是一串 json 格式的数据 我们怎么把他变成像类一 样的 方便操作呢? 比如 A 公司发布了 rest wcf 服务 数据格式 json 公司 B 要调用 A 公司的服务 以前 ws 或 wcf 都有代理 可以生成代理类 现在却得到的是 json 数据 而我们还 没有要反序列化的实体类 比如 我现在想把得到的数据 像表格一样的展示出来 有两种方法 1.对照 json 数据 客户端新建个类 然后通过反序列化 把 json 反序列化为类对 象 这样比较麻烦 类少还可以 要是多了 则需要新建很多实体类 更关键的是 服务 端的实体类 更新了 以前 WCF 或 ws 更新下服务即可 而现在 还要更新客户端实 体类岂不是很麻烦? 所以推荐用下面的方法 2.将 json 反序列话为 dynamic 类型 json 转换为 dynamic 类型 起初 我用的是 第三方的 json.net 经过尝试 发 现再把 json 转换为集合类型时 总是出错 如果有朋友知道怎么弄 还望告知下 后来经过尝试 我采用了 微软自带的 JavaScriptSerializer 下面展示代码 使用方法很简单 经过 不论是序列化 集合 还是单个的 都没有问题 这个时间 也会正常显示 没有问题~ 结果上面的代码 就可以把接受的 json 类型 展示为 一个 table 了 好了 这个问题解决了 可以上三种方式发送 get 请求了 这个毕竟简单 大家直 接看代码 可以看到 第一个 和第三个 都是非常简单的 这里要注意的是 要注意编码问题 因为传递的有中文 会有乱码问题 所以要注意编码问题 再第二个里 可以通过得到 response.ContentType 先得 到编码 这样就可以自动识别编码格式了 再说下 HttpClient 这个真正发送请求 是在 message.EnsureStatusIsSuccessful(); 这句话 通过上面任意一种方法 我们都可以得到 json 这样再通过将 json 反序列话为 dynamic 类型 就可以像有实体类一样简单的操作了~~ 今天先写到这 下面讲 post 等提交数据 以及 原生流文件的上传 下载 等 最后 祝大家周末愉快 欢迎一起交流技术 参考: http://blog.csdn.net/fangxinggood/article/details/6235662 原文: /wlflovenet/archive//WCFREST.html本文链接MVC3+EF4.1 学习系列(十)----MVC+EF 处理树形结构2 年9月5日 ? ?011? ?? ?? ,?:27:00 | wlf 9 文章索引和简介通过前几篇文章 我们处理了 一对一, 一对多,多对多关系 很好的发挥了 ORM 框架的做用 但是 少说了一种 树形结构的处理, 而这种树形关系 我们也经常 遇到,常见的 N 级类别的处理, 以及经常有数据与类别挂钩。今天主要写下 EF 处理树形结构以及 MVC 如何展示树形结构。 前面几篇的例子 一直用的是一个 例子,内容是连贯的。这篇是完全单独的~先来说下工作中会遇到的常见场景 针对这几个场景来处理~1.类别a.类别可以有无限级别 b.类别的最末端 不确定是第几级 某个节点 可以到二级 其他的节点 有可能到 四级c.tree 型展示整个类别 并可以对 tree 进行 CRUD (可以一次递归全部加载 也 可以异步加载 )d.面包屑型展示类别e.删除父类 应把下面所有的子类删除2.与类别挂钩的数据 (本文是文章)a. 可以根据任意级别的类别 查看文章b. 合并两个类别的文章上面这些场景 基本覆盖了类别操作的常见情况 如果大家觉得还有什么要处理 可以给我说 我补充上去~~下面开始讲解~一.准备工作 1.如何建立类别实体类 来展示树形结构上代码/// &summary& /// 类别 /// &/summary& public class Category { /// &summary& /// 主键 /// &/summary& public int CategoryId { } /// &summary& /// 类别名字 /// &/summary& [Required()] [StringLength(5)] public string CategoryName { } /// &summary& /// 父 ID /// &/summary& public Nullable&int& ParentId { } /// &summary& /// 上面的父节点 /// &/summary& public virtual Category Parent { } /// &summary& /// 下面的子节点 /// &/summary& [ForeignKey(&ParentId&)] public virtual ICollection&Category& ChildKeys { } /// &summary& /// 该类别的文章集合 /// &/summary& public virtual ICollection&Article& articleList { } /// &summary& /// 编号 /// &/summary& public string Note { }/// &summary& /// 状态 /// &/summary& public string State { } /// &summary& /// 级别 /// &/summary& public Nullable&int& Lev { } /// &summary& /// 排序 /// &/summary& public int Sort { } }这样的设计 很好的展示了树形结构 一个节点有一个父类 多个子类 一个类别 可以有多个文章 这里说下 后面的四个属性 不是必要的~ 2.文章实体类上代码这个比较好理解 不解释了~/// &summary& /// 文章 /// &/summary& public class Article { /// &summary& /// 文章 ID /// &/summary& public int ArticleId { } /// &summary& /// 文章名 /// &/summary& public string ArticleName { } /// &summary& /// 创建时间 /// &/summary& public DateTime CreateTime { } /// &summary& /// 文章顺序 /// &/summary& public int Sort { } /// &summary& /// 类别 ID /// &/summary& public int CategoryId { } /// &summary& /// 类别 /// &/summary& [ForeignKey(&CategoryId&)] public Category Category { } }3.建立 Context上代码public class TreeDemoContext : DbContext { private readonly static string CONNECTION_STRING = &name=WlfSys_EFCF_ConnString&; public DbSet&Category& Category { } public DbSet&Article& Article { } public TreeDemoContext() : base(CONNECTION_STRING) { // this.Configuration.ProxyCreationEnabled = }protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove&PluralizingTableNameConvention&();// 移除复数表名的契约 } } 这里不需要使用 Fluent API 来映射实体类与数据库的关系 里面也没什么亮点 ( 其实我一直想知道 怎么用 Fluent API 映射 来解决下面的问题 有知道的高 人指点下~~ 感激 )4.数据库初始化这是我这种方法 用 ef 处理树形结构最关键的一点 熟练使用 ef 的人 看了我上 面的类别实体类的建立 就会发现这是错误的 因为这会造成自引用 看下图生成 的数据库表结构由于自引用 插入时会出现 INSERT 语句与 FOREIGN KEY SAME TABLE 约束 &Category_ChildKeys&冲突。我的解决办法是 在初始化数据库时 删除这个外键约束 上代码 public class TreeDemoInitializer : DropCreateDatabaseIfModelChanges&TreeDemoContext& { protected override void Seed(TreeDemoContext context) { //删除关联 context.Database.ExecuteSqlCommand(&ALTER TABLE [dbo].[Category] DROP CONSTRAINT [Category_ChildKeys]&);//必须加上 ID var Category = new List&Category& { new Category{ CategoryId=1, CategoryName=&亚洲&, Lev=1, ParentId=0, Note=&001&,ChildKeys=new List&Category&{ new Category{CategoryId=2,CategoryName=&中国 &,Lev=2,Note=&00101&,ChildKeys=new List&Category&{ new Category{CategoryId=6, CategoryName=&河南&, Lev=3, Note=&0010101& },new Category{CategoryId=7, CategoryName=&广州&, Lev=3, Note=&0010102& } }}, new Category{CategoryId=3,CategoryName=&日本 &,Lev=2,Note=&00102&,ChildKeys=new List&Category&{ new Category{CategoryId=8, CategoryName=&日本省 1&, Lev=3, Note=&0010201& },new Category{CategoryId=9, CategoryName=&日本省 2&, Lev=3, Note=&0010202& } } } } }, new Category { CategoryId=4, CategoryName=&欧洲&, Lev=1, ParentId=0, Note=&002&, ChildKeys=new List&Category&{ new Category{CategoryId=5,CategoryName=&荷兰 &,Lev=2,Note=&00201&} } } }; Category.ForEach(c =& context.Category.Add(c));var Articles = new List&Article&{ new Article{ ArticleName=&小说 13&, CreateTime=DateTime.Now, CategoryId=5}, new Article{ ArticleName=&小说 14&, CreateTime=DateTime.Now, CategoryId=5}, new Article{ ArticleName=&小说 15&, CreateTime=DateTime.Now, CategoryId=5}, new Article{ ArticleName=&小说 1&, CreateTime=DateTime.Now, CategoryId=6}, new Article{ ArticleName=&小说 2&, CreateTime=DateTime.Now, CategoryId=6}, new Article{ ArticleName=&小说 3&, CreateTime=DateTime.Now, CategoryId=6}, new Article{ ArticleName=&小说 4&, CreateTime=DateTime.Now, CategoryId=7}, new Article{ ArticleName=&小说 5&, CreateTime=DateTime.Now, CategoryId=7}, new Article{ ArticleName=&小说 6&, CreateTime=DateTime.Now, CategoryId=7}, new Article{ ArticleName=&小说 7&, CreateTime=DateTime.Now, CategoryId=8}, new Article{ ArticleName=&小说 8&, CreateTime=DateTime.Now, CategoryId=8}, new Article{ ArticleName=&小说 9&, CreateTime=DateTime.Now, CategoryId=8}, new Article{ ArticleName=&小说 10&, CreateTime=DateTime.Now, CategoryId=9}, new Article{ ArticleName=&小说 11&, CreateTime=DateTime.Now, CategoryId=9}, new Article{ ArticleName=&小说 12&, CreateTime=DateTime.Now, CategoryId=9} }; Articles.ForEach(a =& context.Article.Add(a)); context.SaveChanges(); } }并初始化一些数据进去这个初始化 就算是类别的添加了 在这添加时 遇到个小问题 我们的数据库类 别 ID 默认是自增长的 按理说不用指定主键 ID 但是不指定 ID 像我上面 一下次 插入多条时 插入时却报错 ~~ 无法确定“ContosoUniversity.DAL.Category_ChildKeys”关系的主体端。 添加 的多个实体可能主键相同。指定了 ID 才解决了这个问题5. 搭建基本项目结构依然使用 unit of work +Repository ( 项目大的话 加入 Iservice, Service 再 加上 IOC,这里只是个简单的 demo) 如图二.关于类别的操作以及展示1.tree 型展示整个类别 在 webfrom 时代 实现 tree 展示很容易 因为我们有犀利的控件 treeview 用 treeview 控件 再加个递归绑定 就很简单的完成了 这是 webfrom 的好处 但也 是不好的地方 比如 treeview 生成出来的 是 table 嵌套 table 的 我如果想换成 ul li 怎么办 控件开发 造成了耦合度过 高 题外话说多了 回归正题~这里 我用两种方法实现 treeview 的展示A 方法 扩展一个 HtmlHelper实现 HTML.Tree(类别) 就能展示出 treeview用这个方法前 再说几句 不喜欢这个方法 因为这有点像用控件了 代码与视图 依然在一起 第二 这里我使用了递归, 但是小项目的话 没什么问题 直接上 codepublic static MvcHtmlString Tree(this HtmlHelper html, Category treeModel) { return BindTree(treeModel); } private static MvcHtmlString BindTree(Category treeModel) { StringBuilder sb = new StringBuilder(); if (treeModel != null) { sb.Append(&&ul&&); List&Category& list = treeModel.ChildKeys.ToList(); foreach (var item in list) { sb.Append(&&li&&); sb.Append(item.CategoryName); sb.Append(&&/li&&); sb.Append(BindTree(item)); } sb.Append(&&/ul&&); } MvcHtmlString mstr = new MvcHtmlString(sb.ToString()); }上面实现了最最简单的展示树形结构 无非就是递归的运用 这个可以扩展 是否 展示 checkbox 啊 上来默认展示几级啊 后面的增删改连接啊 and so on~~b. 利用 ajax 实现异步加载 ( 个人喜欢的方法 ) 先上一个实现后的图我没做任何美工 样子很难看~ 大家将就看下前面有小箭头表示可以打开~~ 打开后 变成打开的状态~下面上视图 解释和思路 直接加在里面了 &script type=&text/javascript&& $( function () { var clickLi = function () { $(this).children(&ul&).toggle(); // 切换隐藏和显示 li 下面 的 ul//切换 img 图标是 选中还是未选中 if ($(this).children(&img&).attr(&src&) == &/Content/img/selectNode.jpg&) { $(this).children(&img&).attr(&src&, &/Content/img/noselectNode.jpg&); } else { $(this).children(&img&).attr(&src&, &/Content/img/selectNode.jpg&); }//什么时候发送加载下面节点的请求呢? //在 img 属性不为空 证明下面有节点 因为没有节点是不会有 img 树形的 并且他的下面的 ul 个数为 0 if ($(this).children(&img&).attr(&src&) != undefined && $(this).children(&ul&).length == 0) { var cid = $(this).attr(&id&); var li = $(this); $.post(&GetCategoryById&, { id: cid }, function (data) { if (data == &-1&) { alert(&失败&); } else { li.append(data); } }); } } //为什么用 live 不是直接 click? //因为 ajax 请求加载的 ~~切记 $(&#CategoryTree li&).live(&click&, null, clickLi); } ); &/script& &h2&Index&/h2& &p& @Html.ActionLink(&Create New&, &Create&) &/p& @*@Html.Tree(Model);*@ &ul id=&CategoryTree&& @foreach (var item in Model.ChildKeys) { &li id=&@item.CategoryId&& @if(item.ChildKeys.Count & 0) { &img src=&/Content/img/noselectNode.jpg& /& } &span class=&name&&@item.CategoryName&/span& @Html.ActionLink(&添加&, &Create&, new { id=@item.CategoryId}) @Html.ActionLink(&修改&, &Edit&, new { id=@item.CategoryId}) &/li& } &/ul& click 事件是不管用的 要用 live 才可以这里说下 jquery ajax 请求时 我们经常返回 json 然后来构建 这里说下 mvc 另 一种方法 返回一个部分视图~~ 我很喜欢这种方法 上 code~ 不解释啦~ [HttpPost] public ActionResult GetCategoryById(int id) { if (Request.IsAjaxRequest()) { Category CategoryModel = unitOfWOrk.CategoryRepository.GetTEntityByID(id); if (CategoryModel != null) { return PartialView(CategoryModel); } else { return Content(&-1&); } } else { return Content(&-1&); } }@model TreeDemoCore.Model.Category &ul& @foreach (var childitem in Model.ChildKeys) { &li id=@childitem.CategoryId& @if (childitem.ChildKeys.Count & 0) { &img src=&/Content/img/noselectNode.jpg& /& } &span class=&name&&@childitem.CategoryName &/span& @Html.ActionLink(&添加&, &Create&) @Html.ActionLink(&修改&, &Edit&) &/li& } &/ul&2.展示面包屑树形结构的展示 我们经常遇到 treeview 型的 还会遇到另一种 面包屑这样的 如我们这里要做的就是 根据当前类别 向上一层层推到最上面 并把最后一个加粗 显示~ 我的思路是这样的 比如当前所在的类别 为最小的 广州 根据这个 一层 层推到最上面 通过递归得到 广州&中国&亚洲 在通过反转字符串 并给加粗就行了~~ 代码如下/// &summary& /// 根据当前类别建造面包屑 /// &/summary& /// &returns&&/returns& public static MvcHtmlString Menu(this HtmlHelper html, Category treeModel) { return new MvcHtmlString(MenuReverse(BindMenu(treeModel))); } /// &summary& /// 递归调用 得到 广州&中国&亚洲 /// &/summary& /// &returns&&/returns& private static string BindMenu(Category Model) { StringBuilder sb = new StringBuilder(); sb.Append(Model.CategoryName); if (Model.Parent != null) { sb.Append(&&&); sb.Append(BindMenu(Model.Parent)); } return sb.ToString(); } /// &summary& /// 反转字符串 并给最后一个加上黑体字标签 /// &/summary& /// &returns&&/returns& private static string MenuReverse(string menu) { return string.Join(&&&, menu.Split('&').Select((s, i) =& i == 0 ? string.Format(&&strong&{0}&/Strong&&, s) : s).Reverse().ToArray()); }3.删除父类要把下面的子类全部删除这里就涉及到一个树形结构的重要方法 通过当前类 得到该类的所有子类子子 类等的 ID 集合 这时删除时用 delete in(子类集合 ) 就行了 ~~ 忘了 怎么直 接执行 SQL 语句的~~ 去看上一篇文章.. 通过现在类 获得下面子类集合的方法 如下/// &summary& /// 获得父类下所有子类的集合 /// &/summary& /// &returns&&/returns& private List&int& GetCidbyPid(int pid) { List&int& cidList = new List&int&(); Category CategoryModel = unitOfWork.CategoryRepository.GetTEntityByID(pid); foreach (var item in CategoryModel.ChildKeys) { cidList.Add(item.CategoryId); } foreach (var item in CategoryModel.ChildKeys) { cidList.AddRange(GetCidbyPid(item.CategoryId)); } return cidL // 获得 1,2,3,4,5 } tring strcid= string.Join(&,&, cidList);依然是通过递归 获得所有的 子节点集合 再通过 string.Join(&,&, cidList) 得到 delete in ( ) 里需要的的 就行了~~三.与类别挂钩的数据的展示一. 可以根据任意级别的类别查看文章依然提供两个方法~ 1.根据类别 查询文章 利用递归不过这个效率就太纠结了~~/// &summary& /// 通过任意类别 获得下面的全部文章 /// &/summary& /// &param name=&cid&&&/param& /// &returns&&/returns& private List&Article& GetArticleByCid(int cid) { List&Article& ArticleList=new List&Article&(); Category CategoryModel=unitOfWork.CategoryRepository.GetTEntityByID(cid); if (CategoryModel.ChildKeys.Count == 0) { ArticleList.AddRange(CategoryModel.articleList.ToList()); } foreach (var item in CategoryModel.ChildKeys) { ArticleList.AddRange(GetArticleByCid(item.CategoryId)); } return ArticleL }2. 利用前面说到的 通过当前类 得到该类的所有子类子子类等的 ID 集合 再查 询的时候 IN 就行了这里有个小知识~ 用 linq 执行 sql in 的操作~ 用 Contains 就行了 代码如下public ActionResult Index() { //准备测试数据 测试不同情况 GetCidbyPid 为根据 ID 获得所 有子类以及子子类等的集合 //测试最高级 List&int& CidList = GetCidbyPid(1); // var CidList = GetCidbyPid(2); //测试最低级 //var CidList = GetCidbyPid(6); // 贪婪加载类别 为了显示类别名字~ var ArticleList = unitOfWork.ArticleRepository.Get(c =& CidList.Contains(c.CategoryId), includeProperties: &Category&); return View(ArticleList); }二.合并两个类别的文章这个很简单啦~ 合并两个类 就是把一个类下的文章 id 都变成另一个 也就是 说批量操作 不要用 EF 的一个个更新就行 太慢了~还要发送多条更新语句用 context.Database.SqlQuery 直接执行 update 这个就不写代码啦~四.通过第三方工具 Telerik 更加酷炫的 展示 tree我上面写的东西 都是小打小闹 自娱自乐的玩下 有很多地方不完善 不合理,而 MVC 实现 Tree 第三方工具已经有了帮我们做的非常优秀的了 这里给大家推荐个开源的 是 Telerik 的 tree------介绍与连接把该有的操作基本全部都封装在了里面啦~非常方便 而且里面还有很多其他的 控件~还有,推荐大家看看源码 第三方工具一定要多看看实现 不要只会用五.总结这篇是完全独立的 和前面几篇没什么关系~ 代码贴的都是核心片段~ 聪明的大 家看看就能明白了 而且应该有更好的实现希望大家分享下 EF MVC 处理 tree 的经验以及遇到的问题~~我也提个问题 因为 tree 结构的操作 很多都用到了递归 在 EF 高并发 大数据 量处理时 我觉得会出现一些问题 希望大家说说怎么解决总体内容没有太多难度~ 大家自己敲敲练练吧实在太懒的同学 留个邮件 我把 demo 发给你们~(ps:文章写在自己的 22 岁生日, 学习.net3 年多了 希望自己通过不断的学习 再次进步~ 也希望在园子里 得到更多的人的指点与帮助 祝福自己! ) 本文链接MVC3+EF4.1 学习系列(九)-----EF4.1 其他的一些技 巧的使用2 年8月8日 ? ?011? ?? ?? ,?:42:00 | wlf 8 文章索引和简介上节通过一系列重构 简单的项目就实现了 不过还有些 EF 的功能没有讲 这节 就通过项目 讲讲 EF 其他的功能与技巧一.直接执行 SQL 语句通常来讲 EF 不用写 SQL 语句的 但是 在有些场合 比如对生成的 SQL 语句 觉得 不满意 要做优化 或者做报表统计时 要写很变态的 SQL 语句再或者 批量操作等

我要回帖

更多关于 graphic instructor 的文章

 

随机推荐