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

ToDictionary won't work, if using with navigation property #10312

Closed
Dev0033 opened this issue Nov 16, 2017 · 2 comments
Closed

ToDictionary won't work, if using with navigation property #10312

Dev0033 opened this issue Nov 16, 2017 · 2 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

@Dev0033
Copy link

Dev0033 commented Nov 16, 2017

model from https://docs.microsoft.com/en-us/ef/core/get-started/full-dotnet/new-db

If I try to get #Dictionary and navigation property from query, I'll get an exception.

Exception message:

No coercion operator is defined between types 'Intro.Blog' and 'System.String'.

Stack trace:

   at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type)
   at Remotion.Linq.Parsing.RelinqExpressionVisitor.<AdjustArgumentsForNewExpression>d__0.MoveNext()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
   at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, IEnumerable`1 arguments, IEnumerable`1 members)
   at Remotion.Linq.Parsing.RelinqExpressionVisitor.VisitNew(NewExpression expression)
   at System.Linq.Expressions.NewExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.TrackEntitiesInResults[TResult](QueryModel queryModel)
   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, INodeTypeProvider nodeTypeProvider, IDatabase database, IDiagnosticsLogger`1 logger, Type contextType)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass15_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.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

Steps to reproduce

using Intro;
using System;
using System.Linq;

namespace ConsoleApp2
{
  class Program
  {
    static void Main(string[] args)
    {
      using (var db = new BloggingContext())
      {

        if (!db.Blogs.Any())
        {
          db.Blogs.Add(new Blog { Url = "http://Blog1.com" });
          db.Blogs.Add(new Blog { Url = "http://Blog2.com" });
          db.SaveChanges();
        }

        Console.WriteLine("All blogs in database:");

        var query = db.Blogs.Select(c => new
        {
          Url = c.Url,
          Navigation = c,
          Posts = c.Posts.ToDictionary(a => a.PostId.ToString(), x => x.Title),
        });

        foreach (var blog in query)
        {
          Console.WriteLine(" - {0}", blog.Url);
        }
      }
      Console.ReadKey();
    }
  }
}

Workaround

change order of selected fields.
With query:

var query = db.Blogs.Select(c=>new
        {
          Url = c.Url,
          Posts = c.Posts.ToDictionary(a=>a.PostId.ToString(),x=>x.Title),
          Navigation = c,
        });

I can get what I want.

Further technical details

EF Core version: 2.0.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 Enterprise

@smitpatel
Copy link
Member

Correct QM

dbug: Microsoft.EntityFrameworkCore.Query[100104]
      Optimized query model:
      'from Blog c in DbSet<Blog>
      order by Property([c], "Id") asc
      select new <>f__AnonymousType0<string, Dictionary<string, string>, Blog>(
          [c].Url,
          Dictionary<string, string> ToDictionary(
              source: List<Post> _ProjectCollectionNavigation(
                  entity: Blog _Include(
                      queryContext: queryContext,
                      entity: [c],
                      included: new object[]{ },
                      fixup: (QueryContext queryContext | Blog entity | Object[] included) =>
                      {
                          Void queryContext.QueryBuffer.StartTracking(
                              entity: entity,
                              entityType: EntityType: Blog)
                          return Void queryContext.QueryBuffer.IncludeCollection(
                              includeId: 0,
                              navigation: Blog.Posts,
                              inverseNavigation: null,
                              targetEntityType: EntityType: Post,
                              clrCollectionAccessor: ClrICollectionAccessor<Blog, List<Post>, Post>,
                              inverseClrPropertySetter: null,
                              tracking: True,
                              instance: entity,
                              valuesFactory: () =>
                                  from Post c.Posts in DbSet<Post>
                                  join AnonymousObject _c in
                                      from Blog c in DbSet<Blog>
                                      select new AnonymousObject(new object[]{ (object)EF.Property(?[c]?, "Id") })
                                  on Property([c.Posts], "BlogId") equals (Nullable<int>)object [_c].GetValue(0)
                                  order by object [_c].GetValue(0) asc
                                  select [c.Posts])
                      }
                  ),
                  accessor: (Blog c) => c?.Posts ?? List<Post>
                  {
                  }
              ),
              keySelector: (Post a) => string a.PostId.ToString(),
              elementSelector: (Post x) => x.Title),
          [c]
      )'

Faulty QM

dbug: Microsoft.EntityFrameworkCore.Query[100104]
      Optimized query model:
      'from Blog c in DbSet<Blog>
      order by Property([c], "Id") asc
      select new <>f__AnonymousType0<string, Blog, Dictionary<string, string>>(
          Blog _Include(
              queryContext: queryContext,
              entity: [c],
              included: new object[]{ },
              fixup: (QueryContext queryContext | Blog entity | Object[] included) =>
              {
                  Void queryContext.QueryBuffer.StartTracking(
                      entity: entity,
                      entityType: EntityType: Blog)
                  return Void queryContext.QueryBuffer.IncludeCollection(
                      includeId: 0,
                      navigation: Blog.Posts,
                      inverseNavigation: null,
                      targetEntityType: EntityType: Post,
                      clrCollectionAccessor: ClrICollectionAccessor<Blog, List<Post>, Post>,
                      inverseClrPropertySetter: null,
                      tracking: True,
                      instance: entity,
                      valuesFactory: () =>
                          from Post c.Posts in DbSet<Post>
                          join AnonymousObject _c in
                              from Blog c in DbSet<Blog>
                              select new AnonymousObject(new object[]{ (object)EF.Property(?[c]?, "Id") })
                          on Property([c.Posts], "BlogId") equals (Nullable<int>)object [_c].GetValue(0)
                          order by object [_c].GetValue(0) asc
                          select [c.Posts])
              }
          ).Url,
          [c],
          Dictionary<string, string> ToDictionary(
              source: List<Post> _ProjectCollectionNavigation(
                  entity: [c],
                  accessor: (Blog c) => c?.Posts ?? List<Post>
                  {
                  }
              ),
              keySelector: (Post a) => string a.PostId.ToString(),
              elementSelector: (Post x) => x.Title)
      )'

Seems like error in _Include optimization or TrackEntitiesInResults

@maumar
Copy link
Contributor

maumar commented Feb 13, 2018

This works in current bits, now using correlated collection optimizations rather than Include pipeline, which was source of the problem.

Current QM:

from Blog c in DbSet<Blog>
order by (Nullable<int>)EF.Property(?[c]?, "Id") asc
select new <>f__AnonymousType0<string, Blog, Dictionary<string, string>>(
    [c].Url, 
    [c], 
    Dictionary<string, string> ToDictionary(
        source: (List<Post>)List<Post> queryContext.QueryBuffer.CorrelateSubquery(
            correlatedCollectionId: 0, 
            navigation: Blog.Posts, 
            resultCollectionFactory: (INavigation n) => (List<Post>)object IClrCollectionAccessor GetCollectionAccessor(n).Create(), 
            outerKey: new MaterializedAnonymousObject(new object[]{ (object)EF.Property(?[c]?, "Id") }), 
            tracking: True, 
            correlatedCollectionFactory: () => 
                from Post c.Posts in DbSet<Post>
                join MaterializedAnonymousObject _c in 
                    from Blog c in DbSet<Blog>
                    select new MaterializedAnonymousObject(new object[]{ (object)(Nullable<int>)EF.Property(?[c]?, "Id") })
                on Property([c.Posts], "BlogId") equals (Nullable<int>)object [_c].GetValue(0)
                order by object [_c].GetValue(0) asc
                select new Tuple<Post, MaterializedAnonymousObject, MaterializedAnonymousObject>(
                    [c.Posts], 
                    new MaterializedAnonymousObject(new object[]{ (object)EF.Property(?[c.Posts]?, "BlogId") }), 
                    new MaterializedAnonymousObject(new object[]{ object [_c].GetValue(0) })
                ), 
            correlationPredicate: (MaterializedAnonymousObject o | MaterializedAnonymousObject i) => object i.GetValue(0) == default(object) || object o.GetValue(0) == default(object) ? False : (Nullable<int>)(int)object o.GetValue(0) == (Nullable<int>)object i.GetValue(0)), 
        keySelector: (Post a) => string a.Id.ToString(), 
        elementSelector: (Post x) => x.Title)
)

@maumar maumar closed this as completed Feb 13, 2018
@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 Feb 13, 2018
@ajcvickers ajcvickers modified the milestones: 2.1.0-preview2, 2.1.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. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants