zoukankan      html  css  js  c++  java
  • 《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

     1 @model EntityFrameworkRecipe2.ViewModels.CustomerVM
     2 
     3 @{
     4     Layout = null;
     5 }
     6 
     7 @{
     8     ViewBag.Title = "Customer";
     9     WebGrid grid = new WebGrid(canPage:false,canSort:false);
    10     grid.Bind(Model.Customers, 
    11               autoSortAndPage: false
    12     ); 
    13 }
    14 
    15 @using (Html.BeginForm())
    16 {
    17 <table>
    18     <tr>
    19         <td>
    20             Name
    21         </td>    
    22         <td>
    23             @Html.TextBoxFor(model => model.Name)
    24         </td>
    25     </tr>
    26     <tr>
    27         <td>
    28             City
    29         </td>
    30         <td>
    31             @Html.TextBoxFor(model => model.City)
    32         </td>
    33     </tr>
    34     <tr>
    35         <td>
    36             State
    37         </td>
    38         <td>
    39             @Html.TextBoxFor(model => model.State)
    40         </td>
    41     </tr>
    42     <tr>            
    43         <td colspan="2">
    44             <input type="submit" id="search" title="Search" value="Search" />
    45         </td>
    46     </tr>
    47 </table>
    48 <div id="searchResults">
    49         <!-- placeHolder for search results -->
    50         @grid.GetHtml(
    51             fillEmptyRows: true,
    52             alternatingRowStyle: "alternate-row",
    53             headerStyle: "grid-header",
    54             footerStyle: "grid-footer",
    55             columns: new [] {
    56                 grid.Column("Name"),
    57                 grid.Column("City"),
    58                 grid.Column("State")
    59         })
    60 </div>
    61 }

      一旦视图编写好,我就开始在Controller中编写支持Get和Post方式的查询功能。我们打算将从数据库中获取的Customer数据放到 view modle中,实现如代码清单4-6所示.

    代码清单4-6. 获取数据代搜索页测试

     1  public class CustomerController : Controller
     2     {
     3         public ActionResult Search()
     4         {
     5             using (var db = new CustomerEntities())
     6             {
     7                 var customer = db.Customers.ToList();
     8                 var data = new CustomerVM()
     9                 {
    10                     Customers = customer
    11                 };
    12                 return View(data);
    13             }
    14         }
    15         [HttpPost]
    16         public ActionResult Search(CustomerVM customerVmValue)
    17         {
    18             using (var db = new CustomerEntities())
    19             {
    20                 var customerSearchResults = from customerRec in db.Customers
    21                                              where ((customerVmValue.Name == null) || (customerRec.Name == customerVmValue.Name.Trim()))
    22                                              && ((customerVmValue.City == null) || (customerRec.City == customerVmValue.City.Trim()))
    23                                              && ((customerVmValue.State == null) || (customerRec.State == customerVmValue.State.Trim()))
    24                                              select new
    25                                              {
    26                                                  Name = customerRec.Name
    27                                                  ,
    28                                                  City = customerRec.City
    29                                                  ,
    30                                                  State = customerRec.State
    31                                              };
    32                 List<Customer> lstCustomer = new  List<Customer>();
    33                 foreach (var record in customerSearchResults)
    34                 {
    35                     Customer customerValue = new Customer();
    36                     customerValue.Name = record.Name;
    37                     customerValue.City = record.City;
    38                     customerValue.State = record.State;
    39                     lstCustomer.Add(customerValue);
    40                 }
    41                 customerVmValue.Customers = lstCustomer;
    42                 return View(customerVmValue);
    43             }
    44         }
    45     }

       你在你的浏览器中,页面呈现如图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中创建一条路由

     1 protected void Application_Start()
     2         {
     3             AreaRegistration.RegisterAllAreas();
     4 
     5             WebApiConfig.Register(GlobalConfiguration.Configuration);
     6             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
     7             RouteTable.Routes.MapRoute("Product", "{controller}/{name}",
     8                                 new { controller = "Product", action = "Index" }
     9                             );
    10             RouteConfig.RegisterRoutes(RouteTable.Routes);
    11             BundleConfig.RegisterBundles(BundleTable.Bundles);
    12             AuthConfig.RegisterAuth();
    13         }

      在代码清单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视图代码,它显示通过目录过滤后的产品

     1 @model IEnumerable<EntityFrameworkRecipe3.ViewModels.ProductVM>
     2 
     3 @{
     4     Layout = null;
     5 }
     6 
     7 <!DOCTYPE html>
     8 
     9 <html>
    10 <head>
    11     <meta name="viewport" content="width=device-width" />
    12     <title>Index</title>
    13 </head>
    14 <body>
    15     <table>
    16         <tr>
    17             <th>
    18                 @Html.DisplayNameFor(model => model.Name)
    19             </th>
    20             <th>
    21                 @Html.DisplayNameFor(model => model.CategoryName)
    22             </th>
    23             <th></th>
    24         </tr>
    25     
    26     @foreach (var item in Model) {
    27         <tr>
    28             <td>
    29                 @Html.DisplayFor(modelItem => item.Name)
    30             </td>
    31             <td>
    32                 @Html.DisplayFor(modelItem => item.CategoryName)
    33             </td>
    34         </tr>
    35     }
    36     
    37     </table>
    38 </body>
    39 </html>

    代码清单4-9. Controller代码,使用通过Category Name过滤后的Product数据填充模型

     1 public class ProductController : Controller
     2     {
     3         //
     4         // GET: /Product/
     5 
     6         public ActionResult Index(string name)
     7         {
     8             using (var db = new ProductEntities())
     9             {
    10                 var query = from productRec in db.Products
    11                             join categoryRec in db.Categories
    12                             on productRec.CategoryId
    13                             equals categoryRec.CategoryId
    14                             where categoryRec.Name == name
    15                             select new
    16                             {
    17                                 Name = productRec.Name
    18                                 ,
    19                                 CategoryName = categoryRec.Name
    20                             };
    21                 List<ProductVM> lstProduct = new List<ProductVM>();
    22                 foreach(var record in query)
    23                 {
    24                     ProductVM productValue = new ProductVM();
    25                     productValue.Name = record.Name;
    26                     productValue.CategoryName = record.CategoryName;
    27                     lstProduct.Add(productValue);
    28                 }
    29                 return View(lstProduct);
    30             }
    31         }
    32 
    33     }

    图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/

  • 相关阅读:
    线程池小结(一)
    [转]ViewPager学习笔记(一)——懒加载
    [转]Private Libraries、Referenced Libraries、Dependency Libraries的区别
    关于context你必须知道的一切
    【转】在mac上配置安卓SDK
    【转】HTTP中的长连接和短连接分析
    中间件解析FDMEMTABLE.delta生成SQL的方法
    delphi 中配置文件的使用(*.ini)和TIniFile 用法
    Delphi 字符串加密与解密函数
    Delphi编写的等长加密与解密
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4517099.html
Copyright © 2011-2022 走看看