在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. Linux(7)chmod解析

    在UNIX和Linux的操作系统中, 每个文件(文件夹也被看作是文件)都按读, 写, 运行设定权限 比如用ls -l或ll命令列文件表时, 得到如下输出: -rw-r--r-- 1 apple use ...

  2. 解决oracle数据库删除sql语句出现^H字样

    1:安装readline包 yum install readline* 2:安装源码包: rlwrap-0.30.tar.gz    ./configure && make & ...

  3. POJ3614 Sunscreen 优先队列+贪心

    Description To avoid unsightly burns while tanning, each of the C (1 ≤ C ≤ 2500) cows must cover her ...

  4. React Native 系列(六) -- PropTypes

    前言 本系列是基于React Native版本号0.44.3写的.在我们之前的通过props实现组件间传值的时候,大家有没有发现在父组件传递值过去,在子控件获取props的时候没有提示,那么如何能实现 ...

  5. hdu 6121---Build a tree(深搜+思维)

    题目链接 Problem Description HazelFan wants to build a rooted tree. The tree has n nodes labeled 0 to n− ...

  6. 【机器学习笔记之四】Adaboost 算法

    本文结构: 什么是集成学习? 为什么集成的效果就会好于单个学习器? 如何生成个体学习器? 什么是 Boosting? Adaboost 算法? 什么是集成学习 集成学习就是将多个弱的学习器结合起来组成 ...

  7. Linux操作系统-安装JAVA

    首先准备好jdk文件(例如:本地已下载了jdk-6u33-linux-x64.bin),将它上传到路径 “/home/username/” 这个目录的下面 其次按步骤执行: 1.进入到 “/home/ ...

  8. 使用VIEWER.JS进行简单的图片预览

    <script src="../res/js/viewer.min.js"></script><script type="text/java ...

  9. javaweb 登陆注册页面

    视图的数据修改,表中也修改引用工具类用<%@ page import=""%> <%@ page import="java.util.Date" ...

  10. WAV文件格式

    作者:阿宝 更新:2016-09-21 来源:彩色世界(https://blog.hz601.org/2016/09/21/waveform-audio-file-format/index.html) ...