efcore: EF Core 3.1.7 Eager Load two level ThenInclude last one with inherit type not working
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, Func
1 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, IQueryable
1 source, Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable
1 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)
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 17 (8 by maintainers)
https://github.com/dotnet/efcore/issues/10140 is needed to map this scenario correctly