创建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)-添加查询功能 & 新字段的更多相关文章

  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. radio相关

    radio 按钮组, name=”sex”. <input type="radio" name="sex" value="Male"& ...

  2. Linux 部署ASP.NET SQLite 应用 的坎坷之旅 附demo及源码

    Linux 部署ASP.NET SQLite 应用 的坎坷之旅.文章底部 附示例代码. 有一台闲置的Linux VPS,尝试着部署一下.NET 程序,结果就踏上了坑之路,不过最后算是完美解决问题,遂记 ...

  3. CSS3魔法堂:CSS3滤镜及Canvas、SVG和IE滤镜替代方案详解

    一.前言    IE特有的滤镜常常作为CSS3各种新特性的降级处理补充,而Adobe转向HTML5后与Chrome合作推出CSS3的Filter特性,因此当前仅Webkit内核的浏览器支持CSS3 F ...

  4. ASP.NET MVC 5 Web编程1 -- 入门

    开篇引言 说起ASP.NET MVC,我想作为WebForms开发者第一点要问的是:为什么要使用它?我的理解是:MVC是更细节化的框架,“细节可控”意味着你的系统更精致.具体体现在应用上.MVC的出现 ...

  5. SQL Server 事务隔离级别详解

    标签: SQL SEERVER/MSSQL SERVER/SQL/事务隔离级别选项/设置数据库事务级别 SQL 事务隔离级别 概述 隔离级别用于决定如果控制并发用户如何读写数据的操作,同时对性能也有一 ...

  6. EF:打开Oracle连接时报错

    基础提供程序在 Open 上失败. The underlying provider failed on Open. 解决:安装最新的ODTwithODAC121024.

  7. LeetCode Note 1st,practice makes perfect

    1. Two Sum Given an array of integers, return indices of the two numbers such that they add up to a ...

  8. iOS---后台运行机制详解

    一.iOS的“伪后台”程序 首先,先了解一下iOS 中所谓的「后台进程」到底是怎么回事吧? Let me be as clear as I can be: the iOS multitasking b ...

  9. uwp如何建立任何形状的头像,如圆形,方形,六边形等

    最近掌上英雄联盟更新了新的界面,其中“我”界面的更新比较大,我目前还在加紧跟进.在做这个界面的时候,这个头像我想了一下,其实挺好解决的.先上个原图 这个头像一开始我也完全找不到头绪,然后我把头像放大了 ...

  10. MacOS中使用QT开发iOS应用

    因为项目合同中规定一部分业务内容要在手机端实现,包括安卓机和苹果机,因此选择了QT作为开发工具.程序在Win10和安卓系统上已经完美运行,这几天开始搭建iOS的编译和发布环境,因为以前没有使用过mac ...