创建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. 安装ant

    从ant官方网站下载ant安装包:apache-ant-1.9.7-bin.tar.gz,解压 tar xvf apache-ant-1.9.7-bin.tar.gz -C /usr/java/ 配置 ...

  2. HTML5- Canvas入门(三)

    前两章我们掌握了线段.矩形和多边形的绘制方法,今天我们主要是学习如何绘制圆弧和贝塞尔曲线. 圆弧的绘制 圆弧可以理解为一个圆上的某部分线段,在canvas中,绘制一条圆弧的语法如下: ctx.arc( ...

  3. Python黑帽编程 2.0 第二章概述

    Python黑帽编程 2.0 第二章概述 于 20世纪80年代末,Guido van Rossum发明了Python,初衷据说是为了打发圣诞节的无趣,1991年首次发布,是ABC语言的继承,同时也是一 ...

  4. Go语言实战 - revel框架教程之MongDB的最佳搭档revmgo

    由于revel框架本身对于model层的编写没有提供任何指导,所以在设计这部分的时候就有些犹豫,反复斟酌到底怎样才算是最佳实践. 我在做山坡网的时候刚开始也纠结了一下,拿不准mongodb的sessi ...

  5. 如何使用Worktile进行敏捷项目开发管理

    Worktile在任务管理上采用了看板视图,非常适合进行敏捷项目开发管理.事实上,在开发Worktile的过程中,我们也是自产自销,使用Worktile管理Worktile本身的开发过程,在本文中跟大 ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...

  7. Android-webview和js互相调用

    Android-webview和js互相调用 Android 和 H5 都是移动开发应用的非常广泛.市面上很多App都是使用Android开发的,但使用Android来开发一些比较复杂附属类,提示性的 ...

  8. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  9. WCF学习之旅—WCF第二个示例(五)

    二.WCF服务端应用程序 第一步,创建WCF服务应用程序项目 打开Visual Studio 2015,在菜单上点击文件—>新建—>项目—>WCF服务应用程序.在弹出界面的“名称”对 ...

  10. c#字段

    1.C#中是不允许用new操作符创建String对象的,编译器会报错. 2.实例字段:使用new对class创建对象直接赋初始值,不适用于struct slass Rect { public int ...