EntityFrameworkCore Like

本文探讨了Entity Framework Core中Like查询的使用,与StartsWith、Contains方法的区别,以及在通配符查询中的索引优化策略。重点揭示了EF6中的通配符处理方式和模糊查询背后的逻辑。

Entity Framework Core Like 查询揭秘

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

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

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,这样可以直接运行对应的示例,并且也可以直接输出对应的结果。
我们来看一下运行的结果:

EF.Functions.Like

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

字符串匹配模式

在这里,我暂且将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”** ,运行的结果如下:

EF Contains

运行结果与 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 查询示例:

EF.Functions.Like

Contains 查询示例:
EF 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-c],前面多了一个特殊符号**~,并且查询子句的后面还多了一部分 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中,模糊查询不支持通配符,这一点是因为我没有找到对应的解决方案

例子:

using Microsoft.EntityFrameworkCore;

public void GetUserinfo()
{
   _dbContext.UserInfos.Where(c => EF.Functions.Like(c.UserName, "%风%")).ToList();
}
<think># Entity Framework Core 与 Entity Framework 6 的主要区别 Entity Framework Core (EF Core) 是 Entity Framework 的现代化轻量级版本,与 EF6 相比有重大架构变化。以下是它们的主要区别: ## 1. 平台支持 | 特性 | EF6 | EF Core | |------|-----|---------| | .NET Framework | 支持 | 支持 (仅部分版本) | | .NET Core | 不支持 | **完全支持** | | .NET 5+ | 不支持 | **完全支持** | | UWP/Xamarin | 不支持 | **支持** | | Linux/macOS | 不支持 (通过Mono) | **原生支持** | ## 2. 架构设计 - **EF6**: - 单一架构 - 紧密耦合的组件 - 遗留代码负担 - **EF Core**: - **模块化架构** - 轻量级 - 可扩展性强 - 独立组件 (如查询、变更跟踪等) ## 3. 数据库提供程序 ```csharp // EF6 - 配置提供程序 public class MyConfiguration : DbConfiguration { public MyConfiguration() { SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance); } } // EF Core - 配置提供程序 (Startup.cs) services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); ``` 区别: - **EF6** 提供程序通过DbConfiguration注册 - **EF Core** 提供程序通过UseXxx方法链式配置 - **EF Core** 有更广泛的数据库支持 (SQLite, PostgreSQL, MySQL等) ## 4. 查询功能 | 功能 | EF6 | EF Core | |------|-----|---------| | LINQ to Entities | 支持 | **增强** | | 原始SQL查询 | 支持 | **支持(更灵活)** | | 包含延迟加载 | 支持 | 支持(需额外配置) | | 全局查询过滤器 | 不支持 | **支持** | | 从属实体查询 | 有限支持 | **改进支持** | ### EF Core 全局查询过滤器示例 ```csharp protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().HasQueryFilter(b => !b.IsDeleted); } ``` ## 5. 变更跟踪 - **EF6**: - 自动变更跟踪 - 基于快照的变更跟踪 - 需要继承EntityObject - **EF Core**: - **支持多种变更跟踪策略** - 轻量级变更跟踪 - **支持无键实体** - **支持更简单的POCO类** ## 6. 关系映射 | 关系类型 | EF6 | EF Core | |----------|-----|---------| | 一对一 | 支持 | **改进支持** | | 一对多 | 支持 | 支持 | | 多对多 | 支持 | **需要配置连接实体** | | 表拆分 | 支持 | **支持** | | 继承映射 (TPH/TPT/TPC) | 支持 | **TPT在EF Core 5+支持** | ### EF Core多对多配置示例 ```csharp modelBuilder.Entity<Post>() .HasMany(p => p.Tags) .WithMany(t => t.Posts) .UsingEntity(j => j.ToTable("PostTags")); ``` ## 7. 迁移操作 | 功能 | EF6 | EF Core | |------|-----|---------| | 迁移命令 | `Add-Migration` | `dotnet ef migrations add` | | 更新数据库 | `Update-Database` | `dotnet ef database update` | | 脚本生成 | 支持 | **增强** | | 迁移文件存储 | 项目内 | 项目内 | | 空迁移 | 支持 | 支持 | | 模型快照 | 无 | **有** | ## 8. 性能比较 - **EF Core性能显著优于EF6**: - 查询执行快100倍 (特定场景) - 更少的内存占用 - 优化的LINQ查询转换 - 批量操作支持 (EF Core 5+) ### 批量更新示例 (EF Core 5+) ```csharp // 传统方式 (EF6和EF Core旧版本) foreach (var blog in context.Blogs.Where(b => b.Rating < 3)) { blog.Rating = 5; } context.SaveChanges(); // 每条记录生成单独的UPDATE语句 // EF Core 5+ 批量更新 context.Blogs .Where(b => b.Rating < 3) .ExecuteUpdate(b => b.SetProperty(x => x.Rating, 5)); // 生成单个UPDATE语句 ``` ## 9. 其他关键区别 | 领域 | EF6 | EF Core | |------|-----|---------| | 连接弹性 | 有限 | **内置重试策略** | | 并发处理 | 支持 | **改进支持** | | 值转换器 | 不支持 | **支持** | | 阴影属性 | 不支持 | **支持** | | 模型构建API | 基于DataAnnotations | **Fluent API为主** | ## 10. 何时选择哪个? **选择 EF6 当**: - 维护现有.NET Framework应用 - 需要完整功能集而EF Core尚未支持 - 依赖EF6特定特性(如自动编译查询) **选择 EF Core 当**: - 开发新应用 - 需要跨平台支持 - 追求高性能 - 使用.NET Core/.NET 5+ ## 迁移策略 从EF6迁移到EF Core的步骤: 1. **评估兼容性**:检查EF Core特性支持 2. **创建新项目**:使用.NET Core/5+项目 3. **逐步迁移实体**: ```csharp // EF6 public class Blog { [Key] public int BlogId { get; set; } public string Url { get; set; } } // EF Core public class Blog { public int BlogId { get; set; } public string Url { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().HasKey(b => b.BlogId); } ``` 4. 重写数据访问层 5. 使用兼容性包处理特殊情况 §§相关问题§§: 1. 如何将现有项目从Entity Framework 6迁移到Entity Framework Core? 2. Entity Framework Core中如何实现多对多关系映射? 3. EF Core的批量操作具体有哪些性能优势? 4. 在EF Core中如何配置和使用连接弹性? </think> # Entity Framework Core 与 Entity Framework 6 的主要区别 Entity Framework Core (EF Core) 是 Entity Framework 的现代化轻量级版本,与 EF6 相比有重大架构变化。以下是两者的主要区别: ## 1. 平台支持 | 特性 | EF6 | EF Core | |------|-----|---------| | .NET Framework | ✅ 支持 | ✅ 支持 (部分版本) | | .NET Core | ❌ 不支持 | ✅ 完全支持 | | .NET 5/6/7+ | ❌ 不支持 | ✅ 完全支持 | | Xamarin/UWP | ❌ 不支持 | ✅ 支持 | | Linux/macOS | ⚠️ 有限支持 | ✅ 完全支持 | ## 2. 架构设计差异 ```csharp // EF6 基于EDMX设计器 (Database First) // 自动生成大量模板代码,紧密耦合 // EF Core 纯代码优先 public class Product { public int Id { get; set; } public string Name { get; set; } } public class AppDbContext : DbContext { public DbSet<Product> Products { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 流畅API配置 modelBuilder.Entity<Product>() .Property(p => p.Name) .IsRequired() .HasMaxLength(100); } } ``` **关键区别**: - EF6 使用 `ObjectContext` 和 `DbContext` API - EF Core 使用更简洁的 `DbContext` 和 `ModelBuilder` - EF Core 移除了EDMX设计器,完全采用代码配置 ## 3. 查询功能对比 ### LINQ查询差异示例 ```csharp // EF6 - 支持部分客户端求值 var results = context.Products .Where(p => p.Name.StartsWith("A")) .ToList(); // 可能在客户端执行部分操作 // EF Core - 更严格的服务器端求值 var results = context.Products .Where(p => EF.Functions.Like(p.Name, "A%")) // 显式使用数据库函数 .ToList(); ``` **查询特性对比**: | 功能 | EF6 | EF Core | |------|-----|---------| | 延迟加载 | ✅ | ✅ (需安装`Microsoft.EntityFrameworkCore.Proxies`) | | 贪婪加载 | `Include()` | `Include()` + `ThenInclude()` | | 原始SQL查询 | `SqlQuery()` | `FromSqlRaw()` / `FromSqlInterpolated()` | | 全局查询过滤器 | ❌ | ✅ | | 编译查询 | ✅ | ✅ (更高效) | ## 4. 迁移管理 ### EF6 迁移命令 ```powershell Add-Migration InitialCreate Update-Database ``` ### EF Core 迁移命令 ```bash dotnet ef migrations add InitialCreate dotnet ef database update ``` **迁移差异**: - EF6 依赖Visual Studio包管理器控制台 - EF Core 支持跨平台CLI命令 - EF Core 迁移文件更简洁,支持空迁移 - EF Core 5+ 支持多提供程序迁移 ## 5. 关系映射差异 ### 多对多关系实现 ```csharp // EF6 - 自动创建中间表 public class Student { public ICollection<Course> Courses { get; set; } } public class Course { public ICollection<Student> Students { get; set; } } // EF Core - 需要显式配置 modelBuilder.Entity<StudentCourse>(sc => { sc.HasKey(s => new { s.StudentId, s.CourseId }); }); modelBuilder.Entity<Student>() .HasMany(s => s.Courses) .WithMany(c => c.Students) .UsingEntity<StudentCourse>(); ``` **关系支持**: | 关系类型 | EF6 | EF Core | |----------|-----|---------| | 一对一 | ✅ | ✅ | | 一对多 | ✅ | ✅ | | 多对多 | ✅ | ✅ (EF Core 5+) | | 表拆分 | ✅ | ✅ | | 继承策略 | TPH/TPT | TPH/TPT (EF Core 5+)/TPC (EF Core 7+) | ## 6. 性能优化 ### EF Core 批量操作 (EF Core 7+) ```csharp // 批量删除 context.Products .Where(p => p.Price < 10) .ExecuteDelete(); // 批量更新 context.Products .Where(p => p.Price < 10) .ExecuteUpdate(s => s.SetProperty(p => p.Price, p => p.Price * 1.1)); ``` **性能对比**: - EF Core 查询效率比 EF6 提高 2-10 倍 - EF Core 支持批量操作,减少数据库往返 - EF Core 内存占用更低 - EF Core 启动时间更短 ## 7. 其他关键区别 | 特性 | EF6 | EF Core | |------|-----|---------| | 变更跟踪 | 自动 | 可选择跟踪策略 | | 并发控制 | 行版本 | 行版本/并发令牌 | | 值转换器 | ❌ | ✅ (如存储枚举为字符串) | | 阴影属性 | ❌ | ✅ | | 连接弹性 | ❌ | ✅ (自动重试) | | 拥有实体 | ❌ | ✅ (复杂类型) | ## 8. 何时选择哪个? ### 选择 EF6 当: - 维护现有 .NET Framework 4.x 应用 - 需要 EDMX 设计器支持 - 依赖 EF6 特有功能(如空间类型) ### 选择 EF Core 当: - 开发新应用程序 - 需要跨平台支持 - 追求高性能 - 使用 .NET Core/5/6/7+ - 需要最新功能(如批量操作) ## 迁移路径 ```csharp // 从EF6迁移到EF Core步骤: // 1. 创建新EF Core项目 // 2. 逐步迁移实体类 // 3. 重写数据访问层 // 4. 使用兼容包处理特殊情况 // 安装兼容包 Install-Package Microsoft.EntityFrameworkCore.SqlServer ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值