Nullable arrays are executed on client
Describe your issue
Given Guid[]? serverIds = null (or any other type really, such as int[]?) and a condition such as:
var total = await entities
.Where(x => serverIds == null || !serverIds.Any() || serverIds.Contains(x.ServerId))
.CountAsync();
The problem is with !serverIds.Any(), I've tried using serverIds.Count() == 0 and serverIds.Length == 0. However it seems that it tries to execute this on client when building the parameter (a guess based on the error below):
Exception message:
Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'source')
Stack trace:
at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source)
at lambda_method34(Closure, Expression, IDataContext, Object[])
at LinqToDB.Linq.QueryRunner.SetParameters(Query query, Expression expression, IDataContext parametersContext, Object[] parameters, Int32 queryNumber, SqlParameterValues parameterValues)
at LinqToDB.Linq.QueryRunnerBase.SetCommand(Boolean forGetSqlText)
at LinqToDB.Data.DataConnection.QueryRunner.ExecuteReaderAsync(CancellationToken cancellationToken)
at LinqToDB.Linq.QueryRunner.ExecuteElementAsync[T](Query query, IDataContext dataContext, Mapper`1 mapper, Expression expression, Object[] ps, Object[] preambles, CancellationToken cancellationToken)
at LinqToDB.Linq.QueryRunner.ExecuteElementAsync[T](Query query, IDataContext dataContext, Mapper`1 mapper, Expression expression, Object[] ps, Object[] preambles, CancellationToken cancellationToken)
at LinqToDB.Linq.ExpressionQuery`1.LinqToDB.Async.IQueryProviderAsync.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at LinqToDB.Linq.ExpressionQuery`1.LinqToDB.Async.IQueryProviderAsync.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
However the way to fix this would be !(serverIds ?? new Guid[0]).Any(), so the following works:
.Where(x => serverIds == null || !(serverIds ?? new Guid[0]).Any() || serverIds.Contains(x.ServerId)
Steps to reproduce
Here is the test code generated:
//---------------------------------------------------------------------------------------------------
// This code was generated by LinqToDB.
//---------------------------------------------------------------------------------------------------
using System;
using System.Linq;
using System.Linq.Expressions;
using LinqToDB;
using NUnit.Framework;
namespace T0.T1
{
[Table("entities")]
public class T2 : T0.T1.T3.T4
{
}
}
namespace T0.T1.T3
{
[Table("BaseEntity")]
public abstract class T4
{
[Column("server_id")]
public Guid P5 { get; set; }
}
}
namespace Tests.UserTests
{
[TestFixture]
public class UserTest : TestBase
{
[Test]
public void Test([DataSources(ProviderName.SQLite)] string context)
{
// dctp6.GetTable().Where(p6 => (((value(Program+<>c__DisplayClass0_0).P7 == null) OrElse Not(value(Program+<>c__DisplayClass0_0).P7.Any())) OrElse value(Program+<>c__DisplayClass0_0).P7.Contains(p6.P5))).Count()
using (var db = GetDataContext(context))
{
var query = db
.GetTable<T0.T1.T2>()
.Where(
p6 => (((value().P7 == null) || !value().P7
.Any()) || value().P7
.Contains(
p6.P5)))
.Count();
}
}
}
}
Environment details
Linq To DB version: 5.3.2 and 5.3.1
Database (with version): ClickHouse
ADO.NET Provider (with version): ClickHouse.Client
Operating system: Windows
.NET Version: .NET 6
Will look into it this week. I observe same or similar issue with client-side collection in my projects.