question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

EF Core 3.1.7 Eager Load two level ThenInclude last one with inherit type not working

See original GitHub issue

second .ThenInclude with a derived type class, .ThenInclude(…).ThenInclude(x => ((derivedClass)x).prop)

i have below code not working and need help to make it work. i have two data models classes in two separate DLLs, and wanted to create a one-to-one optional relationship, of course with direct reference that would lead to “circular reference” situation. my solution was to have a third DLL referencing both and have another data model inherited from one of the models and add relationship to that. now i need to include the extra property on that module, my code goes as follow

SecurityGroup securityGroup = await _securityGroupRepository.GetAll().Where(sg => sg.SecurityGroupId == new Guid(groupId))
                                                   .Include(sg => sg.SecurityPolicies)
                                                   .Include(sg => sg.Users)
                                                        .ThenInclude(sgu => sgu.User)
                                                        .ThenInclude(su => ((CrossSecurityUser)su).Person) //this is not working, if i remove it work
                                                        .FirstOrDefaultAsync();

on 1st DLL, Person Module

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;

namespace WorkNxt.Contacts.Models
{
    public class Person
    {
        [Required]
        public Guid PersonId { get; set; }
        [Required]
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        [Required]
        public string LastName { get; set; }
        //public virtual List<PersonsOrgnizations> Organizations { get; set; }
        [NotMapped]
        public string FullName => $"{FirstName} {MiddleName} {LastName}";
        public byte[] RowVersion { get; set; }
    }

    public class PersonEfConfig : IEntityTypeConfiguration<Person>
    {
        public void Configure(EntityTypeBuilder<Person> builder)
        {
            builder.ToTable("Person");
            builder.Property(e => e.PersonId).HasColumnName("PersonId").ValueGeneratedOnAdd();
            builder.HasKey(e => e.PersonId);
            //builder.HasMany(p => p.Organizations).WithOne(po => po.Person).HasForeignKey(po => po.PersonId).IsRequired(false);
            builder.Property(e => e.RowVersion).IsConcurrencyToken().ValueGeneratedOnAddOrUpdate();
        }
    }
}

on 2nd DLL, SecurityGroup, SecurityGroupUsers and Security User which i want one-to-one with Person and same from Person (Circular reference) Modules

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace WorkNxt.Security.Models
{
    public class SecurityGroup
    {
        [Required]
        public Guid SecurityGroupId { get; set; }
        public string SecurityGroupCode { get; set; }
        public string Descrption { get; set; }
        public byte[] RowVersion { get; set; }

        public virtual List<SecurityGroupsTenantsPolicies> SecurityPolicies { get; set; }

        public virtual List<SecurityGroupsUsers> Users { get; set; }
    }

    public class SecurityGroupEfConfig : IEntityTypeConfiguration<SecurityGroup>
    {
        public void Configure(EntityTypeBuilder<SecurityGroup> builder)
        {
            builder.ToTable("SecurityGroup");
            builder.Property(e => e.SecurityGroupId).HasColumnName("SecurityGroupId");

            builder.HasKey(e => new { e.SecurityGroupId });

            builder.Property(e => e.RowVersion).IsConcurrencyToken().ValueGeneratedOnAddOrUpdate();

            builder.HasMany(sg => sg.SecurityPolicies).WithOne(pg => pg.SecurityGroup).HasForeignKey(pg => pg.SecurityGroupId);
            builder.HasMany(sg => sg.Users).WithOne(ug => ug.SecurityGroup).HasForeignKey(ug => ug.GroupId);
        }
    }
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.ComponentModel.DataAnnotations;

namespace WorkNxt.Security.Models
{
    public class SecurityGroupsUsers
    {
        [Required]
        public Guid UserId { get; set; }
        public SecurityUser User { get; set; }
        [Required]
        public Guid GroupId { get; set; }
        public SecurityGroup SecurityGroup { get; set; }

        public class SecurityGroupsUsersEfConfig : IEntityTypeConfiguration<SecurityGroupsUsers>
        {
            public void Configure(EntityTypeBuilder<SecurityGroupsUsers> builder)
            {
                builder.ToTable("SecurityGroupsUsers");
                builder.Property(e => e.UserId).HasColumnName("UserId");
                builder.Property(e => e.GroupId).HasColumnName("GroupId");

                builder.HasKey(e => new { e.UserId, e.GroupId });

                builder.HasOne(ug => ug.User).WithMany(u => u.SecurityGroups).HasForeignKey(ug => ug.UserId);
                builder.HasOne(ug => ug.SecurityGroup).WithMany(sg => sg.Users).HasForeignKey(ug => ug.GroupId);
            }
        }
    }
}
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;


namespace WorkNxt.Security.Models
{
    public class SecurityUser
    {
        public Guid SecurityUserId { get; set; }
        [Required]
        public string UserName { get; set; }
        [NotMapped]
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
        public string Hash { get; set; }
        public string Salt { get; set; }
        public byte[] RowVersion { get; set; }
        public virtual List<SecurityGroupsUsers> SecurityGroups { get; set; }
    }

    public class SecurityUserEfConfig : IEntityTypeConfiguration<SecurityUser>
    {
        public void Configure(EntityTypeBuilder<SecurityUser> builder)
        {
            builder.ToTable("SecurityUser");
            builder.Property(e => e.SecurityUserId).HasColumnName("SecurityUserId");
            builder.HasKey(e => new { e.SecurityUserId });

            builder.HasIndex(e => e.UserName).IsUnique();

            builder.Property(e => e.RowVersion).IsConcurrencyToken().ValueGeneratedOnAddOrUpdate();

            builder.HasMany(u => u.SecurityGroups).WithOne(sg => sg.User).HasForeignKey(sg => sg.UserId);
        }
    }
}

to avoid that i have a 3rd DLL with

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using WorkNxt.Contacts.Models;
using WorkNxt.Security.Models;

namespace WorkNxt.Security.CrossModels
{
    public class CrossSecurityUser : SecurityUser
    {
        public Guid? PersonId { get; set; }
        [ForeignKey("PersonId")]
        public virtual Person Person { get; set; }
    }
}

now when this execute

SecurityGroup securityGroup = await _securityGroupRepository.GetAll().Where(sg => sg.SecurityGroupId == new Guid(groupId))
                                                   .Include(sg => sg.SecurityPolicies)
                                                   .Include(sg => sg.Users)
                                                        .ThenInclude(sgu => sgu.User)
                                                        .ThenInclude(su => ((CrossSecurityUser)su).Person) //this is not working, if i remove it work
                                                        .FirstOrDefaultAsync();

i get “Invalid include.”

Stack Trace

at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.PopulateIncludeTree(IncludeTreeNode includeTreeNode, Expression expression) at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ProcessInclude(NavigationExpansionExpression source, Expression expression, Boolean thenInclude) at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.Expand(Expression query) 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__DisplayClass12_01.<ExecuteAsync>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable1 source, Expression expression, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at WorkNxt.Security.Api.Controllers.GroupController.<Get>d__7.MoveNext() in {removed for privacy}\WorkNxt.Security.Api\Controllers\GroupController.cs:line 101

Further technical details

EF Core version: 3.1.7 Database provider: (Microsoft.EntityFrameworkCore.SqlServer) Target framework: (.NET Core 3.1) Operating system: Windows10 IDE: (Visual Studio 2019 16.7.2)

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:17 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
AndriySvyrydcommented, Sep 3, 2020

https://github.com/dotnet/efcore/issues/10140 is needed to map this scenario correctly

0reactions
alfred-zakicommented, Sep 10, 2020

here’s my solution to the issue i encountered, hope it’s useful to someone. https://blog.alfredzaki.com/2020/09/ef-core-two-data-models-on-two-separate.html

Read more comments on GitHub >

github_iconTop Results From Across the Web

Eager Loading of Related Data - EF Core
Eager loading a collection navigation in a single query may cause performance issues. For more information, see Single vs. split queries.
Read more >
Entity Framework Core Eager Loading Then Include on a ...
Can someone supply the code the goes in the following ThenInclude to load the User for each item in the collection. _dbContext.Sale.Include(s => ......
Read more >
Eager Loading using Include & ThenInclude in EF Core
In this tutorial, we look at include method and learn how to load entities from multiple levels and multiple tables. We also show...
Read more >
Eager Loading in Entity Framework Core
You can use the ThenInclude() method to load multiple levels of related entities. The following code snippet illustrates how this can be achieved....
Read more >
Correct Collection in ConstantExpression for EF Core ...
Most likely the problem is caused by an unbound lambda expression parameter here after deserialization s => tstList.Contains(s.Id).
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found