Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArgumentException thrown When GroupJoin is applied on a DbSet as a first join and NOT when applied as a second or later #27343

Open
ankitmatrix08 opened this issue Feb 2, 2022 · 0 comments

Comments

@ankitmatrix08
Copy link

@ankitmatrix08 ankitmatrix08 commented Feb 2, 2022

1. Exception Case:
LINQ: Here, I have a query which is doing a GroupJoin on an DbSet and then adding other inner joins.
Repository.ConvertToBigIntTable -> This is an output of a call to a Table-Valued function which returns an IQueryable (T is a Keyless entity having a long Id field)

private IQueryable<IDocumentTypePermission> GetDocumentTypePermissionsTest<T>(IQueryable<T> entitySet) where T : class, IDocumentType
        {
            var userGroupIds = RunComponent(GetActiveUserGroupsOfCurrentUser.New(new GetActiveUserGroupsOfCurrentUserParam() { CurrentUserId = CurrentUserId })).UserGroupIds;
            return (from documentType in entitySet
                    join userGroupId in Repository.ConvertToBigIntTable(userGroupIds, "userGroupId")
                    on documentType.Id equals userGroupId.Id into UserGroupIds
                    from userGroupId in UserGroupIds.DefaultIfEmpty()
                    join documentTypePermission in Repository.DocumentTypePermissions
                    on documentType.Id equals documentTypePermission.DocumentTypeId
                    join userSelectionParam in Repository.UserSelectionParams
                    on documentTypePermission.UserSelectionId equals userSelectionParam.Id

                    
                    where documentTypePermission.IsActive && ((userSelectionParam.UserGroupId != null && userGroupId.Id != null)
                                                              || (userSelectionParam.UserId != null && userSelectionParam.UserId == CurrentUserId))
                    select documentTypePermission);
        }
DbSet<EDocumentType>()
    .GroupJoin(
        inner: BananaContext.ConvertCSVToBigIntTable(
            List: <>c__DisplayClass958_0.List, 
            Delim: <>c__DisplayClass958_0.Delim), 
        outerKeySelector: documentType => documentType.Id, 
        innerKeySelector: userGroupId => userGroupId.Id, 
        resultSelector: (documentType, UserGroupIds) => new { 
            documentType = documentType, 
            UserGroupIds = UserGroupIds
         })
    .SelectMany(
        collectionSelector: <>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.UserGroupIds
            .DefaultIfEmpty(), 
        resultSelector: (<>h__TransparentIdentifier0, userGroupId) => new { 
            <>h__TransparentIdentifier0 = <>h__TransparentIdentifier0, 
            userGroupId = userGroupId
         })
    .Join(
        inner: DbSet<EDocumentTypePermission>(), 
        outerKeySelector: <>h__TransparentIdentifier1 => (long?)<>h__TransparentIdentifier1.<>h__TransparentIdentifier0.documentType.Id, 
        innerKeySelector: documentTypePermission => documentTypePermission.DocumentTypeId, 
        resultSelector: (<>h__TransparentIdentifier1, documentTypePermission) => new { 
            <>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, 
            documentTypePermission = documentTypePermission
         })
    .Join(
        inner: DbSet<EUserSelectionParam>(), 
        outerKeySelector: <>h__TransparentIdentifier2 => <>h__TransparentIdentifier2.documentTypePermission.UserSelectionId, 
        innerKeySelector: userSelectionParam => (long?)userSelectionParam.Id, 
        resultSelector: (<>h__TransparentIdentifier2, userSelectionParam) => new { 
            <>h__TransparentIdentifier2 = <>h__TransparentIdentifier2, 
            userSelectionParam = userSelectionParam
         })
    .Where(<>h__TransparentIdentifier3 => <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.documentTypePermission.IsActive && <>h__TransparentIdentifier3.userSelectionParam.UserGroupId != null && (long?)<>h__TransparentIdentifier3.<>h__TransparentIdentifier2.<>h__TransparentIdentifier1.userGroupId.Id != null || <>h__TransparentIdentifier3.userSelectionParam.UserId != null && <>h__TransparentIdentifier3.userSelectionParam.UserId == (long?)AbstractDomainBehavior.CurrentUserId)
    .Select(<>h__TransparentIdentifier3 => <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.documentTypePermission)

Exception:

Expression of type 'System.Func`2[Lw.Domain.IDocumentType,System.Int64]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[Lw.Domain.EDocumentType,System.Int64]]' of method 'System.Linq.IQueryable`1[<>f__AnonymousType290`2[<>f__AnonymousType289`2[Lw.Domain.IDocumentType,System.Collections.Generic.IEnumerable`1[Lw.Sys.Repository.ITableValueFunctionResult]],Lw.Sys.Repository.ITableValueFunctionResult]] LeftJoin[EDocumentType,ITableValueFunctionResult,Int64,<>f__AnonymousType290`2](System.Linq.IQueryable`1[Lw.Domain.EDocumentType], System.Collections.Generic.IEnumerable`1[Lw.Sys.Repository.ITableValueFunctionResult], System.Linq.Expressions.Expression`1[System.Func`2[Lw.Domain.EDocumentType,System.Int64]], System.Linq.Expressions.Expression`1[System.Func`2[Lw.Sys.Repository.ITableValueFunctionResult,System.Int64]], System.Linq.Expressions.Expression`1[System.Func`3[Lw.Domain.EDocumentType,Lw.Sys.Repository.ITableValueFunctionResult,<>f__AnonymousType290`2[<>f__AnonymousType289`2[Lw.Domain.IDocumentType,System.Collections.Generic.IEnumerable`1[Lw.Sys.Repository.ITableValueFunctionResult]],Lw.Sys.Repository.ITableValueFunctionResult]]])' (Parameter 'arg2')

Stack Trace:

   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.TryFlattenGroupJoinSelectMany(MethodCallExpression methodCallExpression)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.NormalizeQueryableMethod(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryTranslationPreprocessor.NormalizeQueryableMethod(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

NOTE: Interestingly trying to do a very similar operation on LinqPad7 seems to work:
image

2. Working Case
LINQ: Here, I have a query which is performing inner joins on few DbSets and then applying GroupJoin on one of the DbSet.
Repository.ConvertToBigIntTable -> This is an output of a call to a Table-Valued function which returns an IQueryable (T is a Keyless entity having a long Id field)

 private IQueryable<IDocumentTypePermission> GetDocumentTypePermissions<T>(IQueryable<T> entitySet) where T : class, IDocumentType
        {
            var userGroupIds = RunComponent(GetActiveUserGroupsOfCurrentUser.New(new GetActiveUserGroupsOfCurrentUserParam() { CurrentUserId = CurrentUserId })).UserGroupIds;
            return (from documentType in entitySet
                    join documentTypePermission in Repository.DocumentTypePermissions on documentType.Id equals documentTypePermission.DocumentTypeId
                    join userSelectionParam in Repository.UserSelectionParams on documentTypePermission.UserSelectionId equals userSelectionParam.Id
                    join userGroupId in Repository.ConvertToBigIntTable(userGroupIds, "userGroupId")
                    on userSelectionParam.UserGroupId equals userGroupId.Id into UserGroupIds
                    from userGroupId in UserGroupIds.DefaultIfEmpty()
                    where documentTypePermission.IsActive && ((userSelectionParam.UserGroupId != null && userGroupId.Id != null)
                                                              || (userSelectionParam.UserId != null && userSelectionParam.UserId == CurrentUserId))
                    select documentTypePermission);
        }

Generated Expression:

DbSet<EDocumentType>()
    .Join(
        inner: DbSet<EDocumentTypePermission>(), 
        outerKeySelector: documentType => (long?)documentType.Id, 
        innerKeySelector: documentTypePermission => documentTypePermission.DocumentTypeId, 
        resultSelector: (documentType, documentTypePermission) => new { 
            documentType = documentType, 
            documentTypePermission = documentTypePermission
         })
    .Join(
        inner: DbSet<EUserSelectionParam>(), 
        outerKeySelector: <>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.documentTypePermission.UserSelectionId, 
        innerKeySelector: userSelectionParam => (long?)userSelectionParam.Id, 
        resultSelector: (<>h__TransparentIdentifier0, userSelectionParam) => new { 
            <>h__TransparentIdentifier0 = <>h__TransparentIdentifier0, 
            userSelectionParam = userSelectionParam
         })
    .GroupJoin(
        inner: BananaContext.ConvertCSVToBigIntTable(
            List: <>c__DisplayClass958_0.List, 
            Delim: <>c__DisplayClass958_0.Delim), 
        outerKeySelector: <>h__TransparentIdentifier1 => <>h__TransparentIdentifier1.userSelectionParam.UserGroupId, 
        innerKeySelector: userGroupId => (long?)userGroupId.Id, 
        resultSelector: (<>h__TransparentIdentifier1, UserGroupIds) => new { 
            <>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, 
            UserGroupIds = UserGroupIds
         })
    .SelectMany(
        collectionSelector: <>h__TransparentIdentifier2 => <>h__TransparentIdentifier2.UserGroupIds
            .DefaultIfEmpty(), 
        resultSelector: (<>h__TransparentIdentifier2, userGroupId) => new { 
            <>h__TransparentIdentifier2 = <>h__TransparentIdentifier2, 
            userGroupId = userGroupId
         })
    .Where(<>h__TransparentIdentifier3 => <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.<>h__TransparentIdentifier1.<>h__TransparentIdentifier0.documentTypePermission.IsActive && <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.<>h__TransparentIdentifier1.userSelectionParam.UserGroupId != null && (long?)<>h__TransparentIdentifier3.userGroupId.Id != null || <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.<>h__TransparentIdentifier1.userSelectionParam.UserId != null && <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.<>h__TransparentIdentifier1.userSelectionParam.UserId == (long?)AbstractDomainBehavior.CurrentUserId)
    .Select(<>h__TransparentIdentifier3 => <>h__TransparentIdentifier3.<>h__TransparentIdentifier2.<>h__TransparentIdentifier1.<>h__TransparentIdentifier0.documentTypePermission)

Generated SQL:

DECLARE @__List_1 nvarchar(4000) = N'1,5,6,7,8,14,15,44,46,62,63,64,67,69,73,85,88';
DECLARE @__Delim_2 nvarchar(1) = N',';
DECLARE @__CurrentUserId_3 bigint = CAST(1 AS bigint);

SELECT [d0].[Id], [d0].[Condition], [d0].[CreatedById], [d0].[CreatedTime], [d0].[DocumentTypeId], [d0].[IsActive], [d0].[IsOverridable], [d0].[IsReevaluate], [d0].[RowVersion], [d0].[UpdatedById], [d0].[UpdatedTime], [d0].[UserSelectionId], [d0].[AssignmentType], [d0].[ConditionFor], [d0].[CreationAllowed], [d0].[Permission]
FROM [DocumentTypes] AS [d]
INNER JOIN [DocumentTypePermissions] AS [d0] ON [d].[Id] = [d0].[DocumentTypeId]
INNER JOIN [UserSelectionParams] AS [u] ON [d0].[UserSelectionId] = [u].[Id]
LEFT JOIN [dbo].[ConvertCSVToBigIntTable](@__List_1, @__Delim_2) AS [c] ON [u].[UserGroupId] = [c].[Id]
WHERE ([d0].[IsActive] = CAST(1 AS bit)) AND (([u].[UserGroupId] IS NOT NULL AND [c].[Id] IS NOT NULL) OR ([u].[UserId] IS NOT NULL AND ([u].[UserId] = @__CurrentUserId_3)))

EF Core version: 6.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 6.0
Operating system: Win 10 Pro
IDE: Visual Studio 2022 v17.0.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant