F# query expression and C# LINQ with Array.Contains fail after migration to .NET8

196 Views Asked by At

Recently I've migrated my code to .NET8 and some of my queries stopped working. Here's an example, which used to work in .NET7

let accounts: Array[] = ...

query {
    for user in db.User do
      where accounts.Contains user.Account
      select user
  }

I get an error:

Unhandled exception. System.InvalidOperationException: The LINQ expression 
'[Microsoft.EntityFrameworkCore.Query.InlineQueryRootExpression]' could not be translated. 
Additional information: Empty collections are not supported as inline query roots. 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'.

I noticed that now the expression doesn't like empty arrays and it fails when accounts doesn't contain any items. So I need to add a guard everywhere, e.g.

let accounts: Array[] = ...

// This works just fine
query {
    for user in db.User do
      where (accounts.Length <> 0 && accounts.Contains user.Account)
      select user
  }

The only problem is that in my codebase I use the .Contains method many, many times. Do I really need to add the part accounts.Length <> 0 && eeverywhere or maybe there's something better I could do?

I use SQL Server 2022.

1

There are 1 best solutions below

2
On BEST ANSWER

There is an open issue about this: Did Contains stop working on empty list in EF Core 8? #32375 with minimal repo by roji

Minimal repro is any composing on top of an empty inline array:

_ = ctx.Blogs.Where(b => new int[] { }.Contains(b.Id)).ToList();

It has been fixed in the EF Core main branch, see Allow empty inline collections (#32414), but is still present in the 8.0.0 branch of RelationalQueryableMethodTranslatingExpressionVisitor.cs. On Nov 30, 2023 the fix was approved for inclusion in servicing release EF Core 8.0.2.

So you could either wait for the 8.0.2 servicing release of EF Core or, if you cannot wait, use the accounts.Length <> 0 && ... workaround.