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

Query: Exception when projecting navigation property value that could return null, but does not ("Argument Types do not match") #5522

Closed
brianro opened this issue May 25, 2016 · 13 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@brianro
Copy link

brianro commented May 25, 2016

When doing this, also make sure the scenario from #5883 is taken care of - dotting thru an optional nav to get a value

If you have a model (see attached project) with class A with a nullable id field for child class E - int? EId, querying A->E fails with "argument types do not match" error.

            var item =
                (from a
                     in db.As
                 select new A
                 {
                     Id = a.Id,
                     EId = a.EId,
                     E = new E
                     {
                         Id = a.E.Id
                     }
                 }
                     ).First();

In attached project query_ef_test_ac fails with the following information:

Test Name: BugDemoEF.EFTest.query_ef_test_ae
Test FullName: BugDemoEF.EFTest.query_ef_test_ae
Test Source: C:\Code\General\BugDemo\src\BugDemoEF\EFTests.cs : line 205
Test Outcome: Failed
Test Duration: 0:00:01.976

Result StackTrace:
at System.Linq.Expressions.Expression.Bind(MemberInfo member, Expression expression)
at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection1 nodes, Func2 elementVisitor)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection1 nodes, Func2 elementVisitor)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
at Remotion.Linq.Clauses.SelectClause.TransformExpressions(Func2 transformation) at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.NavigationRewritingExpressionVisitor.Rewrite(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.OptimizeQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateQueryExecutor[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass19_01.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 BugDemoEF.EFTest.query_ef_test_ae() in C:\Code\General\BugDemo\src\BugDemoEF\EFTests.cs:line 208
Result Message: Argument types do not match

BugDemo.zip

@maumar
Copy link
Contributor

maumar commented May 27, 2016

the problem is in here

                     E = new E
                     {
                         Id = a.E.Id
                     }

Because a.E is optional navigation, a.E.Id can potentially be null. To compensate we convert the calls to int? and now we are trying to fit int? into int column.

Workaround is to use anonymous type instead:

            var item =
                (from a
                     in db.As
                 select new
                 {
                     Id = a.Id,
                     EId = a.EId,
                     E = new
                     {
                         Id = (int?)a.E.Id
                     }
                 }
                     ).First();

@divega
Copy link
Contributor

divega commented May 27, 2016

Talking to @maumar and @ajcvickers, we don't feel like this is RTM, because of the workaround and it doesn't seem to be that common. Thoughts?

cc @rowanmiller @anpete

@brianro
Copy link
Author

brianro commented May 27, 2016

Thank you for the quick response.

This actually doesn't solve the problem at all and is a major issue. My objective is to return a new E class, not a new anonymous class similar to E. A detached instance is going to the client as a web service result.

I also tried using a nullable syntax in the query e.g.
E = (a.EId.HasValue ? return new E {} etc, null)

but that didn't work either.

To resolve this, I would have to then map the result to the strong type definition and I can't use Automapper for anonymous types.

This results in a ton of extra code and manual maintenance.

@smitpatel
Copy link
Contributor

Try this solution:

var item =
    (from a
            in db.As
        select new A
        {
            Id = a.Id,
            EId = a.EId,
            E = new E
            {
                Id = (int)((int?)a.E.Id ?? 0)
            }
        }
            ).First();

Generates following query

SELECT [a].[Id], [a].[CId], [a].[DId], [a].[EId], [a.E].[Id], 0
FROM [As] AS [a]
LEFT JOIN [Es] AS [a.E] ON [a].[EId] = [a.E].[Id]
ORDER BY [a].[EId]

And does not throw above exception. ToList() version runs without any exception. (First() fails for me because my database doesn't have any data.)

@brianro
Copy link
Author

brianro commented May 27, 2016

Nope – the moment I add in the additional fields, it fails with the same error. The problem seems to be a lot deeper than just the key field and it applies to any model that has a parent with an optional child.

B.

From: Smit Patel [mailto:[email protected]]
Sent: Friday, May 27, 2016 2:22 PM
To: aspnet/EntityFramework [email protected]
Cc: brianro [email protected]; Author [email protected]
Subject: Re: [aspnet/EntityFramework] Model with nullable FK fails parent child query with "Argument Types do not match" (#5522)

Try this solution:

var item =
(from a
in db.As
select new A
{
Id = a.Id,
EId = a.EId,
E = new E
{
Id = (int)((int?)a.E.Id ?? 0)
}
}
).First();

Generates following query

SELECT [a].[Id], [a].[CId], [a].[DId], [a].[EId], [a.E].[Id], 0
FROM [As] AS [a]
LEFT JOIN [Es] AS [a.E] ON [a].[EId] = [a.E].[Id]
ORDER BY [a].[EId]

And does not throw above exception. ToList() version runs without any exception. (First() fails for me because my database doesn't have any data.)


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub #5522 (comment) , or mute the thread https://github.com/notifications/unsubscribe/ADYRDDmoIeIXaaDdKjxe6pI8r2NxDU6wks5qF0R0gaJpZM4Im7fi . https://github.com/notifications/beacon/ADYRDC4lDkcVa-WRh5SzZeXN9WsUBtEMks5qF0R0gaJpZM4Im7fi.gif

@smitpatel
Copy link
Contributor

As @maumar described above
A.E is option navigation. (due to Int32? a nullable type being FK property). Therefore A.E.Id can have null value and the result value is nullable type of whatever is type of A.E.Id. And that applies to all the properties of E.
Therefore based on what you want,
This should be the work-around.

var item =
    (from a
            in db.As
        select new A
        {
            Id = a.Id,
            EId = a.EId,
            E = a.EId.HasValue ?
            new E
            {
                Id = (int)a.E.Id
// if you add more property which are of non-nullable type, then explicit cast is needed
            }: null
        }
            ).ToList();

For further properties added inside new E you need to cast the properties if they are of non-nullable type since a.E.Property is still nullable type. That should work properly. (I tested above code in the project you provided and it works fine).
We can still do something about explicit cast not being required but work-around should be good enough for now.

@brianro
Copy link
Author

brianro commented Jun 1, 2016

@smitpatel - this solutions works thanks ... uglifies my code but its functional.

@maumar
Copy link
Contributor

maumar commented Jun 1, 2016

@brianro with a24f9bc checked in, you should be able to just use coalesce, without any casting or HasValue checks. This is currently available in out nightly builds and will also be available in the next release

@brianro
Copy link
Author

brianro commented Jun 1, 2016

Super news - thank you so much for getting that knocked out.
As always, I appreciate your attentiveness and dedication.
B.

@rowanmiller
Copy link
Contributor

When doing this, also make sure the scenario from #5883 is taken care of - dotting thru an optional nav to get a value

@rowanmiller rowanmiller modified the milestones: 1.0.1, 1.1.0 Jul 1, 2016
@rowanmiller rowanmiller removed the pri0 label Jul 6, 2016
maumar added a commit that referenced this issue Jul 8, 2016
…Argument Types do not match"

Problem was that during navigation rewrite we sometimes change types of expressions, e.g.

o.Customer.Id (originally int)

would get converted to:

(o != null) ? (int?)o.CustomerId : null (change type to int?)

We try to compensate for this later, by casting back to the original type, but we missed some cases: MemberAssignment, ElementInit, NewArray.

Fix is to add the compensation for those nodes.
maumar added a commit that referenced this issue Jul 8, 2016
…Argument Types do not match"

Problem was that during navigation rewrite we sometimes change types of expressions, e.g.

o.Customer.Id (originally int)

would get converted to:

(o != null) ? (int?)o.CustomerId : null (change type to int?)

We try to compensate for this later, by casting back to the original type, but we missed some cases: MemberAssignment, ElementInit, NewArray.

Fix is to add the compensation for those nodes.
maumar added a commit that referenced this issue Jul 11, 2016
…Argument Types do not match"

Problem was that during navigation rewrite we sometimes change types of expressions, e.g.

o.Customer.Id (originally int)

would get converted to:

(o != null) ? (int?)o.CustomerId : null (change type to int?)

We try to compensate for this later, by casting back to the original type, but we missed some cases: MemberAssignment, ElementInit, NewArray.

Fix is to add the compensation for those nodes.

CR: Smit
@maumar
Copy link
Contributor

maumar commented Jul 11, 2016

fixed in 7d359ab

@maumar maumar closed this as completed Jul 11, 2016
@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 Jul 11, 2016
@rowanmiller rowanmiller changed the title Model with nullable FK fails parent child query with "Argument Types do not match" Query: Exception when projecting nav prop value that could return null, but does not ("Argument Types do not match") Jul 21, 2016
@itsdevcoffee
Copy link

I'm running into a similar problem and I've tried all the workarounds and fixes but none of them worked. Here is my code and bear in mind I'm getting the same error.

https://gist.github.com/DmsChrisPena/a8bfa560f5c2bd2565a778a67018dcd1

@rowanmiller
Copy link
Contributor

@DmsChrisPena the issue being tracked here is fixed. If you are still seeing the failure on our nightly builds, then please open a new issue with details.

@divega divega changed the title Query: Exception when projecting nav prop value that could return null, but does not ("Argument Types do not match") Query: Exception when projecting navigation property value that could return null, but does not ("Argument Types do not match") Sep 13, 2016
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. type-bug
Projects
None yet
Development

No branches or pull requests

7 participants