System.Linq.Dynamic.Core icon indicating copy to clipboard operation
System.Linq.Dynamic.Core copied to clipboard

SelectMany collectionSelectorArgs not allowing context.Set<>()

Open HT-Xaris opened this issue 1 year ago • 0 comments

1. Description

If an additional DBSet is used via SelectMany, this cannot be resolved.

2. Exception

Exception message:
Unhandled exception. No property or field 'Set' exists in type 'SampleContext' (at index 8)
Stack trace:
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseMemberAccess(Type type, Expression expression) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 1891
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimary() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 819
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseUnary() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 806
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseArithmetic() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 751
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAdditive() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 718
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseShiftOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 694
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseComparisonOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 482
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLogicalAndOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 414
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIn() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 325
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 308
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 290
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 270
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 257
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 241
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAsLambda(String id) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 1963
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseMemberAccess(Type type, Expression expression) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 1882
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIdentifier() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 1058
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimaryStart() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 843
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimary() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 811
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseUnary() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 806
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseArithmetic() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 751
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAdditive() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 718
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseShiftOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 694
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseComparisonOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 482
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLogicalAndOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 414
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIn() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 325
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 308
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 290
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 270
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 257
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 241
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 156
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 121
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 98
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 357
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, String collectionSelector, String resultSelector, String collectionParameterName, String resultParameterName, Object[] collectionSelectorArgs, Object[] resultSelectorArgs) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicQueryableExtensions.cs:line 2085
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.SelectMany(IQueryable source, String collectionSelector, String resultSelector, String collectionParameterName, String resultParameterName, Object[] collectionSelectorArgs, Object[] resultSelectorArgs) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicQueryableExtensions.cs:line 2114
   at Program.Main()

3. Fiddle or Project

Sample includes a static version which is working and the dynamic which will fail.

https://dotnetfiddle.net/NwtrN5

using System;
using System.Linq;
using System.Linq.Dynamic.Core;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
					
public class Program
{
	public static void Main()
	{
		var context = new SampleContext();
		
		context.Add(new TableOne { Id = 1 });
		context.Add(new TableTwo { Id = 1, TableOneId = 2 });
		context.SaveChanges();
		
		var staticQuery = context.Set<TableOne>().SelectMany(t => context.Set<TableTwo>().Where(t2 => t2.TableOneId != t.Id),
			(t, t2) => new { t, t2 });
		
		Console.WriteLine(staticQuery.Expression.ToString());
		
		var staticResult = staticQuery.ToList();
		
		Console.WriteLine("static result: " + staticResult.Count.ToString());
		
		var dynamicQuery = context.Set<TableOne>().SelectMany("t => @0.Set<TableTwo>().Where(t2 => t2.TableOneId != t.Id)",
			"new (t as t, t2 as t2)", "t", "t2",
			new object[] { context });
		
		Console.WriteLine(dynamicQuery.Expression.ToString());
		
		var dynamicResult = dynamicQuery.ToDynamicList();
		
		Console.WriteLine("dynamic result: " + dynamicResult.Count.ToString());
	}
	
	public class SampleContext : DbContext
	{		
		public SampleContext()
			:base(GetOptions()) { }
		
		private static DbContextOptions GetOptions()
		{
			var optionsBuilder = new DbContextOptionsBuilder();
			optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());
			
			return optionsBuilder.Options;
		}
		
		protected override void OnModelCreating(ModelBuilder modelBuilder)
		{
			base.OnModelCreating(modelBuilder);

			modelBuilder.Entity<TableOne>().HasKey(x => x.Id);
			modelBuilder.Entity<TableTwo>().HasKey(x => x.Id);
		}
	}
	
	public record TableOne
	{
		public int Id { get; set; }
	}
	
	public record TableTwo
	{
		public int Id { get; set; }
		
		public int TableOneId { get; set; }
	}
}

4. Any further technical details

I know that we can combine 2 entities using Join or GroupJoin. However, the point is that different operators are supported and not just "=".

The static expression is supporting a method call to resolve the inner collection:

[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].SelectMany(t => value(Program+<>c__DisplayClass0_0).context.Set().Where(t2 => (t2.TableOneId != t.Id)), (t, t2) => new <>f__AnonymousType0`2(t = t, t2 = t2))

HT-Xaris avatar Feb 09 '24 16:02 HT-Xaris