我更改了当前代码
_context.TABLE.Where(x => x.COLUMN1.Contains("xxx") || x.COLUMN2.Contains("xxx"))
进入
_context.TABLE.Where(x => EF.Functions.ILike(x.COLUMN1, "%xxx%") || EF.Functions.ILike(x.COLUMN2, "%xxx%")))
这满足了我的需要——不区分大小写的搜索。但它破坏了许多已经完成的单元测试。
异常信息:
System.InvalidOperationException : The LINQ expression '...' could not be translated. Additional information: Translation of method 'Microsoft.EntityFrameworkCore.NpgsqlDbFunctionsExtensions.ILike' failed. If this method can be mapped to your custom function, see https://go.microsoft.com/fwlink/?linkid=2132413 for more information. ... Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
我想将 ILike 函数的原子逻辑注入到提供程序中,以便它可以使用它 - 只是在单元测试的情况下。我读了很多书,似乎它不能像我想象的那样被嘲笑。但是从异常消息中阅读链接似乎解释了如何做到这一点:https://learn.microsoft.com/en-us/ef/core/querying/user-definition-function-mapping#mapping-a-method-到自定义 SQL
由于我的 EF 查询在数据库端被转换为带有 ILIKE 函数的 SQL 查询:
SELECT ... FROM ... AS t WHERE ... AND ((t."COLUMN1" ILIKE '%xxx%' ESCAPE '') OR t."COLUMN2" ILIKE '%xxx%' ESCAPE '');
我尝试将其注入上下文中:
modelBuilder.HasDbFunction(typeof(MYDBCONTEXT).GetMethod(nameof(XXX), new[] { typeof(string), typeof(string) })).HasTranslation(args => args[0]);
在
XXX
下我尝试过EF.Functions.ILike
但nameof不适用于扩展方法。然后我尝试了PostgresILikeExpression
,但这实际上是类,所以这里肯定缺少一些语法。而且翻译还没有准备好。
我不确定我是否正确理解了用户定义的函数映射。我可能完全错了,也许问题并不能像我想象的那样得到解决。不过我想向自己保证。也许有人最终实现了我尝试做的事情?
制定诸如模拟整个查询之类的解决方法在我的情况下没有任何意义。在项目的当前状态和可用时间下,切换到数据库本身的测试也是不可能的。
我花了大约一个月的时间来解决这个问题。而且解决办法就是这么简单...
您只需要实现内存中搜索并检查静态标志即可调用它。
示例:
public static class UnitTestChecker
{
public static bool IsTest { get; set; }
}
public static class PostgreSqlExtensions
{
public static bool ILike(string input, string pattern)
{
if (UnitTestChecker.IsTest)
return InMemoryExtensions.ILike(input, pattern);
else
return EF.Functions.ILike(input, pattern);
}
}
public static class InMemoryExtensions
{
private static readonly IReadOnlyDictionary<char, string> _patternMapping = new Dictionary<char, string>
{
{ '%', ".*" },
{ '_', "?*" },
{ '\\', "" },
};
public static bool ILike(string input, string postgreSqlPattern)
{
var capacity = postgreSqlPattern.Length + postgreSqlPattern.Length / 2; // presumably
var stringBuilder = new StringBuilder(capacity);
foreach (var character in postgreSqlPattern)
{
if (_patternMapping.TryGetValue(character, out var newCharacters))
stringBuilder.Append(newCharacters);
else
stringBuilder.Append(character);
}
var regexPattern = stringBuilder.ToString();
var regex = new Regex(
regexPattern,
options: RegexOptions.IgnoreCase | RegexOptions.CultureInvariant,
matchTimeout: TimeSpan.FromMilliseconds(50));
return regex.IsMatch(input);
}
}
最后,在单元测试或初始设置中只需设置标志:
if (!UnitTestChecker.IsTest)
UnitTestChecker.IsTest = true;