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

EF Core 5 seems to break nested entity ownership #23316

Closed
TAGC opened this issue Nov 13, 2020 · 1 comment
Closed

EF Core 5 seems to break nested entity ownership #23316

TAGC opened this issue Nov 13, 2020 · 1 comment

Comments

@TAGC
Copy link

TAGC commented Nov 13, 2020

I've recently started migrating an ASP.NET 3.1 Core project to target .NET 5. I now get an exception whenever I try to resolve a single entry from my DbContext:

var productConsultant = await _commissionsContext.ProductConsultants.FindAsync(request.ProductConsultantId);

The stack trace for this is listed below. The only thing particularly special about this entity is that it has nested ownership of other entities:

  • ProductConsultant owns many AnnualTarget
  • AnnualTarget owns many MonthlyTarget

It seems to be this nested ownership that causes the issue. I've reproduced this as minimally as possible using the code below.

Steps To Reproduce

  1. Create a project using the project file and code below, targeting .NET Core 3.1
  2. Update the DbContext to reference a temporary database on a SQL server
  3. Run dotnet ef migrations add InitialCreate, which should successfully generate a migration
  4. Run dotnet ef database update to apply the migration
  5. Run dotnet run, which should generate some output and terminate successfully
  6. Revert the migration: dotnet ef database update 0
  7. Remove the migration: dotnet ef migrations remove
  8. Update the project file to target .NET 5:
    1. Replace netcoreapp3.1 with net5.0
    2. Update all 3.1.0 dependencies to 5.0.0
  9. Repeat steps 3-4. A new migration should successfully be created and applied
  10. Repeat step 5. An InvalidOperationException should get thrown this time

Project File

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <LangVersion>9</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

Program

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using static System.Console;

using (var context = new Context())
{
    var annualTarget = new AnnualTarget(2019, new[] { 10, 20, 30, 40 });
    var productConsultant = new ProductConsultant(54050, new[] { annualTarget });

    context.Database.EnsureCreated();
    context.ProductConsultants.Add(productConsultant);
    context.SaveChanges();
}

using (var context = new Context())
{
    var productConsultant = context.ProductConsultants.Find(54050);
    var annualTarget = productConsultant.AnnualTargets.Single();

    WriteLine($"year: {annualTarget.CommissionYear}");

    foreach (var monthlyTarget in annualTarget.MonthlyTargets)
    {
        WriteLine($"month: {monthlyTarget.Month}: target: {monthlyTarget.Target}");
    }
}

// DbContext
class Context : DbContext
{
    private const string DatabaseConnection = "Data Source=tcp:SERVER;Trusted_Connection=Yes;database=DATABASE;";

    public DbSet<ProductConsultant> ProductConsultants { get; private set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(DatabaseConnection);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ProductConsultant>(pc =>
        {
            pc.ToTable(nameof(ProductConsultant));
            pc.HasKey(x => x.Id);
            pc.Property(x => x.Id).ValueGeneratedNever();

            pc.OwnsMany(x => x.AnnualTargets, at =>
            {
                at.ToTable(nameof(AnnualTarget));
                at.WithOwner();
                at.HasKey("CommissionYear", "ProductConsultantId");
                at.Property(x => x.CommissionYear).ValueGeneratedNever();
                at.OwnsMany(x => x.MonthlyTargets, mt =>
                {
                    mt.ToTable(nameof(MonthlyTarget));
                    mt.WithOwner();
                    mt.Property(x => x.Month).ValueGeneratedNever();
                    mt.HasKey("CommissionYear", "Month", "ProductConsultantId");
                });
            });
        });
    }
}

// Models
class ProductConsultant
{
    protected ProductConsultant() { }
    public ProductConsultant(int id, IEnumerable<AnnualTarget> annualTargets)
    {
        Id = id;
        AnnualTargets = annualTargets.ToList();
    }

    public int Id { get; private set; }
    public virtual List<AnnualTarget> AnnualTargets { get; set;}
}

public class AnnualTarget
{
    protected AnnualTarget() { }
    public AnnualTarget(int year, int[] targets)
    {
        CommissionYear = year;

        MonthlyTargets = new List<MonthlyTarget>();

        for (var i = 0; i < targets.Length; i++)
        {
            MonthlyTargets.Add(new MonthlyTarget(year, i + 1, targets[i]));
        }

    }

    public int CommissionYear { get; private set; }
    private int ProductConsultantId { get; set; }
    public virtual List<MonthlyTarget> MonthlyTargets { get; private set; }
}

public class MonthlyTarget
{
    protected MonthlyTarget() { }
    public MonthlyTarget(int year, int month, int target)
    {
        CommissionYear = year;
        Month = month;
        Target = target;
    }

    public int CommissionYear { get; private set; }
    public int Month { get; private set; }
    private int ProductConsultantId { get; set; }
    public int Target { get; set; }
}

Stack Trace

Unhandled exception. System.InvalidOperationException: The LINQ expression 'ShapedQueryExpression:
    QueryExpression:
        Projection Mapping:
            EmptyProjectionMember -> [EntityProjectionExpression]
        SELECT 1
        FROM MonthlyTarget AS m
    ShaperExpression: EntityShaperExpression:
            EntityType: MonthlyTarget
            ValueBufferExpression:
                ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False

    .Where(namelessParameter{0} => new object[]
    {
        (object)EF.Property<int>(EntityShaperExpression:
            EntityType: AnnualTarget
            ValueBufferExpression:
                ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
        , "CommissionYear"),
        (object)EF.Property<int>(EntityShaperExpression:
            EntityType: AnnualTarget
            ValueBufferExpression:
                ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
        , "ProductConsultantId")
    } != null && new object[]
    {
        (object)EF.Property<int>(EntityShaperExpression:
            EntityType: AnnualTarget
            ValueBufferExpression:
                ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
        , "CommissionYear"),
        (object)EF.Property<int>(EntityShaperExpression:
            EntityType: AnnualTarget
            ValueBufferExpression:
                ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
        , "ProductConsultantId")
    } == new object[]
    {
        (object)EF.Property<int>(namelessParameter{0}, "CommissionYear"),
        (object)EF.Property<int>(namelessParameter{0}, "ProductConsultantId")
    })' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|15_0(ShapedQueryExpression translated, <>c__DisplayClass15_0& )
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.TranslateSubquery(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.IncludeExpression.VisitChildren(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   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 System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.TranslateSubquery(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.IncludeExpression.VisitChildren(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   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 System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   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.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 System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinder`1.Find(Object[] keyValues)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.Find(Object[] keyValues)
   at <Program>$.<Main>$(String[] args) in C:\Projects\Playground\Program.cs:line 18

Verbose Output

PS C:\Projects\Playground> dotnet ef dbcontext list --verbose
Using project 'C:\Projects\Playground\Playground.csproj'.
Using startup project 'C:\Projects\Playground\Playground.csproj'.
Writing 'C:\Projects\Playground\obj\Playground.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\me\AppData\Local\Temp\tmp6785.tmp /verbosity:quiet /nologo C:\Projects\Playground\Playground.csproj
Writing 'C:\Projects\Playground\obj\Playground.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\me\AppData\Local\Temp\tmp6A26.tmp /verbosity:quiet /nologo C:\Projects\Playground\Playground.csproj
Build started...
dotnet build C:\Projects\Playground\Playground.csproj /verbosity:quiet /nologo

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.15
Build succeeded.
dotnet exec --depsfile C:\Projects\Playground\bin\Debug\net5.0\Playground.deps.json --additionalprobingpath C:\Users\me\.nuget\packages --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig C:\Projects\Playground\bin\Debug\net5.0\Playground.runtimeconfig.json C:\Users\me\.dotnet\tools\.store\dotnet-ef\5.0.0\dotnet-ef\5.0.0\tools\netcoreapp3.1\any\tools\netcoreapp2.0\any\ef.dll dbcontext list --assembly C:\Projects\Playground\bin\Debug\net5.0\Playground.dll --startup-assembly C:\Projects\Playground\bin\Debug\net5.0\Playground.dll --project-dir C:\Projects\Playground\ --language C# --working-dir C:\Projects\Playground --verbose --root-namespace Playground
Using assembly 'Playground'.
Using startup assembly 'Playground'.
Using application base 'C:\Projects\Playground\bin\Debug\net5.0'.
Using working directory 'C:\Projects\Playground'.
Using root namespace 'Playground'.
Using project directory 'C:\Projects\Playground\'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'Playground'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'Context'.

Provider and Version Information

EF Core version: 5.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: NET 5.0
Operating system: Windows 10 1909
IDE: Visual Studio 2019 16.8.1

@smitpatel
Copy link
Member

Duplicate of #23130
Resolved by #23143

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants