Sayansen
Sayansen

Reputation: 61

EF.Functions.Like in a compiled query could not be translated to SQL equivalent

I am writing a compiled query which takes a DBContext and search string as input and returns a list of Class1 It basically runs 3 filters on the class1 objects and returns a filtered list of class1 objects.

Main query:

var temp = await context.Class1.Where(c => EF.Functions.Like(c.param1, $"%{query}%") && c.param2== 1 && c.param3!= 1).OrderBy(p => EF.Functions.Like(p.param1, $"{query}%") ? 0 : 1).AsNoTracking().ToListAsync();

This actually filters the list and then sorts them in an order where the names starting with the search query are shown before the others. Then

var result = temp.Select(a => new Class2{ T1= a.A1, T2= a.A2, T3= a.A3}).Distinct().Take(10).ToList();

Till now this is the Compiled Query I have wrote:

private static Func<DbContext, string, IEnumerable<Class1>> Search =
            EF.CompileQuery((DbContext context, string query) =>
            context.Class1
            .Where(c => EF.Functions.Like(c.param1, $"%{query}%") 
             && c.param2== 1 
             && c.param3!= 1)
            );

But when this compiled query is called it throws this exception: the linq expression could not be translated

How to solve this?

Update: This is solved now.

private static Func<DbContext, string, IEnumerable<Class2>> Search =
            EF.CompileQuery((DbContextcontext, string query) =>
            context.Class1
            .Where(c => c.param1.ToLower().Contains(query) && c.param2== 1 && c.param3!= 1)
            .OrderBy(p => p.param1.ToLower().StartsWith(query) ? 0 : 1)
            .Select(a => new Class2{ T1 = a.A1, T2 = a.A2, T3 = a.A3 })
            .Take(10).AsNoTracking());

Upvotes: 2

Views: 4031

Answers (3)

Ivan Stoev
Ivan Stoev

Reputation: 205759

You seems to be hitting yet another EF Core limitation. The problem is not the EF.Functions.Like method, but the usage of the interpolated string inside, which as you found works inside regular query, but not inside compiled query definition.

The solution/workaround is to use string concatenation in place of string interpolation:

private static Func<DbContext, string, IEnumerable<Class1>> Search =
    EF.CompileQuery((DbContext context, string query) => context.Class1
    .Where(c => EF.Functions.Like(c.param1, "%" + query + "%") // <-- 
     && c.param2== 1 
     && c.param3!= 1)
    );

The resulting SQL query is a bit different, but at least you got a translation.

Upvotes: 3

Sayansen
Sayansen

Reputation: 61

private static Func<DbContext, string, IEnumerable<Class2>> Search =
            EF.CompileQuery((DbContextcontext, string query) =>
            context.Class1
            .Where(c => c.param1.ToLower().Contains(query) && c.param2== 1 && c.param3!= 1)
            .OrderBy(p => p.param1.ToLower().StartsWith(query) ? 0 : 1)
            .Select(a => new Class2{ T1 = a.A1, T2 = a.A2, T3 = a.A3 })
            .Take(10).AsNoTracking());

Upvotes: 1

osman Rahimi
osman Rahimi

Reputation: 1507

If you are using EF Core, you don't need using CompileQuery, just use Ef.Function.Like as below

    var users = context.Users.Where(a => EF.Functions.Like(a.FirstName, "%a%")).ToList();

So Rewriting your code in this way would be like this:

    var result= context.Class1
        .Where(c => EF.Functions.Like(c.param1, $"%{query}%")
         && c.param2 == 1
         && c.param3 != 1).ToList();

Updated: If you want to get its result as t-sql, you can use ToQueryString() like this:

  var users = context.Users.Where(a => EF.Functions.Like(a.FirstName, "%a%")).ToQueryString();

Upvotes: 0

Related Questions