postgresql 如何在单元测试中通过EF的自定义函数Map来模拟EF.Functions.ILike

7uhlpewt  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(1)|浏览(156)

我已更改当前代码

_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-defined-function-mapping#mapping-a-method-to-a-custom-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,但这实际上是类,所以这里肯定缺少一些语法。以及翻译还没有准备好。
我不确定我是否正确理解了用户自定义函数Map。我可能完全错了,也许它不能像我想象的那样解决。但是我想向自己保证。也许有人最终实现了我想做的事情?
像模拟整个查询这样的变通方法在我的情况下没有意义。在项目的当前状态和可用时间下,切换到对数据库本身的测试也是不可能的。

pjngdqdw

pjngdqdw1#

我花了大约一个月的时间来解决这个问题。而解决方法是如此简单...
您只需要实现内存搜索并检查静态标志来调用它。
范例:

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);
    }
}

字符串
然后在代码中将EF.Functions.ILike替换为PostgreSqlExtensions.ILike。例如:

var mercedesCars = await _db.Cars
    .Where(x => PostgreSqlExtensions.ILike(x.Name, "Mercedes%"))
    .ToListAsync(cancellationToken);


最后,在单元测试或初始测试设置中,只需设置标志:

if (!UnitTestChecker.IsTest)
    UnitTestChecker.IsTest = true;

相关问题