如何使用 Any(x => DbFunctions.Like()) 优化 LINQ Where 条件

How to optimize a LINQ Where condition with Any(x => DbFunctions.Like())

此 LINQ 语句:

var entities = SomeEntities.Where(se => se.SomeProperty == "SomeValue");

var stringList = new List<string> { "string1", "string2", "string3" }
var startsWith = stringList.Select(x => x + "%");
entities = entities.Where(e => startsWith.Any(sw => DbFunctions.Like(e.StringProperty, sw))).Select(e => e.Id);

类似的 SQL 查询结果如下:

SELECT 
[Project8].[Id] AS [Id]
FROM ( SELECT 
    [Extent1].[Id] AS [Id], 
    FROM  [SomeEntities] AS [Extent1]
    WHERE ([Extent1].[SomeProperty] == 'SomeValue') AND( EXISTS (SELECT 
        1 AS [C1]
        FROM  (SELECT 
            N'string1%' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
        UNION ALL
            SELECT 
            N'string2%' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable2]
        UNION ALL
            SELECT 
            N'string3%' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2]
        WHERE [Extent1].[StringProperty] LIKE [UnionAll2].[C1]
    )))
)  AS [Project8]
ORDER BY [Project8].[Status] DESC

如何实现类似于以下的查询:

SELECT [Id]
FROM [SomeEntities]
WHERE [SomeProperty] == 'SomeValue'
AND (StringProperty] LIKE 'string1%'
    OR StringProperty] LIKE 'string2%'
    OR StringProperty] LIKE 'string3%')
entities = entities.Where(e => startsWith.Any(sw => e.StringProperty.Contains("string1") || e.StringProperty.Contains("string2") || e.StringProperty.Contains("string3") ))).Select(e => e.Id);

你可以试试:

var entityIds = SomeEntities
                  .Where(se => se.SomeProperty == "SomeValue" 
                     && stringList.Any(sw => se.StringProperty.StartsWith(sw)))
                  .Select(se=>se.Id);

首先,我建议使用 PredicateBuilder 进行复杂查询。它有助于轻松创建和组合表达式,然后使用 EF 对其求值。

然后, EF.Functions.LikeContains, StartWith, etc 决定你需要哪一个。

代码示例:

List<string> searchStrings=new List<string>(){"name1","name2","name3"};
var predicate = searchStrings
            .Select<string, Expression<Func<Person, bool>>>(search => item => EF.Functions.Like(item.Name, $"{search}%"))
            .DefaultIfEmpty(patient => false) //or whatever else you want to do if there are no search strings
            .Aggregate(PredicateBuilder.Or);

predicate = predicate.And(item => item.Deleted == null);        
var filteredPatients = await _context.Persons.Where(predicate).ToListAsync();

来自 sql 服务器分析器的查询:

exec sp_executesql N'SELECT [p].[Id], [p].[B], [p].[Deleted], [p].[Email], 
[p].[Name] FROM [Persons] AS [p]
WHERE [p].[Deleted] IS NULL AND (((([p].[Name] LIKE @__Format_1) OR ([p]. 
[Name] LIKE @__Format_2)) OR ([p].[Name] LIKE @__Format_3)) AND [p].[Deleted] 
IS NULL)',N'@__Format_1 nvarchar(4000),@__Format_2 nvarchar(4000),@__Format_3 
nvarchar(4000)',@__Format_1=N'name1%',@__Format_2=N'name2%',@__Format_3=N'name3%'