在 EF Core 中编写不区分大小写的搜索查询?
Write a search query which is case insensitive in EF Core?
我想问一个关于 SQL Server 和 EF Core 的问题。数据库中的排序规则是 Latin1_CI_AS
,我想编写一个包含土耳其语字符的搜索查询。
在数据库中,'personnel'table中有一条名为"SELİM"的记录。当我在 EF Core 中编写这样的查询时:
public async Task<IList<PersonnelGetDto>> Get(PersonnelGetPayload payload)
{
if (payload.Name != null)
query = query.Where(x => x.Name.Contains(payload.Name));
}
如果我的搜索条件是"selim",则列表为空。
我没有机会将数据库中的排序规则更改为土耳其语,因为我们的应用程序是多语言的。我认为其他语言会有问题。还是我错了?
我也写了字符串扩展。但是,当将 LINQ 查询转换为 SQL 时,所有记录都会到达服务层,因为 LIKE 运算符没有分配 WHERE 子句。在sql这边运行这个条件很重要。如果我把所有的数据集都拿到服务层去查询,我的成本会很高
当我像这样在数据库中键入查询时,我可以解决问题:
SELECT * FROM Personnel WHERE Name LIKE 'selim' COLLATE Turkish_CI_AS
我想如果我可以在 EF Core 上操作整理,我就会解决问题。
您在 EF.Functions.Like 之后,这需要添加 using Microsoft.EntityFrameworkCore;
,如果您还没有的话。然后,您的查询将类似于:
query.Where(x => EF.Functions.Like(x.Name, $"%{payload.Name}%"))
这直接转换为生成的 SQL 语句中的 LIKE
运算符。它并非适用于每个 DBMS,但只要您添加了 Microsoft.EntityFrameworkCore.SqlServer,您就可以开始了(假设您的问题已正确标记)。
我已经测试了 like 函数,但结果并不像操作说明的那样正确。所以只剩下一个选项了。即创建拦截器并实现自定义逻辑。我创建了一个如下示例:
public class Suffixes
{
public const string Collate = "--Collate";
}
public class CollationDbCommandInterceptor : DbCommandInterceptor
{
private const string CollateSyntax = " collate turkish_ci_as";
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
var args = command.Parameters.OfType<DbParameter>()
.Where(t => t.DbType == DbType.String && t.Value.ToString().EndsWith(Suffixes.Collate)).ToList();
if (args.Count <= 0)
return base.ReaderExecuting(command, eventData, result);
foreach (var parameter in args)
{
parameter.Value = parameter.Value.ToString().Replace(Suffixes.Collate, "");
var equality = $"= {parameter.ParameterName}";
var ixs = AllIndexesOf(command.CommandText, equality);
#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
foreach (var eq in ixs)
{
command.CommandText = command.CommandText.Insert(eq+equality.Length,CollateSyntax);
}
#pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
}
return base.ReaderExecuting(command, eventData, result);
}
private static IEnumerable<int> AllIndexesOf(string str, string value)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("the string to find may not be empty", nameof(value));
var indexes = new List<int>();
for (var index = 0; ; index += value.Length)
{
index = str.IndexOf(value, index);
if (index == -1)
return indexes;
indexes.Insert(0,index);
}
}
}
配置:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
....
optionsBuilder.AddInterceptors(new CollationDbCommandInterceptor());
....
}
}
用法:
var kadayif = $"kadayıf{Suffixes.Collate}";
var william = $"Wİlliam{Suffixes.Collate}";
var auths = ctx.Authors.Where(t => t.FirstName == william ||t.LastName == kadayif).ToList();
// returns William Shakespeare and Abuzer Kadayıf
逻辑是创建一个拦截器,在查询中传递的 sql 参数中寻找特定后缀。将查询特定的排序规则注入最终的 sql 命令文本。我试图涵盖一些高级场景,例如参数重用。它可能需要更多改进。
请注意,此示例适用于 Entity Framework Core 3.0,即拦截器引入的版本。在早期的 ef core 版本中拦截是一个小技巧。您可以参考 this link 了解更多信息。
我想问一个关于 SQL Server 和 EF Core 的问题。数据库中的排序规则是 Latin1_CI_AS
,我想编写一个包含土耳其语字符的搜索查询。
在数据库中,'personnel'table中有一条名为"SELİM"的记录。当我在 EF Core 中编写这样的查询时:
public async Task<IList<PersonnelGetDto>> Get(PersonnelGetPayload payload)
{
if (payload.Name != null)
query = query.Where(x => x.Name.Contains(payload.Name));
}
如果我的搜索条件是"selim",则列表为空。
我没有机会将数据库中的排序规则更改为土耳其语,因为我们的应用程序是多语言的。我认为其他语言会有问题。还是我错了?
我也写了字符串扩展。但是,当将 LINQ 查询转换为 SQL 时,所有记录都会到达服务层,因为 LIKE 运算符没有分配 WHERE 子句。在sql这边运行这个条件很重要。如果我把所有的数据集都拿到服务层去查询,我的成本会很高
当我像这样在数据库中键入查询时,我可以解决问题:
SELECT * FROM Personnel WHERE Name LIKE 'selim' COLLATE Turkish_CI_AS
我想如果我可以在 EF Core 上操作整理,我就会解决问题。
您在 EF.Functions.Like 之后,这需要添加 using Microsoft.EntityFrameworkCore;
,如果您还没有的话。然后,您的查询将类似于:
query.Where(x => EF.Functions.Like(x.Name, $"%{payload.Name}%"))
这直接转换为生成的 SQL 语句中的 LIKE
运算符。它并非适用于每个 DBMS,但只要您添加了 Microsoft.EntityFrameworkCore.SqlServer,您就可以开始了(假设您的问题已正确标记)。
我已经测试了 like 函数,但结果并不像操作说明的那样正确。所以只剩下一个选项了。即创建拦截器并实现自定义逻辑。我创建了一个如下示例:
public class Suffixes
{
public const string Collate = "--Collate";
}
public class CollationDbCommandInterceptor : DbCommandInterceptor
{
private const string CollateSyntax = " collate turkish_ci_as";
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
var args = command.Parameters.OfType<DbParameter>()
.Where(t => t.DbType == DbType.String && t.Value.ToString().EndsWith(Suffixes.Collate)).ToList();
if (args.Count <= 0)
return base.ReaderExecuting(command, eventData, result);
foreach (var parameter in args)
{
parameter.Value = parameter.Value.ToString().Replace(Suffixes.Collate, "");
var equality = $"= {parameter.ParameterName}";
var ixs = AllIndexesOf(command.CommandText, equality);
#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
foreach (var eq in ixs)
{
command.CommandText = command.CommandText.Insert(eq+equality.Length,CollateSyntax);
}
#pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
}
return base.ReaderExecuting(command, eventData, result);
}
private static IEnumerable<int> AllIndexesOf(string str, string value)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("the string to find may not be empty", nameof(value));
var indexes = new List<int>();
for (var index = 0; ; index += value.Length)
{
index = str.IndexOf(value, index);
if (index == -1)
return indexes;
indexes.Insert(0,index);
}
}
}
配置:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
....
optionsBuilder.AddInterceptors(new CollationDbCommandInterceptor());
....
}
}
用法:
var kadayif = $"kadayıf{Suffixes.Collate}";
var william = $"Wİlliam{Suffixes.Collate}";
var auths = ctx.Authors.Where(t => t.FirstName == william ||t.LastName == kadayif).ToList();
// returns William Shakespeare and Abuzer Kadayıf
逻辑是创建一个拦截器,在查询中传递的 sql 参数中寻找特定后缀。将查询特定的排序规则注入最终的 sql 命令文本。我试图涵盖一些高级场景,例如参数重用。它可能需要更多改进。
请注意,此示例适用于 Entity Framework Core 3.0,即拦截器引入的版本。在早期的 ef core 版本中拦截是一个小技巧。您可以参考 this link 了解更多信息。