创建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查询在被定义或者通过调用类似于WhereContainsOrderBy的方法进行修改的时候不会执行。相反的,查询会延迟执行,比如在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类,所以你需要更新所绑定的白名单,这样新的属性将会包含这内。为CreateEdit方法更新[Bind]特性以包含Height属性。

[Bind("ID,Name,Email,Bio,Height,Title,Dept")]

为了显示新的字段还要更新视图模板。

打开Index.cshtmlCreate.cshtmlEdit.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)-添加查询功能 & 新字段的更多相关文章

  1. 创建ASP.NET Core MVC应用程序(6)-添加验证

    创建ASP.NET Core MVC应用程序(6)-添加验证 DRY原则 DRY("Don't Repeat Yourself")是MVC的设计原则之一.ASP.NET MVC鼓励 ...

  2. 创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图

    创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图 创建CRUD动作方法及视图 参照VS自带的基架(Scaffold)系统-MVC Controller with view ...

  3. 创建ASP.NET Core MVC应用程序(1)-添加Controller和View

    创建ASP.NET Core MVC应用程序(1)-添加Controller和View 参考文档:Getting started with ASP.NET Core MVC and Visual St ...

  4. 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表

    创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...

  5. 创建ASP.NET Core MVC应用程序(2)-利用MySQL Connector NET连接到MySQL

    创建ASP.NET Core MVC应用程序(2)-利用MySQL Connector NET连接到MySQL 用惯.NET的研发人员都习惯性地使用SQLServer作为数据库.然而.NET Core ...

  6. 跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)

    作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...

  7. 【翻译】使用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 ...

  8. 学习ASP.NET Core Razor 编程系列九——增加查询功能

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  9. ASP.NET Core MVC应用程序中的后台工作任务

    在应用程序的内存中缓存常见数据(如查找)可以显着提高您的MVC Web应用程序性能和响应时间.当然,这些数据必须定期刷新. 当然你可以使用任何方法来更新数据,例如Redis中就提供了设定缓存对象的生命 ...

随机推荐

  1. PowerDesigner连接SQL Server

    以前听说过PowerDesigner可以和数据库连接,根据在PowerDesigner创建的数据模型创建表.触发器.存储过程到数据库中.也可以将已有的数据库导出到PowerDesigner中为数据模型 ...

  2. 探索C#之微型MapReduce

    MapReduce近几年比较热的分布式计算编程模型,以C#为例简单介绍下MapReduce分布式计算. 阅读目录 背景 Map实现 Reduce实现 支持分布式 总结 背景 某平行世界程序猿小张接到B ...

  3. 【效率】专为Win7系统设计的极简番茄计时器 - MiniPomodoro (附源码)

    时光飞逝,一转眼坚持使用番茄工作法已经快3年了!能坚持这么长时间,主要还是得益于它的简单.但是令人纠结的是,这么长时间以来,换了7款不同的番茄计时器,仍然没有找到非常满意的: ■ 机械的噪音太大,会妨 ...

  4. .NET 基础 一步步 一幕幕 [前言]

    .NET 基础 一步步 一幕幕 [前言部分] 本人小白一枚,虽然说从去年就开通博客了,到现在也没有写多少东东,虽然工作了,也没有更好得总结.故此重新祭出博客园法宝,修炼技术,争取早日走上大神之位. 故 ...

  5. PHP 基础知识测试题 答案分析

    一:选择题(单项选择,每题2分): 1. LAMP具体结构不包含下面哪种(A      ) A:Windows系统              如果是这个就是WMP B:Apache服务器 C:MySQ ...

  6. 为什么DOM操作很慢

    转自:http://kb.cnblogs.com/page/534571/ 一直都听说DOM很慢,要尽量少的去操作DOM,于是就想进一步去探究下为什么大家都会这样说,在网上学习了一些资料,这边整理出来 ...

  7. python统计某一个进程名所占用的内存

    设计思路: 通过python,执行cmd中tasklist命令,获取要统计的进程的相关信息:通过正则表达式,查找出进程名称.进程pid.内存使用,然后打印出来. 作为pythoner,有时候需要统计p ...

  8. px、dp与sp的区别以及换算

    1.px 即像素,1px代表屏幕上的一个物理像素点. 2.dp dip:device independent pixels(设备独立像素),dp与dip一样,不同的设备有不同的显示效果,一般为了支持W ...

  9. 深入学习jQuery鼠标事件

    × 目录 [1]类型 [2]写法 [3]合成事件[4]鼠标按键[5]修改键[6]坐标位置 前面的话 鼠标事件是DOM事件中最常用的事件,jQuery对鼠标事件进行了封装和扩展.本文将详细介绍jQuer ...

  10. 引用类型-Array类型

    ECMAScript数组的每一项可以保存任何类型的数据,并且数组的大小是可以动态调整的. 创建数组的基本方式有两种,第一种是使用Array构造函数 var colors = new Array(); ...