linq2db icon indicating copy to clipboard operation
linq2db copied to clipboard

Nullable arrays are executed on client

Open IvMisticos opened this issue 2 years ago • 1 comments

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

IvMisticos avatar Oct 19 '23 04:10 IvMisticos

Will look into it this week. I observe same or similar issue with client-side collection in my projects.

MaceWindu avatar Oct 23 '23 09:10 MaceWindu