-1

The below code works fine (throws no translation error):

string text = "someString";
string refId = "refId";
bool forward = true;
IQueryable<Tin> q = queryable.Where(o => forward ? text.IsGreaterThan(refId) : text.IsLessThan(refId));
var x = await q.ToArrayAsync();

IsGreaterThan() and IsLessThan() are methods that have been registered in OnModelCreating(ModelBuilder builder) from dbContext, as explained in this answer. So the problem is not with those methods.

The below code doesn't work:

string refId = "refId";
Expression<Func<Tin, string>> key = obj => obj.Id;
bool forward = true;
IQueryable<Tin> q = queryable.Where(o => forward ? key.Compile()(o).IsGreaterThan(refId) : key.Compile()(o).IsLessThan(refId));
var x = await q.ToArrayAsync();

It throws the below error.

System.InvalidOperationException: The LINQ expression 'DbSet<Follow>
    .Where(f => f.FollowerId == __userId_0)
    .Where(f => Invoke(__Compile_1, f[Follow])
        .IsGreaterThan(__refId_2))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().

I understand why it fails. Expression<Func<>>.Compile() returns a Func<> and those cannot be used in Linq to Sql. So I've been looking for workarounds.

The most promising I found is from this answer. It worked as expected in the fiddle he provided, but for some reason, it didn't work for me. My code after implementing his answer:

string refId = "refId";
Expression<Func<Tin, string>> key = obj => obj.Id;
bool forward = true;
Func<Tin, string> keyFunc = key.Compile();
Expression<Func<Tin, bool>> where = o => forward ? keyFunc(o).IsGreaterThan(refId) : keyFunc(o).IsLessThan(refId);
where.Replace(() => keyFunc, key);
IQueryable<Tin> q = queryable.Where(where);
var x = await q.ToArrayAsync();

Is there some other way to nest Expression<Func<>>s?

EDIT: As mentioned by @pinkfloydx33:

where.Replace(() => keyFunc, key);

should have been:

where = where.Replace(() => keyFunc, key);

Funny enough, I make this same mistake with string.Replace() a loooot. It works fine now after this.

YoungDON
  • 113
  • 1
  • 2
  • 8
  • I don't believe EF supports nested Expressions... Some clarification on what you want to build as end result could help... – Alexei Levenkov Feb 10 '21 at 04:22
  • Btw... In Your example code of trying the expression visitor you're not reassigning the `where` variable. Expressions are immutable. So you need `where = where.Replace(...)` and the visitor should work – pinkfloydx33 Feb 10 '21 at 10:17
  • @pinkfloydx33 Ahhh..... This was what was missing!! Thank you! – YoungDON Feb 10 '21 at 18:57

1 Answers1

0

This feature is not supported by EF, and i don't know why ;) You have to install LINQKit and use AsExpandable() adn Invoke instead of Compile:

string refId = "refId";
Expression<Func<Tin, string>> key = obj => obj.Id;
bool forward = true;
IQueryable<Tin> q = queryable
  .AsExpandable()
  .Where(o => forward ? key.Invoke(o).IsGreaterThan(refId) : key.Invoke(o).IsLessThan(refId));
var x = await q.ToArrayAsync();
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32
  • Thank you for this. I'd rather not install a library for this one case. But this looks much cleaner. And I wonder about the performance of the solution I have now. If I have to do this expression nesting often, I may switch to using this. – YoungDON Feb 10 '21 at 19:14
  • Actually look closer to these libraries. What you have did it is nothing comparing to what they can do: https://github.com/hazzik/DelegateDecompiler and https://github.com/axelheer/nein-linq – Svyatoslav Danyliv Feb 10 '21 at 19:26