ASP.NET MVC with Entity Framework and CSS一挥毫翻系列随笔的第三段:搜索、高级过滤与视图模型

  之所以将分类也蕴含在内,是因若用户输入的是“clothes”,而非是一模一样桩特定的衣,那么所有的衣服都会见叫搜到。假诺我们不以分类作为找条件,用户可能无汇合招来到另外结果,这是以产品之称号或者描述字段中恐怕没有包含“clothes”这一个单词。

  于登时无异章节中,我们学了哪添加搜索效果,包括什么行使Bootstrap添加一个搜索框,然后上了争发现更多关于利用Boootstrap如何样式化的站点的信息。我们还以身作则了什么样使ViewBag添加一个对搜索结果的过滤,以及哪下视图模型加上一个一发扑朔迷离的过滤。

return View(viewModel);

3.1.3
在站点为主航栏中增长搜索框

3.4 小节

图片 1

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 5     <meta charset="utf-8" />
 6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 7     <title>@ViewBag.Title - 我的 ASP.NET 应用程序</title>
 8     @Styles.Render("~/Content/css")
 9     @Scripts.Render("~/bundles/modernizr")
10 
11 </head>
12 <body>
13     <div class="navbar navbar-inverse navbar-fixed-top">
14         <div class="container">
15             <div class="navbar-header">
16                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
17                     
18                     
19                     
20                 </button>
21                 @Html.ActionLink("Baby Store", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
22             </div>
23             <div class="navbar-collapse collapse">
24                 <ul class="nav navbar-nav">
25                     <li>@Html.ActionLink("主页", "Index", "Home")</li>
26                     <li>@Html.ActionLink("分类", "Index", "Categories")</li>
27                     <li>@Html.ActionLink("产品", "Index", "Products")</li>
28                 </ul>
29                 @using (Html.BeginForm("Index", "Products", FormMethod.Get, new { @class = "navbar-form navbar-left" }))
30                 {
31                     <div class="form-group">
32                         @Html.TextBox("Search", null, new { @class = "form-control", @placeholder = "Search Products" })
33                     </div>
34                     <button type="submit" class="btn btn-default">Submit</button>
35                 }
36                 @Html.Partial("_LoginPartial")
37             </div>
38         </div>
39     </div>
40     <div class="container body-content">
41         @RenderBody()
42         <hr />
43         <footer>
44             <p>&copy; @DateTime.Now.Year - 我的 ASP.NET 应用程序</p>
45         </footer>
46     </div>
47 
48     @Scripts.Render("~/bundles/jquery")
49     @Scripts.Render("~/bundles/bootstrap")
50     @RenderSection("scripts", required: false)
51 </body>
52 </html>
 1 using System.Collections.Generic;
 2 using System.Linq;
 3 using System.Web.Mvc;
 4 using BabyStore.Models;
 5 
 6 namespace BabyStore.ViewModels
 7 {
 8     public class CategoryWithCount
 9     {
10         public int ProductCount { get; set; }
11         public string CategoryName { get; set; }
12         public string CatNameWithCount
13         {
14             get
15             {
16                 return CategoryName + " (" + ProductCount.ToString() + ")";
17             }
18         }
19     }
20 
21     public class ProductIndexViewModel
22     {
23         public IQueryable<Product> Products { get; set; }
24         public string Search { get; set; }
25         public IEnumerable<CategoryWithCount> CatsWithCount { get; set; }
26         public string Category { get; set; }
27 
28         public IEnumerable<SelectListItem> CatFilterItems
29         {
30             get
31             {
32                 var allCats = CatsWithCount.Select(cc => new SelectListItem
33                 {
34                     Value = cc.CategoryName,
35                     Text = cc.CatNameWithCount
36                 });
37 
38                 return allCats;
39             }
40         }
41     }
42 }

图片 2

图3-1:经过URL手动搜索含有“red”的成品

  注意,不属其他分类的底出品将非会晤显得在分拣的下拉列表中,不过仍旧会吃摸到。

1 <p>
2     @Html.ActionLink("Create New", "Create")
3     @using(Html.BeginForm("Index", "Products", FormMethod.Get))
4     {
5         <label>Filter by category:</label> @Html.DropDownList("Category", "All")
6         <input type="submit" value="Filter"/>
7         <input type="hidden" name="Search" id="Search" value="@ViewBag.Search"/>
8     }
9 </p>

  那段代码用会面变卦一个冲Product类的Description属性的表格标题。假使当数据库被之Products表中绝非数,该代码也会师是工作。

图片 3

 1 public ActionResult Index(string category, string search)
 2 {
 3     var products = db.Products.Include(p => p.Category);
 4 
 5     if (!string.IsNullOrEmpty(category))
 6     {
 7         products = products.Where(p => p.Category.Name == category);
 8     }
 9 
10     if (!string.IsNullOrEmpty(search))
11     {
12         products = products.Where(p => p.Name.Contains(search) || p.Description.Contains(search) || p.Category.Name.Contains(search));
13         ViewBag.Search = search;
14     }
15 
16     var categories = products.OrderBy(p => p.Category.Name).Select(p => p.Category.Name).Distinct();
17 
18     ViewBag.Category = new SelectList(categories);
19 
20     return View(products.ToList());
21 }

  将追寻条件保存在VeiwBag中,以便当用户点击分类过滤时方可引用该找条件。尽管大家无保留该找条件,那么是搜索条件将会给丢,产品信息以未会晤被正确地被滤。

  提示:当HTML襄助器的DisplayNameFor方法用于汇而休是纯粹对象时,使用First()方法以便访问我们而缅想映现的性之号。

3.1.4 怎么着利用Bootstrap添加体

 1 public ActionResult Index(string category, string search)
 2 {
 3     var products = db.Products.Include(p => p.Category);
 4 
 5     if (!string.IsNullOrEmpty(category))
 6     {
 7         products = products.Where(p => p.Category.Name == category);
 8     }
 9 
10     if (!string.IsNullOrEmpty(search))
11     {
12         products = products.Where(p => p.Name.Contains(search) || p.Description.Contains(search) || p.Category.Name.Contains(search));
13     }
14 
15     return View(products.ToList());
16 }

  这多少个近乎公事看起相比大家事先运用的代码都要复杂,因而,大家拿同步一步地演说每个属性之用。

  对于每一个分组,分类的名号以及拖欠分类所对应的产品数量被赋值给一个CategoryWithCount对象。

  看起代码都如预想的如出一辙吃科学实施,然则,代码是一个有些题目。图3-7著了搜索“Red”之后,又按Clothes分类过滤后的效应。

  首先,删除站点中的关于与联系情势连接。在此例子中无谋面动用及其,并且其还占空间。为了完成这干活,从Views\Shared\_Layout.cshtml文件中删去以下代码:

  以BabyStore项目下创设一个称也“ViewModels”的新文件夹,然后在其丰硕一个称为吧ProductIndexViewModel的好像,然后以此类中编如下代码:

from matchinngProducts in products
where matchinngProducts.CategoryID != null
group matchinngProducts by matchinngProducts.Category.Name into catGroup

  以即时等同节中,我们首先补充加一个寻产品之模块以增长站点的机能,然后采取视图模型如若不是ViewBag向视图传递复杂数据。

  第三独特性CatNameWithCount重返ProductCount和CategoryName合并后的格式化字符串,例如:Clothes
(2)。

  点击【开头调剂(不履)】菜单项启动应用程序以测试新的摸效果,在首页中点击产品链接,打开产品目录(Index)页面,在URL前面手动添加?search=red,然后回车,我们以会见盼而图3-1所体现之结果。

 1 public ActionResult Index(string category, string search)
 2 {
 3     var products = db.Products.Include(p => p.Category);
 4 
 5     if (!string.IsNullOrEmpty(search))
 6     {
 7         products = products.Where(p => p.Name.Contains(search) || p.Description.Contains(search) || p.Category.Name.Contains(search));
 8         ViewBag.Search = search;
 9     }
10 
11     var categories = products.OrderBy(p => p.Category.Name).Select(p => p.Category.Name).Distinct();
12 
13     if (!string.IsNullOrEmpty(category))
14     {
15         products = products.Where(p => p.Category.Name == category);
16     }
17 
18     ViewBag.Category = new SelectList(categories);
19 
20     return View(products.ToList());
21 }
 1 using BabyStore.ViewModels;
 2 
 3 public ActionResult Index(string category, string search)
 4 {
 5     // instantiate a new view model
 6     ProductIndexViewModel viewModel = new ProductIndexViewModel();
 7 
 8     var products = db.Products.Include(p => p.Category);
 9 
10     if (!string.IsNullOrEmpty(search))
11     {
12         products = products.Where(p => p.Name.Contains(search) || p.Description.Contains(search) || p.Category.Name.Contains(search));
13         viewModel.Search = search;
14     }
15 
16     // group search results into categories and count how many item in each category
17     viewModel.CatsWithCount = from matchinngProducts in products
18                               where matchinngProducts.CategoryID != null
19                               group matchinngProducts by matchinngProducts.Category.Name into catGroup
20                               select new CategoryWithCount()
21                               {
22                                   CategoryName = catGroup.Key,
23                                   ProductCount = catGroup.Count()
24                               };
25 
26     if (!string.IsNullOrEmpty(category))
27     {
28         products = products.Where(p => p.Category.Name == category);
29     }
30 
31     viewModel.Products = products;
32 
33     return View(viewModel);
34 }

  下面,我们添加一个初功用以便用户可按分类过滤搜索结果。在斯例子中,咱们用于检索结果页面被运用ViewBag来体现一个饱含分类信息之下拉框。我们以此下拉框与用户输入的查找条件相关联,以便在下拉框中单独彰显和寻找结果相关的归类,同时不允许用户挑选一个空的归类。

3.1.2 测试产品搜索

  第三远在代码改动是LINQ语句,该语句以一个CategoryWithCount对象的列表赋值给viewModel的CatsWithCount属性。在是例子中,因为此查询相比复杂,大家应用了另外一栽格局之LINQ,该形成称之为查询语法(query
syntax),该语法使得查询语句再度易看。

  第一单特性ProductCount用于保存该分类中所含有的产品数量。

  第二单特性是public string Search { get; set;
},将会师用来替换在当前ProductsController类中利用的ViewBag.Search。

  第五只属性是public IEnumerable<CategoryWithCount>
CatsWithCount { get; set;
},将碰面用来存储CategoryWithCount类型的集结,该集用于视图中的下拉列表中。

  这个问题是在以按部就班Clothes分类过滤后,在分拣列表中的Toys分类信息了。修正那些问题非凡粗略,不过它强调了亟需遵照正确的一一来成立大家的询问。为了修正这一个问题,向如下所出示之代码一样修改\Controllers\ProductsController.cs文件被的Index方法,以要本分类过滤产品之代码放在categories变量已让赋值的后:

   更新\Views\Products\Index.cshtml文件,以便利用视图模型生成过滤下拉列表、检索搜索字符串、呈现表格标题以及呈现产品列表。举行一下改变:

图3-8:追寻“Red”,然后按Clothes分类过滤后,Toys分类仍然得以叫用户举办抉择

  不调试启动站点,然后点击产品链接,大家会发觉在分拣下拉列表中的每个分类且带有一个数,这反映出每个分类所蕴涵的产品的数额。图3-9显得了咱于寻觅框中输入“Red”后,下拉列表所出示的条规。

3.3.2
修改ProductsController控制器的Index方法以利用视图模型

3.3.1 创制视图模型

viewModel.Products = products;

图3-4:每当bootswatch.com站点及冒出于某某元素右上斗的预览符号<>

@Html.DisplayNameFor(model => model.Products.First().Description)

  于是事例中,我们拿上对各一个分拣,咋样实现以下拉列表中而呈现该分类所指向之产品数量。为了贯彻此效果,大家用一个视图模型保存大家传递给视图的具备音信。

图片 4

图片 5

3.2.1
修改ProductsController控制器的Index方法为落实按分类举办过滤

3.3.3
修改视图以便利用视图模型就新过滤效果

<input type=”hidden” name=”Search” id=”Search” value=”@Model.Search”
/>

  最终,我们依据categories变量生成了一个SelectList对象,并将这保存于ViewBag中,以便在视图中使用。

  为了执行产品搜索,大家拿添加一些效果而其能以产品名称、描述和归类开展搜寻,从而被用户暴发一个更好之选择来索有关结果。

  为了抬高一个搜索框,编辑Views\Shared\_Layout.cshtml文件中class属性为“navbar-collapse
collapse”的div,将该代码修改也如下高亮展现的代码:

  CategoryWithCount类相比较简单,重要用来保存分类的名跟拖欠分类所包含该的产品数量。

  为了使\Products\Index.cshtml文件生成的HTML页面具有按分类过滤的效用,咱们得充分一个新的HTML表单以付过滤请求。大家于Views\Products\Index.cshtml文件之Create
New链接后边加加一个饱含下列列表的新表单,代码如下所示:

  ProductIndexViewModel类需要保留先前用ViewBag传递让视图的音讯做及模型IEnumerable<BabyStore.Models.Product>(该型出现在/Views/Products/Index.cshtml文件的顶部)。

  Bootstrap是一个HTML、CSS和JavaScript的框架,起头由Twitter创立。使用基架的ASP.NET项目默认使用她来样式化站点的外观。在网上有同一死堆关于Bootstrap的可用音讯,因而,大家无晤面商讨关于它的另外细节,但是,当我们引入新的文化时,大家会面说为什么我们会那么样式化它。

  最终一个性质public IEnumerable<SelectListItem>
CatFilterItems用于重临一个SelectListItem类型的列表,该属性将晤面用第三单特性CatsWithCount生成SelectListItem对象,该目的的Value对诺CategoryWithCount的CategoryName,Text对承诺CategoryWithCount的CatNameWithCount;Value用于视图中下拉列表的价值,Text用于视图中下拉列表的示文本。

图3-6:含过滤效果的出品目录(Index)页面

  那么,我们怎么着掌握搜索框的体?答案在Thomas(Thomas)Park创设的站点http://www.bootswatch.com。这些站点提供了几乎单免费之Bootstrap大旨,还提供了成千上万元素的HTML预览。假诺咱们点击Themes按钮中之某核心,然后为下带动页面,并且将鼠标悬停在某元素上,那个时<>符号将会面世在该因素的右手上比,如图3-4所展现。假如点击是标记,HTML预览就会面冒出,如图3-5所著。

图片 6

  当我们要变更表格标题时,不像看起那么粗略,因为HTML协理器的DisplayNameFor方法无适用于聚集,由此,我们依照视图模型的Products属性来体现标题。分类标题分外简短,因为当时是视图模型的一个性能。可是,显示Product类的Description属性的名不克运用类这样的代码@Html.DisplayNameFor(model
=>
model.Products.Description),而是要求大家以First()方法,强制辅助器使用自于products集合中的,一个实际上的Product实体,代码如下:

  于就段代码中尚涉嫌了面前大家没以过的HTML5的placeholder特性,它要用来在页面第一次加载时,在查找框架中显得“Search
Products”文本,以向用户指示该搜索框的用途。这要透过以代码@Html.TextBox(“Search”,
null, new { @class = “form-control”, @placeholder = “Search
Products”})中向htmlAttributes参数对象传递额外的章完成的。文本框的name属性被指定为Search,MVC框架将该及寻找参数Search匹配。图3-3形了导航栏的末梢效果。

  代码viewModel.Search =
search;将变量search赋值给viewModel而不是ViewBag。

  一旦我们下ViewBag将多独数据由控制器传递给视图时,我们的代码会更换得难以维护,那重要归咎为ViewBag的动态特性,Visual
Studio没有指向ViewBag可用之属性提供智能提示功用,这丰硕易造成ViewBag属性的拼写错误。

select new CategoryWithCount()
{
  CategoryName = catGroup.Key,
  ProductCount = catGroup.Count()
};

3.3
使用视图模型实现更为扑朔迷离的过滤

  遵照直白的传道,上边的代码表示的意义是:倘若产品之号、产品之讲述和产品的分类名中蕴藏search,则这多少个制品都会师吃当结果重临。这段代码也下了lambda表明式,不过这表明式更加错综复杂,并且动了逻辑或(||)操作符。注意,在lambda操作符(=>)左边还只暴发一个参数,固然于这右手边的代码中出多少长度条语句。当就长达告句提交至数据库时,Contains方法给移为SQL的LIKE操作,并且她不是高低写敏感的。

图3-2:透过修改URL搜索属于clothes分类的活

图3-3:成就搜索框下的导航栏

3.1 添加产品搜索

3.2.2
向产品之目(Index)页面添加过滤效果

  按下的代码修改Controllers\ProductsController.cs文件被之Index方法,以便在ViewBag中蕴藏时寻找的基准,并且大成一个剔除重复值的归类列表,并拿其作为SelectList对象存储在ViewBag中。

  大家不应当希望用户会手动输入URL以寻找产品,因而,我们出必要在站点中上加一个搜索框。大家用当站点的中央航栏中增长这个搜索框,以便用户可从来视它,并可在其他页面被选用她。

  首先,在Index方法被上加了一个称呼也search的参数,然后,判断该参数是否也null或空,假诺未呢null或空,则大家应用下的代码来进展产品之查找:

  就像以第一章节讲述的那么,主导航栏是站点布局页的一样片段,因而其涵盖在Views\Shared\_Layout.cshtml文件中。

  最后一处变更的代码用products变量赋值给viewModel的Products属性,而非是将它们传递让视图。紧接着,我们以viewModel传递给视图,代码如下:

  修改Controllers\ProductsController.cs文件中的Index方法为补充加产品搜索效果,代码如下所示:

3.2
使用ViewBag依据分类过滤搜索结果

  这段代码添加了一个表单,该表单使用GET方法为ProductsController控制器的Index动作方法提交请求,因而,查询字符串所包含的价一并受提交到服务端。使用代码@Html.DropDownList(“Category”,
“All”)生成一个下拉列表,该下拉列表使用了ViewBag.Category属性所提供的价值,参数“All”对下拉列表指定了一个默认值。添加的交由按钮Filter可以被用户提交表单并举行过滤。一个藏匿的HTML元素被用来存储时之追寻条件,当遵照分类过滤产品音信并付诸表单时,可以确保用户最初输入的找条件为保留下来。

1 <li>@Html.ActionLink("关于", "About", "Home")</li>
2 <li>@Html.ActionLink("联系方式", "Contact", "Home")</li>

图片 7

图3-5:根源于bootswatch.com的HTML预览。高亮部分显得了什么样样式化一个搜索框和咋样在导航中插足表单

  对之文件所开的代码修改好简单易行,但意义首要。第一介乎变更@model
BabyStore.ViewModels.ProductIndexViewModel只是简简单单地告知视图使用ProductIndexViewModel作为该视图基于的型。注意该模型现在凡是一个纯净的类似,而不是一个可枚举的集。

图片 8

  第二处变更@Html.DropDownListFor(vm => vm.Category,
Model.CatFilterItems,
“All”);基于视图模型的CatFilterItems属性生成一个下拉列表,第二只参数用于转移下拉列表中的来得数据及价值。第一个参数vm
=>
vm.Category指定该下拉列表的名目,当提交该表单时,该下拉列表的名号将应运而生在URL的查询字符串部分。因为拖欠控件的称呼是Category,因而,大家原先在URL中查找Category参数的代码用会见持续科学地干活。

  不调试启动站点,图3-6示了寻“Red”单词后底产品之目(Index)页面的榜样,注意All是过滤下拉绳的默认值。

  由控制器向视图传递音信更好的一致种植办法是应用视图模型,而未是ViewBag。然后视图基于那种模型,而不是基于域模型(到近期结束,咱们的视图都是基于域模型的)。一些开发人士将这种概念进一步细分,将他们所有的视图都只有按照视图模型。在本书中,我们混用视图模型和域模型。

  以斯看似中的首先单特性public IQueryable<Product> Products {
get; set; },将会晤用来替换在时视图中动用的型。

  更新\Controllers\ProductsController.cs文件的Index方法以便她异常下列代码。改动的代码用粗体高亮呈现:使用视图模型如果无是ViewBag重回路之计数。首先,在拖欠公文的上方添加一个using语句,以要该类可以看我们恰好创造的ProductIndexViewModel类。

3.1.1 修改控制器以拓展产品搜索

  于这法外之第一处于改动的代码是创设一个视图模型:ProductIndexViewModel
viewModel = new ProductIndexViewModel();

  大家用用LINQ to Entities中之方法来查找相关产品音信。

  首先,这一个文件包含八只类似,分别吗CategoryWithCount和ProductIndexViewModel。

  不调试启动站点,虽然我们本实施一个追寻,然后以分类举行过滤,我们会发现于分拣下拉列表中的Toys分类依旧在,如图3-8所显示。

 1 @model BabyStore.ViewModels.ProductIndexViewModel
 2 
 3 @{
 4     ViewBag.Title = "Index";
 5 }
 6 
 7 <h2>Index</h2>
 8 
 9 <p>
10     @Html.ActionLink("Create New", "Create")
11     @using(Html.BeginForm("Index", "Products", FormMethod.Get))
12     {
13         <label>Filter by category:</label>@Html.DropDownListFor(vm => vm.Category, Model.CatFilterItems, "All");
14         <input type="submit" value="Filter"/>
15         <input type="hidden" name="Search" id="Search" value="@Model.Search"/>
16     }
17 </p>
18 <table class="table">
19     <tr>
20         <th>
21             @Html.DisplayNameFor(model => model.Category)
22         </th>
23         <th>
24             @Html.DisplayNameFor(model => model.Products.First().Name)
25         </th>
26         <th>
27             @Html.DisplayNameFor(model => model.Products.First().Description)
28         </th>
29         <th>
30             @Html.DisplayNameFor(model => model.Products.First().Price)
31         </th>
32         <th></th>
33     </tr>
34 
35 @foreach (var item in Model.Products) {
36     <tr>
37         <td>
38             @Html.DisplayFor(modelItem => item.Category.Name)
39         </td>
40         <td>
41             @Html.DisplayFor(modelItem => item.Name)
42         </td>
43         <td>
44             @Html.DisplayFor(modelItem => item.Description)
45         </td>
46         <td>
47             @Html.DisplayFor(modelItem => item.Price)
48         </td>
49         <td>
50             @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
51             @Html.ActionLink("Details", "Details", new { id=item.ID }) |
52             @Html.ActionLink("Delete", "Delete", new { id=item.ID })
53         </td>
54     </tr>
55 }
56 
57 </table>

  表单使用GET而非是POST请求以便查找条件得当URL中视。由此,用户可复制它然后下任何艺术分享她,比如e-mail或社交媒体。遵照常规,GET请求用于查询数据库中之数目,而非是修改数据库被之数码。

  代码var categories = products.OrderBy(p =>
p.Category.Name).Select(p =>
p.Category.Name).Distinct();生成了一个按字母排序并且失去除重复值的归类列表。这些分类列表是免健全的,它仅仅含有与追寻条件有关的制品所蕴涵的分类消息。

图3-9:于下拉列表中之各国一个分类且带有所匹配的产品数量

  第两只属性Category将会为此作视图备受之下拉列表的称。

  最终一处于变更是@foreach (var item in Model.Products)
{,确保我们前几天以视图模型的Products属性来显示产品信息。

  第三不良反保确保隐藏域现在引述的凡视图模型要是非是ViewBag:

  第二独属性CategoryName用于保存分类的名目。

图3-7:查找“Red”后以Clothes过滤后的页面

  为了测试照分类名搜索产品是否能科学工作,我们修改URL为/Products?search=clothes。这些查询现在以匹配在产品名称或产品描述或分类名中涵盖clothes这么些单词的富有产品,结果要图3-2所出示。

1 if (!string.IsNullOrEmpty(search))
2 {
3     products = products.Where(p => p.Name.Contains(search) || p.Description.Contains(search) || p.Category.Name.Contains(search));
4 }

  使用搜索框执行有找,结果当与手动在URL中输入搜索条件的效应一样。

注意:要是您想以本章的代码编写示例,你必形成第二章节要直接打www.apress.com下载第二章的源代码。

图片 9

  该语句之下列代码,首先选取where子句过滤掉分类ID为null的产品,然后照分类名举办分组。

相关文章