《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑
翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇
4.2. 构建一个搜索查询
搜索数据是几乎所有应用的一个基本功能。它一般是动态的,因为用户可能使用提供的任何一个条件,或者一个也不使用。所以,我们在下面讨论实现这个基本功能的一些细节。
问题
你想在ASP.NET MVC4中使用实体框架构建一搜索页面。
解决方案
假设你有如图4-14所示的模型,在这个解决方案中,我们打算使用三个基本的部分来构建搜索页:
1、一张用于构造查询参数的表;
2、一个用于在Razor视图中呈现结果的WebGrid;
3、一个包含视图逻辑的Cotroller;
在数据库中,你有一张Customer表,它存储所有客户的Name,City和State信息。Customer表的数据模型(架构)如图4-14所示。
图4-14 一个包含Customer 实体的模型
在Customer视图模型被创建之后,我们需要使用Razor来编写视图。在这个视图中,我们使用WebGrid控件来显示Customer记录。如代码清单4-5所示。
代码清单4-5. 在MVC Razor视图中使用WebGrid
- @model EntityFrameworkRecipe2.ViewModels.CustomerVM
- @{
- Layout = null;
- }
- @{
- ViewBag.Title = "Customer";
- WebGrid grid = new WebGrid(canPage:false,canSort:false);
- grid.Bind(Model.Customers,
- autoSortAndPage: false
- );
- }
- @using (Html.BeginForm())
- {
- <table>
- <tr>
- <td>
- Name
- </td>
- <td>
- @Html.TextBoxFor(model => model.Name)
- </td>
- </tr>
- <tr>
- <td>
- City
- </td>
- <td>
- @Html.TextBoxFor(model => model.City)
- </td>
- </tr>
- <tr>
- <td>
- State
- </td>
- <td>
- @Html.TextBoxFor(model => model.State)
- </td>
- </tr>
- <tr>
- <td colspan="2">
- <input type="submit" id="search" title="Search" value="Search" />
- </td>
- </tr>
- </table>
- <div id="searchResults">
- <!-- placeHolder for search results -->
- @grid.GetHtml(
- fillEmptyRows: true,
- alternatingRowStyle: "alternate-row",
- headerStyle: "grid-header",
- footerStyle: "grid-footer",
- columns: new [] {
- grid.Column("Name"),
- grid.Column("City"),
- grid.Column("State")
- })
- </div>
- }
一旦视图编写好,我就开始在Controller中编写支持Get和Post方式的查询功能。我们打算将从数据库中获取的Customer数据放到 view modle中,实现如代码清单4-6所示.
代码清单4-6. 获取数据代搜索页测试
- public class CustomerController : Controller
- {
- public ActionResult Search()
- {
- using (var db = new CustomerEntities())
- {
- var customer = db.Customers.ToList();
- var data = new CustomerVM()
- {
- Customers = customer
- };
- return View(data);
- }
- }
- [HttpPost]
- public ActionResult Search(CustomerVM customerVmValue)
- {
- using (var db = new CustomerEntities())
- {
- var customerSearchResults = from customerRec in db.Customers
- where ((customerVmValue.Name == null) || (customerRec.Name == customerVmValue.Name.Trim()))
- && ((customerVmValue.City == null) || (customerRec.City == customerVmValue.City.Trim()))
- && ((customerVmValue.State == null) || (customerRec.State == customerVmValue.State.Trim()))
- select new
- {
- Name = customerRec.Name
- ,
- City = customerRec.City
- ,
- State = customerRec.State
- };
- List<Customer> lstCustomer = new List<Customer>();
- foreach (var record in customerSearchResults)
- {
- Customer customerValue = new Customer();
- customerValue.Name = record.Name;
- customerValue.City = record.City;
- customerValue.State = record.State;
- lstCustomer.Add(customerValue);
- }
- customerVmValue.Customers = lstCustomer;
- return View(customerVmValue);
- }
- }
- }
你在你的浏览器中,页面呈现如图4-15所示:
图4-15 浏览器中呈现的视图
原理
在页面的第一小节中(请看代码清单4-5),我们使用table来格式化查询字段,这里没有花俏的技术,我们只是提供了获取三个查询字段:Name,City和State的结构。他们的值(或者没有),将用于查询按钮被点击了之后Controller的查询(Search)动作方法.因此,这些参数会成为查询的过虑条件。
接下来,我们使用HtmlHelper在WebGrid控件中显示结果集。数据源使用view model。这里需要注意,我们创建了两个模型,一个用于从数据库中获取数据,一个用于视图页收集查询参数,和呈现结果集。
我们使用Linq to entities来查询数据模型中的Customer实体,where从句和参数确定了我们的查询过滤。在视图中,我们将查询参数Name,City和State映射到 HtmlHelper中的文本框。将Name属性映射到文件框Name,依次类推。
我们使用控件WebGrid来显示结果集,它被绑定到CustomerVM.Customers列表,它是专门用来获取查询结果集的。
代码清单4-6中所展示的controller代码,从数据库中获取数据,并在第一次加载视图和点击查询按钮后填充视图。我们使用了本地数据库.mdf文件并在Customer表中填充了记录。
4.3. 使用ASP.NET的URL路由过虑
问题
你想使用MapRoute来简化你的URLs,同时你想凭借这位路由在Razor视图引擎中过滤结果集。
解决方案
假设你有如图4-16的模型,我们已经对产品以及它们的目录建模,用实体类型Product来表示产品。在一个典型的电商网站中,我们需要按目录对产品进行分类。我们要避免在URLs中暴露类似 "/Product/Index?Category=Tents"这样的查询字符串。虽然这样会使编程简单一些,但无益于搜索引擎优化。我们需要看起像 “/Producs/Tents"这样的URLs.
图4-6 一个包含products和它们的categories
我们能通过路由得到对搜索引擎友好的URL,路由一般在Global.asax的Application_Start()事件处理函数中创建。代码清单4-7演示了为Product控制器添加一条路由。
代码清单4-7. 在Global.asax中创建一条路由
- protected void Application_Start()
- {
- AreaRegistration.RegisterAllAreas();
- WebApiConfig.Register(GlobalConfiguration.Configuration);
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- RouteTable.Routes.MapRoute("Product", "{controller}/{name}",
- new { controller = "Product", action = "Index" }
- );
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- BundleConfig.RegisterBundles(BundleTable.Bundles);
- AuthConfig.RegisterAuth();
- }
在代码清单4-8所示的Index视图中,我们使用category name绑定到Product controller中的Inde动作方法的name参数,如代码清单4-7所示。我们使用代码清单4-9中的代码获取category name参数的值,并通过视图处理结果集。图4-17和图4-18分别展示了呈现Tents目录下产品的视图页面,和Cooking Equipment目录下产品的视图页面。
代码清单4-8. Index视图代码,它显示通过目录过滤后的产品
- @model IEnumerable<EntityFrameworkRecipe3.ViewModels.ProductVM>
- @{
- Layout = null;
- }
- <!DOCTYPE html>
- <html>
- <head>
- <meta name="viewport" content="width=device-width" />
- <title>Index</title>
- </head>
- <body>
- <table>
- <tr>
- <th>
- @Html.DisplayNameFor(model => model.Name)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.CategoryName)
- </th>
- <th></th>
- </tr>
- @foreach (var item in Model) {
- <tr>
- <td>
- @Html.DisplayFor(modelItem => item.Name)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.CategoryName)
- </td>
- </tr>
- }
- </table>
- </body>
- </html>
代码清单4-9. Controller代码,使用通过Category Name过滤后的Product数据填充模型
- public class ProductController : Controller
- {
- //
- // GET: /Product/
- public ActionResult Index(string name)
- {
- using (var db = new ProductEntities())
- {
- var query = from productRec in db.Products
- join categoryRec in db.Categories
- on productRec.CategoryId
- equals categoryRec.CategoryId
- where categoryRec.Name == name
- select new
- {
- Name = productRec.Name
- ,
- CategoryName = categoryRec.Name
- };
- List<ProductVM> lstProduct = new List<ProductVM>();
- foreach(var record in query)
- {
- ProductVM productValue = new ProductVM();
- productValue.Name = record.Name;
- productValue.CategoryName = record.CategoryName;
- lstProduct.Add(productValue);
- }
- return View(lstProduct);
- }
- }
- }
图4-17 使用路径“/Product/Tents",得到Tents目录类别的产品集
图4-18 使用路径“/Product/cooking Equipment"得到cooking Equipment目录类别的产品集
原理
在Global.asax中 Applicate_Start()事件处理函数中,我们为之前的"/Product/Index?name=category"映射了路由 "/Product/{name}".在Product controller中,我们使用MapRoute中的路由key,name,过滤结果集,通过给定的目录过滤产品。
至此,第四章结束,感谢你的阅读。下一篇,我们将进入第五章的学习。
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述
微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...
- 《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型
第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以 ...
- 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍
Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...
- 《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型
不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创 ...
- 《Entity Framework 6 Recipes》翻译系列 (5) -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模
2-3 无载荷(with NO Payload)的多对多关系建模 问题 在数据库中,存在通过一张链接表来关联两张表的情况.链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11 测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (41) ------ 第七章 使用对象服务之标识关系中使用依赖实体与异步查询保存
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-7 标识关系中使用依赖实体 问题 你想在标识关系中插入,更新和删除一个依赖实体 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (11) -----第三章 查询之异步查询
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...
随机推荐
- js获取css中的样式
众所周知,obj.style只能够获取 <div id="a" style="width:100px;"></div> 结构上的样式 如 ...
- HTML5开发笔记:图片上传预览
我们知道通过<input type="file">可以用来进行一个图片或者文件的上传,然而浏览器自带的一个缩略图预览的功能其实是相当不美观的,很多时候我们希望可以在上传 ...
- 二分+DP+Trie HDOJ 5715 XOR 游戏
题目链接 XOR 游戏 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- byobu相关操作
http://lingbjxm.iteye.com/blog/2155833 重命名窗口:Fn F8
- 我的前端故事----优美的编辑器GitHub Atom
很多前端的同学都在用sublime text,我之前也在使用,但是后来接触到了Atom,就被它的高颜值深深的吸引了~~不愧是GitHub的工程师哦~审美就是高 Atom 作为一个跨平台的编辑软件,安 ...
- JS获取当前时间戳的方法
JavaScript 获取当前时间戳:第一种方法: var timestamp = Date.parse(new Date()); 结果:1280977330000第二种方法: var timesta ...
- 利用浏览器LocalStorage缓存图片,视频文件
文章路径:https://hacks.mozilla.org/2012/02/saving-images-and-files-in-localstorage/
- sass
本文来自阮一峰http://www.ruanyifeng.com/blog/2012/06/sass.html 学过CSS的人都知道,它不是一种编程语言. 你可以用它开发网页样式,但是没法用它编程.也 ...
- AJax登录。。转
AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. AJAX 是一种用于创建快速 ...
- (学)解决诡异的 Exception type: SocketException 127.0.0.1:80
许久不发博了,老杨听完故事让我持续写一下“十万个为什么” 一.背景: 昨天我们亲密的战友HH刘老板亲临现场,指出我们协用的一个项目,客户方面反馈手持终端系统不定期“卡死”,要我们安排人飞到广州驻场解 ...