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 during query translation: ToString not declared on Nullable<int> #15386

Closed
ixtreon opened this issue Apr 17, 2019 · 2 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@ixtreon
Copy link

ixtreon commented Apr 17, 2019

Describe what is not working as expected:

Calling ToString() on a related object's int-typed column causes an ArgumentException. Doing the same for a Guid-typed column works as expected.

The primary object is an event log entry, and the related object is a (nullable) user with two identifiers - an int and a Guid. Retrieving FromUser.ID.ToString() causes the exception; calling FromUser.Guid.ToString() does not.

Full exception details (message and stack trace):

Exception message: 

    System.ArgumentException : Method 'System.String ToString()' declared on type 'System.Int32' cannot be called with instance of type 'System.Nullable\`1[System.Int32]'

Stack trace:

   at System.Linq.Expressions.Expression.ValidateCallInstanceType(Type instanceType, MethodInfo method)
   at System.Linq.Expressions.Expression.ValidateStaticOrInstanceMethod(Expression instance, MethodInfo method)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.NavigationRewritingExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](ReadOnlyCollection`1 nodes, String callerName)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.NavigationRewritingExpressionVisitor.VisitNewArray(NewArrayExpression node)
   at Remotion.Linq.Clauses.SelectClause.TransformExpressions(Func`2 transformation)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.NavigationRewritingExpressionVisitor.NavigationRewritingQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
   at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.NavigationRewritingExpressionVisitor.Rewrite(QueryModel queryModel, QueryModel parentQueryModel)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.OptimizeQueryModel(QueryModel queryModel, Boolean asyncQuery)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.OptimizeQueryModel(QueryModel queryModel, Boolean asyncQuery)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateQueryExecutor[TResult](QueryModel queryModel)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database, IDiagnosticsLogger`1 logger, Type contextType)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass13_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Remotion.Linq.QueryableBase`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

Steps to reproduce

The relevant parts of the model, in pseudo-code:

class EventLog
{
    [ForeignKey<User>(x => x.ID)]
    public int? FromUserID { get; set; }
    
    public virtual User FromUser { get; set; }
}

class User
{
    [PrimaryKey]
    public int ID { get; set; }

    public int Guid { get; set; }
}

Code that triggers the exception:

var thisWorks = Unit.Set<EventLog>()
    .Where(x => x.FromUserID != null)
    .Select(x => x.FromUser)
    .Select(x => x.Guid.ToString())
    .ToList();

var thisThrows = Unit.Set<EventLog>()
    .Where(x => x.FromUserID != null)
    .Select(x => x.FromUser)
    .Select(x => x.ID.ToString())
    .ToList();

These are trimmed-down examples, not the full code, so the exception above could be a bit misleading..

Further Notes

  • Adding .Where(x => x != null) before the final Select( ... ToString()) does not affect the outcome.
  • Doing .Select(x => x != null ? x.ID.ToString() : null) throws the exception too.
  • The way we solved it was by not using EF for the last bit, i.e:
var thisWorksNow = Unit.Set<EventLog>()
    .Where(x => x.FromUserID != null)
    .Select(x => x.FromUser)
    .Select(x => new { x.ID })
    .AsEnumerable()
    .Select(x => x.ID.ToString())
    .ToList();

Related Issues

Further technical details

EF Core version: 2.2.3, 2.2.4
Database Provider: Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Sqlite
Operating system: Windows 10
IDE: Visual Studio 2017 15.9

@ajcvickers ajcvickers added this to the 3.0.0 milestone Apr 19, 2019
@smitpatel
Copy link
Contributor

It's nav rewrite. Translation code is bug free wrt this kind of exception. Assigning to @maumar

@maumar
Copy link
Contributor

maumar commented Jun 19, 2019

this scenario works in the new query pipeline, running similar query on test model produced the following sql:

SELECT CONVERT(VARCHAR(11), CAST([t].[SquadId] AS int))
FROM [Tags] AS [t0]
LEFT JOIN (
    SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
    FROM [Gears] AS [g]
    WHERE [g].[Discriminator] IN (N'Gear', N'Officer')
) AS [t] ON (([t0].[GearNickName] = [t].[Nickname]) AND [t0].[GearNickName] IS NOT NULL) AND (([t0].[GearSquadId] = [t].[SquadId]) AND [t0].[GearSquadId] IS NOT NULL)
WHERE [t0].[GearSquadId] IS NOT NULL

@maumar maumar closed this as completed Jun 19, 2019
@maumar maumar added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jun 19, 2019
@ajcvickers ajcvickers modified the milestones: 3.0.0, 3.0.0-preview7 Jul 2, 2019
@ajcvickers ajcvickers modified the milestones: 3.0.0-preview7, 3.0.0 Nov 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Projects
None yet
Development

No branches or pull requests

4 participants