创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段
创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段
添加查询功能
本文将实现通过Name查询用户信息。
首先更新GetAll方法以启用查询:
public async Task<IEnumerable<User>> GetAll(string searchString)
{
var users = from u in _context.Users
select u;
if (!string.IsNullOrEmpty(searchString))
{
users = users.Where(u => u.Name.Contains(searchString));
}
return await users.ToListAsync();
}
第一行的LINQ查询仅仅在这里作了定义,并没有在这里实际操作数据库。
LINQ查询在被定义或者通过调用类似于Where、Contains、OrderBy的方法进行修改的时候不会执行。相反的,查询会延迟执行,比如在ToListAsync方法被调用之后。
Contains方法会在数据库中运行,而不是上面的C#代码,在数据库中,Contains会被映射成不区分大小写的SQLLIKE。
由于u => u.Name.Contains(searchString)Lambda表达式在当前我所使用的MySQL Connector/NET版本中运行报错(新版本好像已经修复了该问题,这里懒得更新了),所以这里先改成:
users = users.Where(u => u.Name == searchString);
导航到http://localhost:5000/User,添加?searchString=Zhu查询字符串,将过滤出指定的用户信息(http://localhost:5000/User?searchString=Zhu)。
如果你修改Index方法的签名使得方法包含一个名为id的参数,那么id参数将会匹配Startup.cs文件中设置的默认路由的可选项{id?}。
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Rename后的方法:
public async Task<IActionResult> Index(string id)
{
var users = from u in _context.Users
select u;
if (!String.IsNullOrEmpty(id))
{
users = users.Where(u => u.Name == id);
}
return View(await users.ToListAsync());
}
现在你可以传递这个Name查询条件作为路由数据(URL Segment)来代替查询字符串(http://localhost:5000/User/Index/Zhu)。
然而,我们不能指望用户每次都通过修改URL来进行查询,我们通过添加UI来进行查询。如果你想改变Index方法的签名来测试怎样传递路由绑定ID参数,将searchString参数改回来。
接着,打开Views/User/Index.cshtml文件,添加<form>标记:
<form asp-controller="User" asp-action="Index">
<p>
姓名: <input type="text" name="SearchString">
<input type="submit" value="过滤" />
</p>
</form>
这里HTML<form>标签使用了Form Tag Helper,所以当你提交这个表单时,过滤字符串会被传递到User控制器的Index方法中。

这里没有使用你所希望的[HttpPost] Index重载方法,其实根本不需要该方法,因为这个方法并没有改变这个应用的状态,仅仅用来过滤数据。
你可以添加下面的[HttpPost] Index方法。
[HttpPost]
public string Index(string searchString, bool notUsed)
{
return "From [HttpPost]Index: filter on " + searchString;
}
notUsed参数用于创建一个重载的Index方法。如果你添加了该方法,动作方法将会调用匹配的[HttpPost] Index方法。
这个时候点击过滤按钮时,会显示如下界面:

然而,尽管你添加了[HttpPost]版本的Index方法,最终仍然存在局限性。想象你将一个指定的查询作为书签或者将查询结果作为一个链接发送给你的朋友以便他们在打开时能看到同样的过滤结果时,注意HTTP POST请求的URL和GET请求的URL(http://localhost:5000/User)是一样的-在URL里面没有任何查询信息。查询信息是作为表单数据发送到服务器的。

在请求体中可以看到查询参数和XSRF反伪造标记(通过Form Tag Helper生成),由于查询没有修改数据,所以无需在控制器方法中验证该标记。
为了把查询参数从请求体中移到URL中,必须把请求指定为HTTP GET。

<form asp-controller="Movies" asp-action="Index" method="get">
此时当你再提交时,URL将会包含具体的查询条件。查询将会跳转到HttpGet Index方法,即使存在着HttpPost Index方法。
添加新的字段
我们将通过Entity Framework Code First Migrations工具来添加一个新的字段到模型中,并将新的改变同步到数据库中。
当你使用EF Code First自动创建一个数据库时,Code First会添加一个表到数据库中帮助跟踪数据库的数据结构是否和模型类保持同步。如果不同步,EF会抛出一个异常。
添加身高属性到模型类
public class User
{
public int ID { get; set; }
[Display(Name = "姓名")]
public string Name { get; set; }
[Display(Name = "邮箱")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Display(Name = "简介")]
public string Bio { get; set; }
// 新添加的属性
[Display(Name = "身高")]
public decimal Height { get; set; }
[Display(Name = "职称")]
public string Title { get; set; }
[Display(Name = "部门")]
public string Dept { get; set; }
}
Build该应用(dotnet build)。可能由于我所使用的MySQL EF Core provider还不成熟,添加DateTime类型的字段时会报错(新版本可能会修复该问题,这里懒得去测试了)。
由于你添加了一个新的字段到User类,所以你需要更新所绑定的白名单,这样新的属性将会包含这内。为Create和Edit方法更新[Bind]特性以包含Height属性。
[Bind("ID,Name,Email,Bio,Height,Title,Dept")]
为了显示新的字段还要更新视图模板。
打开Index.cshtml、Create.cshtml、Edit.cshtml等文件,添加新的Height字段。
然后需要更新数据库以包含新的字段。如果没有更新,此时运行将报错,因为更新后User模型类和已存在的数据库User表的结构不一致。
有以下几种方案来解决该错误:
EF可以基于新的模型类自动删除并重建数据库。开发阶段在测试数据库上做开发还是比较方便的,但是你会丢失数据库中的现有数据。因此该方案不适用于生产环境数据库!
显式修改现有数据库的结构,使得它与模型类相匹配,这个方案可以让你保留数据库的现有数据。你可以通过手动或者数据库脚本来进行变更。
使用Code First Migrations来更新数据库结构。
这里采用第三种方案,运行如下命令:
dotnet ef migrations add Height
dotnet ef database update
migrations add命令告诉Migration框架去检查当前的User模型类和当前的User数据库表结构是否一致。如果不一致,就创建必要的代码来迁移数据库到新的模型类。
基于新添加的“部门”字段进行查询
在Models目录下添加UserDeptViewModel类:
using Microsoft.AspNetCore.Mvc.Rendering;
public class UserDeptViewModel
{
public List<User> users;
public SelectList depts;
public string userDept { get; set; }
}
这个视图模型(View Model)将包含:
- 用户列表
users。 - 包含部门列表(depts)的
SelectList,将用于视图页面中允许用户去从列表中选择一个部门。 userDept,包含用户所选中的部门(dept)。
修改Index方法:
public async Task<IActionResult> Index(string userDept, string searchString)
{
IQueryable<string> deptQuery = from u in _context.Users
orderby u.Dept
select u.Dept;
var users = from u in _context.Users
select u;
if (!String.IsNullOrEmpty(searchString))
{
users = users.Where(u => u.Name == searchString);
}
if (!String.IsNullOrEmpty(userDept))
{
users = users.Where(u => u.Dept == userDept);
}
var userDeptVM = new UserDeptViewModel();
userDeptVM.depts = new SelectList(await deptQuery.Distinct().ToListAsync());
userDeptVM.users = await users.ToListAsync();
return View(userDeptVM);
}
下面的代码通过LINQ查询从数据库获取所有的部门数据。
IQueryable<string> deptQuery = from u in _context.Users
orderby u.Dept
select u.Dept;
depts的SelectList是通过投影不重复的部门(Distinct)创建的。
userDeptVM.depts = new SelectList(await deptQuery.Distinct().ToListAsync());
在Index视图中添加“部门”查询字段
首先删除最顶部的@model:
@model IEnumerable<MyFirstApp.Models.User>
替换成:
@model UserDeptViewModel
在<form>标记中添加:
<select asp-for="userDept" asp-items="Model.depts">
<option value="">所有</option>
</select>
在<table>标记中分别删除:
@Html.DisplayNameFor(model => model.Dept)
@foreach (var item in Model) {
替换成:
@Html.DisplayNameFor(model => model.users[0].Dept)
@foreach (var item in Model.users) {
最终的Index.cshtml文件如下:
@model UserDeptViewModel
@{
ViewData["Title"] = "Index - User List";
}
<h2>首页 - 用户列表</h2>
<p>
<a asp-action="Create">新建</a>
</p>
<form asp-controller="User" asp-action="Index" method="GET">
<p>
<select asp-for="userDept" asp-items="Model.depts">
<option value="">所有</option>
</select>
姓名: <input type="text" name="SearchString">
<input type="submit" value="过滤" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.users[0].Name)
</th>
<th>
@Html.DisplayNameFor(model => model.users[0].Email)
</th>
<th>
@Html.DisplayNameFor(model => model.users[0].Height)
</th>
<th>
@Html.DisplayNameFor(model => model.users[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.users[0].Dept)
</th>
<th>
@Html.DisplayNameFor(model => model.users[0].Bio)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.users) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Height)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Dept)
</td>
<td>
@Html.DisplayFor(modelItem => item.Bio)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.ID">编辑</a> |
<a asp-action="Details" asp-route-id="@item.ID">详情</a> |
<a asp-action="Delete" asp-route-id="@item.ID">删除</a>
</td>
</tr>
}
</tbody>
</table>
最终运行的页面:

个人博客
创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段的更多相关文章
- 创建ASP.NET Core MVC应用程序(6)-添加验证
创建ASP.NET Core MVC应用程序(6)-添加验证 DRY原则 DRY("Don't Repeat Yourself")是MVC的设计原则之一.ASP.NET MVC鼓励 ...
- 创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图 创建CRUD动作方法及视图 参照VS自带的基架(Scaffold)系统-MVC Controller with view ...
- 创建ASP.NET Core MVC应用程序(1)-添加Controller和View
创建ASP.NET Core MVC应用程序(1)-添加Controller和View 参考文档:Getting started with ASP.NET Core MVC and Visual St ...
- 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表
创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...
- 创建ASP.NET Core MVC应用程序(2)-利用MySQL Connector NET连接到MySQL
创建ASP.NET Core MVC应用程序(2)-利用MySQL Connector NET连接到MySQL 用惯.NET的研发人员都习惯性地使用SQLServer作为数据库.然而.NET Core ...
- 跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)
作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...
- 【翻译】使用Visual Studio创建Asp.Net Core MVC (一)
This tutorial will teach you the basics of building an ASP.NET Core MVC web app using Visual Studio ...
- 学习ASP.NET Core Razor 编程系列九——增加查询功能
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- ASP.NET Core MVC应用程序中的后台工作任务
在应用程序的内存中缓存常见数据(如查找)可以显着提高您的MVC Web应用程序性能和响应时间.当然,这些数据必须定期刷新. 当然你可以使用任何方法来更新数据,例如Redis中就提供了设定缓存对象的生命 ...
随机推荐
- angularjs provider 供应商服务
今天学习了angularjs的provider的供应商服务,写了个例子(自定义供应商服务,也可使用angularjs内部提供的服务) var starterApp = angular.module(' ...
- Linux下如何自己编译源代码(制作成可以安装的.deb文件)
以tree实用程序(以树型结构获取目录树)为例,介绍Ubuntu中如何管理源码包,包括查询,获取,编译源码包,直至安装. 1) 在获取源码包之前,确保在软件源配置文件/etc/apt/source ...
- 安装SQL Server2016正式版
安装SQL Server2016正式版 今天终于有时间安装SQL Server2016正式版,下载那个安装包都用了一个星期 安装包可以从这里下载: http://www.itellyou.cn/ ht ...
- 新书到手 TRANSACTION PROCESSING:CONCEPTS AND TECHNIQUES
新书到手 TRANSACTION PROCESSING:CONCEPTS AND TECHNIQUES Jim Gray大神的著作 本文版权归作者所有,未经作者同意不得转载.
- IIS 8:IIS 入门
深埋在您的 Microsoft 服务器 (2008年. 2008 R2 和 2012年的版本) 的范围内是最强大的 Web 服务器可用. 它只等待你来发挥其全部潜力. 您的目标是要从家里运行一个 Wo ...
- 剑指Offer面试题:32.数字在排序数组中出现的次数
一.题目:数字在排序数组中出现的次数 题目:统计一个数字在排序数组中出现的次数.例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4. 二.解题思路 2 ...
- 依赖倒置(DIP)与依赖注入(DI)
依赖倒置原则(Dependency Inversion Principle)为我们提供了降低模块间耦合度的一种思路,依赖注入(Dependency Injection)是一种具体的实施方法. 依赖倒置 ...
- 人才市场的IT职位分析
最近要找长沙的工作,于是通过湖南人才市场搜索了一下职位.结果得到的数据让我很难比较,作为一个 IT 业滚爬了多年的程序员,对这样的搜索结果很不满意.于是,我不得不自己来整理数据了.本文内容包括:网页数 ...
- HTML、CSS、JS对unicode字符的不同处理
unicode字符的不同表示法 unicode字符在html.css和js中的表示方法均不相同,下面分别作介绍. 原文发表于这里 css表示法 首先来一段很常见的bootstrap的字体图标代码: . ...
- 谷歌chrome浏览器www.tradeadexchange.com广告弹窗跳转劫持病毒
近期大量网友出现chrome浏览器被劫持的情况,表现如下: · 点击(访问)任意网站任意链接均有概率弹出www.tradeadexchange.com. · ...