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

Entity objects cannot be returned within arrays in EF 3.0 preview 9 #17744

Closed
Shane32 opened this issue Sep 10, 2019 · 7 comments · Fixed by #18296
Closed

Entity objects cannot be returned within arrays in EF 3.0 preview 9 #17744

Shane32 opened this issue Sep 10, 2019 · 7 comments · Fixed by #18296
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

@Shane32
Copy link

Shane32 commented Sep 10, 2019

This query throws an error:

db.Parts.Select(x => new object[] { x }).ToList();

Error message:

Processing of the LINQ expression '[EntityShaperExpression][Parts]' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.

However, this works just fine: (and properly tracks the entity)

db.Parts.Select(x => new MyClass { Part = x }).ToList();

Where Myclass is defined as: (notice that Part is still defined as an object, so that's not the issue)

public class MyClass {
  public object Part { get; set; }
}

Also, selecting any non-entity works just fine:

db.Parts.Select(x => new object[] { x.Id, x.Description }).ToList();

For EF 2.x, the same bug existed, but the workaround was to use AsNoTracking(). This workaround does not work in EF 3 preview 9.

Stack trace as follows:

   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitNewArray(NewArrayExpression node)
   at System.Linq.Expressions.NewArrayExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   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.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   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.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at ZboxDbTest2.App.RunAsync() in C:\Users\Shane\Source\Repos\Zbox\ZboxDBTest2\App.cs:line 461

The problem exists when selecting any navigation property or returning any entity, and also occurs when returning dictionary objects. For example, this would cause the same issue: (assuming that Manufacturer was a navigation property)

var ret = db.Parts.Select(x => new Dictionary<string, object> { { "PartId", x.PartId }, { "Manufacturer", x.Manufacturer } }).ToList();

I have not been able to find any workaround -- for example, this doesn't work:

db.Parts.Select(x => new object[] { new MyClass { Part = x } }).ToList();

FYI, my code uses expression builders to translate graphql queries into dynamic expression trees via nested Dictionary<string, object> objects.

Further technical details

EF Core version: 3.0 preview 9
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.0 preview 9
Operating system: Win 10 Pro x64
IDE: Visual Studio 2019 16.1.3

@divega
Copy link
Contributor

divega commented Sep 11, 2019

cc @smitpatel what do you think about this?

@smitpatel
Copy link
Contributor

Probably 3.1

@smitpatel
Copy link
Contributor

Work-around

db.Parts.Select(x => new { x }).AsEnumerable().Select(t => new object[] { t.x }).ToList();

@Shane32
Copy link
Author

Shane32 commented Sep 21, 2019

The queries are created dynamically at runtime via the Expression namespace, and (as far as I know) it isn’t possible to create an anonymous type at runtime. If it is possible, that would be its own workaround, and I would not need an object returned in an array.

@smitpatel smitpatel added this to the 3.1.0 milestone Sep 27, 2019
@smitpatel smitpatel self-assigned this Sep 27, 2019
@smitpatel
Copy link
Contributor

Tests to add in SimpleQueryTestBase.Select.cs

        [ConditionalTheory]
        [MemberData(nameof(IsAsyncData))]
        public virtual Task Projection_into_object_array(bool isAsync)
        {
            return AssertQuery(
                isAsync,
                ss => ss.Set<Customer>().OrderBy(c => c.CustomerID).Where(c => c.CustomerID.StartsWith("A"))
                    .Select(c => new object[] { c }),
                entryCount: 4,
                assertOrder: true);
        }

        [ConditionalTheory]
        [MemberData(nameof(IsAsyncData))]
        public virtual Task Projection_into_object_array_2(bool isAsync)
        {
            return AssertQuery(
                isAsync,
                ss => ss.Set<Order>().OrderBy(o => o.OrderID).Where(o => o.OrderID < 10300)
                    .Select(o => new object[] { o, o.Customer }),
                entryCount: 87,
                assertOrder: true);
        }

This is issue in entity equality rewrite. The entityReferenceExpression was not removed in the processing hence translation pipeline failed. Removing entityEquality makes it to pass so we have translation properly in place.

@smitpatel smitpatel assigned roji and unassigned smitpatel Oct 8, 2019
roji added a commit that referenced this issue Oct 9, 2019
@roji roji added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Oct 9, 2019
roji added a commit that referenced this issue Oct 14, 2019
roji added a commit that referenced this issue Oct 14, 2019
@Shane32
Copy link
Author

Shane32 commented Oct 14, 2019

@smitpatel @roji Does this also fix the ListInitExpression? It would seem that the code might also need to handle VisitListInit to fix my second use case, which is the following:

var ret = db.Parts.Select(x => new Dictionary<string, object> { { "PartId", x.PartId }, { "Manufacturer", x.Manufacturer } }).ToList();

I'm guessing a test case would look something like this:

        [ConditionalTheory]
        [MemberData(nameof(IsAsyncData))]
        public virtual Task Projection_of_multiple_entity_types_into_dictionary(bool isAsync)
        {
            return AssertQuery(
                isAsync,
                ss => ss.Set<Order>().OrderBy(o => o.OrderID).Where(o => o.OrderID < 10300)
                    .Select(o => new System.Collections.Generic.Dictionary<string, object>() { { "obj", o }, { "cust", o.Customer } }),
                entryCount: 87,
                assertOrder: true);
        }

Thanks!!!

@roji
Copy link
Member

roji commented Oct 15, 2019

@Shane32 the fix I pushed doesn't fix that issue, but I'll be submitting a separate PR for 3.1 that fixes list initialization as well as some other similar things.

Opened #18379 to track.

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

Successfully merging a pull request may close this issue.

5 participants