创建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中就提供了设定缓存对象的生命 ...
随机推荐
- ADO.NET的弹性连接控制[ADO.NET idle connection resiliency]
ADO.NET连接SQL Server有时候联机会无故的中断 (例如闲置过久或是交易时间太长等因素),这时又要重新连接,在.NET Framework 4.5之前,这件事情要由开发人员自己依照ADO. ...
- 简单一招实现json数据可视化
开发一个内部功能时碰到的需求,要把json数据在页面上展示出来,平时浏览器会安装jsonView这样的扩展来看json数据,但是程序要用到的话该怎么办呢?今天在网上搜索的时候,发现了这个小技巧,分享一 ...
- 从Undo,Redo谈命令模式
一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作. 要实现上面的这一功能,最直观的想法就是,我们 ...
- Sql Server数据库备份和恢复:原理篇
本文与您探讨为什么Sql Server有完整备份.差异备份和事务日志备份三种备份方式,以及为什么数据库又有简单模式.完整模式和大容量日志模式这三种恢复模式.本文内容适用于2005以上所有版本的Sql ...
- Entity Framework Code First数据库连接
1. 安装Entity Framework 使用NuGet安装Entity Framework程序包:工具->库程序包管理器->程序包管理器控制台,执行以下语句: PM> Insta ...
- 如何使用Worktile进行敏捷项目开发管理
Worktile在任务管理上采用了看板视图,非常适合进行敏捷项目开发管理.事实上,在开发Worktile的过程中,我们也是自产自销,使用Worktile管理Worktile本身的开发过程,在本文中跟大 ...
- Mongoose Schemas定义中timestamps选项的妙用
在Node.js中使用MongoDB少不了Mongoose. 假设有如下Mongoose Schemas的定义: var ItemSchema = new mongoose.Schema({ biz: ...
- js实用篇之String对象
概述 String对象是JavaScript原生提供的三个包装对象之一,用来生成字符串的包装对象. var s1 = 'abc'; var s2 = new String('abc'); typeof ...
- angularjs自动化测试系列之jasmine
angularjs自动化测试系列之jasmine jasmine参考 html <!DOCTYPE html> <html lang="en"> <h ...
- javascript四种类型识别的方法
× 目录 [1]typeof [2]instanceof [3]constructor[4]toString 前面的话 javascript有复杂的类型系统,类型识别则是基本的功能.javascrip ...