简体   繁体   中英

StartsWith with any string from list to SQL request with LINQ and Entity Framework

I can get objects from EF with request like this:

apples = apples.Where(a => a.Sort.Name.StartsWith("Gold"))

but i wonder is it possible to user list of strings instead of one string? I already tried do it this way:

List<string> list = {...}

apples = apples.Where(a => list.Any(x => a.Sort.Name.StartsWith(x)))

but it give me strange error:

System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'index') at System.Linq.Expressions.InstanceMethodCallExpression1.GetArgument(Int32 index) at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionVisitors.NpgsqlSqlTranslatingExpressionVisitor.VisitLikeAnyAll(SubQueryExpression expression) at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionVisitors.NpgsqlSqlTranslatingExpressionVisitor.VisitSubQuery(SubQueryExpression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlTranslatingExpressionVisitor.Visit(Expression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression expression) at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionVisitors.NpgsqlSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlTranslatingExpressionVisitor.Visit(Expression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlT ranslatingExpressionVisitor.VisitBinary(BinaryExpression expression) at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionVisitors.NpgsqlSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlTranslatingExpressionVisitor.Visit(Expression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression expression) at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionVisitors.NpgsqlSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.SqlTranslatingExpressionVisitor.Visit(Expression expression) at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index) at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection 1 bodyClauses, QueryModel queryModel) at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateAsyncQueryExecutor[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Storage.Database.CompileAsyncQuery[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass22_0 1 bodyClauses, QueryModel queryModel) at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateAsyncQueryExecutor[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Storage.Database.CompileAsyncQuery[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass22_0 1.b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func 1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 1.System.Collections.Generic.IAsyncEnumerable.GetEnumerator() at System.Linq.AsyncEnumerable.Aggregate_[TSource,TAccumulate,TResult](IAsyncEnumerable 1 source, TAccumulate seed, Func 3 accumulator, Func`2 resultSelector, CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Aggregate.cs:line 118 at {my project}

Was not able to get the same error as you (got just failed translation one, so it would be great if you could add minimal reproducible example ), for me worked using EF.Functions.ILike (with latest npgsql package):

List<string> list = new() {"a%", "b%"};
var result = ctx.Apples
    .Where(c => list.Any(xx => EF.Functions.ILike(c.Sort.Name, xx)))
    .ToList();

As far as I can see support for using StartsWith with local collection is not implemented at the moment (based on this PR and this comment ), only EF.Functions.Like and EF.Functions.ILike .

If you are willing to use LINQKit then you can create extension methods to build expression from local collections:

// searchTerms - IEnumerable<TSearch> where one must match for a row
// testFne(row,searchTerm) - test one of searchTerms against a row
// r => searchTerms.Any(s => testFne(r,s))
public static Expression<Func<T, bool>> AnyIs<T, TSearch>(this IEnumerable<TSearch> searchTerms, Expression<Func<T, TSearch, bool>> testFne) {
    var pred = PredicateBuilder.New<T>();
    foreach (var s in searchTerms)
        pred = pred.Or(r => testFne.Invoke(r, s));

    return pred;
}

// searchTerms - IEnumerable<TSearch> where one must match for a row
// testFne(row,searchTerm) - test one of searchTerms against a row
// dbq.Where(r => searchTerms.Any(s => testFne(r,s)))
public static IQueryable<T> WhereAny<T,TSearch>(this IQueryable<T> dbq, IEnumerable<TSearch> searchTerms, Expression<Func<T, TSearch, bool>> testFne) =>
    dbq.AsExpandable().Where(searchTerms.AnyIs(testFne));

Then you can use them with your query:

List<string> list = {...}

apples = apples.WhereAny(list, (a,s) => a.Sort.Name.StartsWith(s));

Of course, you can also create your own mini-LINQKit to accomplish the same task (though it isn't quite that mini).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM