在Entity Framework Core 2.0中增加一个很酷的功能:EF.Functions.Like(),最终解析为SQL中的Like语句,以便于在 LINQ 查询中直接调用。

不过Entity Framework 中默认提供了StartsWithContainsEndsWith方法用于解决模糊查询,那么为什么还要提供EF.Functions.Like,今天我们来重点说说它们之间的区别。

表结构定义

在具体内容开始之前,我们先简单说明一下要使用的表结构。

    public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; } public override string ToString()
{
return $"{nameof(CategoryID)}: {CategoryID}, {nameof(CategoryName)}: {CategoryName}";
}
}

Category 类型定义了两个字段:CategoryIDCategoryName

    public class SampleDbContext : DbContext
{
public virtual DbSet<Category> Categories { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("数据库连接字符串");
base.OnConfiguring(optionsBuilder);
} protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
EntityTypeBuilder<Category> entityTypeBuilder = modelBuilder.Entity<Category>();
entityTypeBuilder.ToTable("Category");
entityTypeBuilder.HasKey(e => e.CategoryID);
entityTypeBuilder.Property(e => e.CategoryID).UseSqlServerIdentityColumn();
}
}

我们使用 SampleDbContext 来访问数据库。

CategoryID CategoryName
1 Clothing
2 Footwear
3 Accessories

在数据库的 Category 表中插入上面三行记录。

EF.Functions.Like 使用示例

我们来看一个EF.Functions.Like()查询示例,查询 CategoryName 字段中包括字符串 “t” 的数据,传递的参数是 “%t%”

        [Fact]
public void Like()
{
using (var dataContext = new SampleDbContext()) { var result= dataContext.Categories.Where(item => EF.Functions.Like(item.CategoryName, "%t%")).ToList(); foreach (var item in result) {
_testOutputHelper.WriteLine(item.CategoryName);
}
}
}

提示:在做一些示例演示时,个人喜欢会用 Xunit + Resharper,这样可以直接运行对应的示例,并且也可以直接输出对应的结果。

我们来看一下运行的结果:

查询的结果包含两条数据,这与我们预期结果一致。

字符串匹配模式

在这里,我暂且将StartsWithContainsEndsWith方法称之为字符串匹配模式

您肯定在Entity Framework中使用过这些方式,我们还是简单说明一下这三个方法的作用:

  • StartsWith:表示字符串的开头是否与指定的字符串匹配;
  • Contains:表示指定的子串是否出现在此字符串中;
  • EndsWith:表示字符串的结尾是否与指定的字符串匹配;

我们可以通过Contains方法实现与前一个示例一致的功能:

        [Fact]
public void Contains()
{
using (var dataContext = new SampleDbContext())
{
var result = dataContext.Categories.Where(item => item.CategoryName.Contains("t")).ToList(); foreach (var item in result)
{
_testOutputHelper.WriteLine(item.CategoryName);
} }
}

我们在Contains方法转入参数“t” ,运行的结果如下:

运行结果与 Like 函数示例的结果是一致的。

在这里我只列举了Contains的示例,StartsWithEndsWith的功能非常相似,我就不重复列举了。

这两个示例的运行结果是一致的,那么微软为什么要提供EF.Functions.Like()方法呢?

通配符模糊查询

我们知道在 T-SQL 语句中 Like 关键字支持 通配符 ,下面简单介绍支持的通配符:

通配符 说明 示例
% 包含零个或多个字符的任意字符串。 WHERE title LIKE '%computer%' 将查找在书名中任意位置包含单词 "computer" 的所有书名。
_(下划线) 任何单个字符。 WHERE au_fname LIKE '_ean' 将查找以 ean 结尾的所有 4 个字母的名字(Dean、Sean 等)。
[ ] 指定范围 ([a-f]) 或集合 ([abcdef]) 中的任何单个字符。 WHERE au_lname LIKE '[C-P]arsen' 将查找以 arsen 结尾并且以介于 C 与 P 之间的任何单个字符开始的作者姓氏,
例如 Carsen、Larsen、Karsen 等。
[^] 不属于指定范围 ([a-f]) 或集合 ([abcdef]) 的任何单个字符。 WHERE au_lname LIKE 'de[^l]%' 将查找以 de 开始并且其后的字母不为 l 的所有作者的姓氏。

关于 Like 和通配符更多的知识请直接到MSDN中了解,链接地址:https://msdn.microsoft.com/zh-cn/library/ms179859(v=sql.110).aspx

我们的将查询关键字由 “t” 改为 “[a-c]”,再来看上面两个示例分别运行的结果:

EF.Functions.Like 查询示例:

Contains 查询示例:

上面运行的结果,Like 查询的结果返回三条记录,而 Contains 查询的结果无任何数据返回。

我们借助 SQL Server Profiler 分别捕获这两个示例实际生成的SQL查询。

EF.Functions.Like 查询生成的SQL语句:

    SELECT [item].[CategoryID], [item].[CategoryName]
FROM [Category] AS [item]
WHERE [item].[CategoryName] LIKE N'%[a-c]%'

Contains 查询生成的SQL语句:

    SELECT [item].[CategoryID], [item].[CategoryName]
FROM [Category] AS [item]
WHERE CHARINDEX(N'[a-c]', [item].[CategoryName]) > 0

通过上面示例以及捕获的SQL,我们可以得知,EF.Functions.Like() 查询会被解释成为 Like,实际上是查询字符串中包括 “a”、“b”、“c” 这三个字符中任何一个字符的数据,而使用 Contains 查询会被解析成为 CharIndex 函数,实际是指查询字符串中包括 “[a-c]” 的字符串。

提示: StartsWithEndsWith分别会被解析成为LeftRight函数,测试结果在这里不再做重复演示。

结论: 在EF Core中提供EF.Functions.Like()方法的根本原因是在 TSQL 语句中 Like 关键字支持通配符,而在.Net中StartsWithContainsEndsWith方法是不支持通配符的;

在EF Core中StartsWithContainsEndsWith模糊查询实际分别被解析成为LeftCharIndexRight,而不是Like

其它要点

通过上面的示例我们已经说清楚了EF.Functions.Like()方法和StartsWithContainsEndsWith方法之间的区别,但是还有以下两点需要说明。

EF Core StartsWith 优化

如果使用StartWith方法来实现模糊查询,解析后的SQL语句会包括一个Like查询,您可能要说,刚才不是已经讲过吗,StartsWithContainsEndsWith方法解析后的SQL不是通过 Like 来查询!先不要着急,我下面来说清楚这个问题。

StartsWith 查询示例:

        [Fact]
public void StartsWith()
{
using (var dataContext = new SampleDbContext())
{
var result = dataContext.Categories.Where(item => item.CategoryName.StartsWith("Clo")).ToList(); foreach (var item in result)
{
_testOutputHelper.WriteLine(item.CategoryName);
}
}
}

借助 SQL Server Profiler 捕获实际生成的SQL查询:

    SELECT [item].[CategoryID], [item].[CategoryName]
FROM [Category] AS [item]
WHERE [item].[CategoryName] LIKE N'Clo' + N'%' AND (LEFT([item].[CategoryName], LEN(N'Clo')) = N'Clo')

在SQL语句中,即用到了Like,也用到Left函数,这是为什么呢?

您可能知道在数据库查询时,如果在某一个字段上使用函数是无法利用到索引的;在使用LeftCharIndexRight时是无法利用到索引的;而Like查询在百分号后置的情况下会利用到索引。关于数据库的这些知识,在博客园上有很多文章,我就不重复说明了。

结论: StartsWith模糊查询解析后的SQL用到Like,这是因为Like在百分号后置的是情况下会利用到索引,这样查询速度会更快。ContainsEndsWith模糊查询解析后的SQL不包括Like查询,因为在分百号前置的情况无法引用到索引。

关于ContainsEndsWith模糊查询的测试,在这里不再重复,您可以自己测试。

EF 6

在EF 6中,模糊查询解析后的SQL语句与EF Core中略有不同,但是执行的结果没有区别。

我们在EF 6中分别捕获StartsWithContainsEndsWith解析后的SQL语句,不过我们搜索的关键字是:“[a-c]”,包含通配符。

StartsWith 查询生成的SQL语句:

SELECT
[Extent1].[CategoryID] AS [CategoryID],
[Extent1].[CategoryName] AS [CategoryName]
FROM [dbo].[Category] AS [Extent1]
WHERE [Extent1].[CategoryName] LIKE N'~[a-c]%' ESCAPE N'~'

Contains 查询生成的SQL语句:

SELECT
[Extent1].[CategoryID] AS [CategoryID],
[Extent1].[CategoryName] AS [CategoryName]
FROM [dbo].[Category] AS [Extent1]
WHERE [Extent1].[CategoryName] LIKE N'%~[a-c]%' ESCAPE N'~'

EndsWith 查询生成的SQL语句:

SELECT
[Extent1].[CategoryID] AS [CategoryID],
[Extent1].[CategoryName] AS [CategoryName]
FROM [dbo].[Category] AS [Extent1]
WHERE [Extent1].[CategoryName] LIKE N'%~[a-c]' ESCAPE N'~'

StartsWithContainsEndsWith方法均会被解析为Like查询,但是是传递的参数由:“[a-c]”变为了[a-b]”**,前面多了一个特殊符号**“,并且查询子句的后面还多了一部分 ESCAPE N'~'

在MSDN上面有关ESCAPE关键字的解释,我们摘取其中一部分来说明:

使用 ESCAPE 子句的模式匹配

搜索包含一个或多个特殊通配符的字符串。 例如,customers 数据库中的 discounts 表可能存储含百分号 (%) 的折扣值。 若要搜索作为字符而不是通配符的百分号,必须提供 ESCAPE 关键字和转义符。 例如,一个样本数据库包含名为 comment 的列,该列含文本 30%。 若要搜索在 comment 列中的任何位置包含字符串 30% 的任何行,请指定 WHERE comment LIKE '%30!%%' ESCAPE '!' 之类的 WHERE 子句。 如果未指定 ESCAPE 和转义符,则数据库引擎将返回包含字符串 30 的所有行。

如果您想了解EF 6是如果过滤这些通配符的,可以在Github上面了解,链接地址:https://github.com/aspnet/EntityFramework6/blob/6.1.3/src/EntityFramework.SqlServer/SqlProviderManifest.cs#L164-L189

结论:在EF 6中StartsWithContainsEndsWith方法均会被解析为Like查询,但是如果出现了通配符,框架会结合ESCAPE以及自身过滤功能将参数进行转义。

总结

通过上面的叙述,我们可以得到如下一些结论:

  • 在EF Core中提供EF.Functions.Like()方法的根本原因是在 TSQL 语句中 Like 关键字支持通配符,而在.Net中StartsWithContainsEndsWith方法是不支持通配符的;
  • 在EF Core中StartsWithContainsEndsWith模糊查询分别被解析成为LeftCharIndexRight,而不是Like
  • 在EF Core中StartsWith模糊查询解析后的SQL用到Like,这是因为Like在百分号后置的是情况下会利用到索引,这样查询速度会更快;
  • 在EF 6中,StartsWithContainsEndsWith方法均会被解析为Like查询,但是如果出现了通配符,框架会结合ESCAPE以及自身过滤功能将参数进行转义;
  • 在EF 6中,模糊查询不支持通配符,这一点是因为我没有找到对应的解决方案,如果您知道,请留言,谢谢!

作者:Sweet Tang

本文地址:http://www.cnblogs.com/tdfblog/p/entity-framework-core-like-query.html

欢迎转载,请在明显位置给出出处及链接。

Entity Framework Core Like 查询揭秘的更多相关文章

  1. Entity Framework Core 软删除与查询过滤器

    本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ...

  2. 使用Entity Framework Core需要注意的一个全表查询问题

    .NET Core 迁移工作如火如荼,今天在使用 Entity Frameowork Core(又名EF Core)时写了下面这样的 LINQ 查询表达式: .Where(u => u.Id = ...

  3. 【EF】Entity Framework Core 软删除与查询过滤器

    本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ...

  4. Entity Framework Core 1.1 升级通告

    原文地址:https://blogs.msdn.microsoft.com/dotnet/2016/11/16/announcing-entity-framework-core-1-1/ 翻译:杨晓东 ...

  5. Entity Framework Core 1.1 Preview 1 简介

    实体框架核心(EF Core)是Entity Framework的一个轻量级,可扩展和跨平台版本. 10月25日,Entity Framework Core 1.1 Preview 1发布了. 升级到 ...

  6. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 读取关系数据

    Reading related data¶ 9 of 9 people found this helpful The Contoso University sample web application ...

  7. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 创建复杂数据模型

    Creating a complex data model 创建复杂数据模型 8 of 9 people found this helpful The Contoso University sampl ...

  8. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 排序、筛选、分页以及分组

    Sorting, filtering, paging, and grouping 7 of 8 people found this helpful By Tom Dykstra The Contoso ...

  9. Working with Data » 使用Visual Studio开发ASP.NET Core MVC and Entity Framework Core初学者教程

    原文地址:https://docs.asp.net/en/latest/data/ef-mvc/intro.html The Contoso University sample web applica ...

随机推荐

  1. 微信小程序-滚动消息通知效果

    这次我主要想总结一下微信小程序实现上下滚动消息提醒,主要是利用swiper组件来实现,swiper组件在小程序中是滑块视图容器. 我们通过vertical属性(默认为false,实现默认左右滚动)设置 ...

  2. 踩坑之路_"var name = ' ';"_迷之BUG

    情景介绍:最近写一个拖拽生成图表的工具,自己的思路每次mousedown的时候动态将this的name属性值赋值给全局中变量(自己手贱测试时直接将变量名命名为了'name',一大波bug还有30s到达 ...

  3. 【整理】01. Fiddler 杂记

    抓手机包步骤: Tools -- Fiddler Options -- Connections (默认)Fiddler listens on port:8888 (勾选)Allow remote co ...

  4. Jetty的工作原理

    Jetty的工作原理 Jetty 的基本架构 Jetty 目前的是一个比较被看好的 Servlet 引擎,它的架构比较简单,也是一个可扩展性和非常灵活的应用服务器,它有一个基本数据模型,这个数据模型就 ...

  5. 用Django做一个省份选择器

    做一个省份选择器 使用django做后端, mysql数据库, jQuery 列出结构主要的文件, 其它配置比较简单 models.py 因为所有数据的结构基本一致, 把所有省份, 市和区全部存储一张 ...

  6. EXT系统中的信息查询

    if (Ext.getCmp('rdPlaceLocation').checked) { choseRoad = false; var placeLocationWindow = Ext.getCmp ...

  7. Log4net快速配置使用指南。(快速搭建log4net日志平台手册)

    每做一个新项目,都会用到log4net,但总是忘记如何快速配置.有时在网上搜半天也找不到好的模板,大都在介绍参数的使用,在此做下总结,争取下次用时仅10分钟就可搭建好log4net. 直接上介绍的步骤 ...

  8. noip冲刺赛第五次考试

    1.公约数 (gcd.cpp\c\pas) [问题描述] 给定一个正整数,在[1,n]的范围内,求出有多少个无序数对(a,b)满足 gcd(a,b)=a xor b. [输入格式] 输入共一行,一个正 ...

  9. connect by prior 递归算法

    http://blog.163.com/xxciof/blog/static/7978132720095193113752/ oracle中 connect by prior 递归算法 Oracle中 ...

  10. MAC下安装MAMP的PHPredis扩展

    下载phpredis扩展安装包.git clone https://github.com/nicolasff/phpredis.git: 解压后,进入该目录: 依次执行以下操作完成安装: /Appli ...