Skip to content

Constructor calls when using inheritance with owned entities causes exceptions #31353

@Jejuni

Description

@Jejuni

Description and Code

When using owned entities it is fairly normal to use some sort of constructor validation, like throwing ArgumentNullException for null values.
When an owned type like this is used in an inheritance hierarchy on a child EfCore tries to call the constructor with null values for some reason when the parent DbSet is queried. This then leads to a ArgumentNullException.
This only happens when entities of another type that actually have NULL values in their columns are also present in the table.

Repro

A repo for the problem can be found here: https://github.com/Jejuni/EfCoreInheritanceProblem

Replace the SqlServer connection string in MyDbContext with a valid one for your machine.

Code

Given these entities:

public class Name
{
    public Name(string firstName, string lastName)
    {
        FirstName = firstName ?? throw new ArgumentNullException(nameof(firstName));
        LastName = lastName ?? throw new ArgumentNullException(nameof(lastName));
    }

    public string FirstName { get; }
    public string LastName { get; }
}


public abstract class Parent
{
    public Guid Id { get; set; }
}

public class ChildOne : Parent
{
    public int Number { get; set; }
}

public class ChildTwo : Parent
{
    public Name Name { get; set; }
}

and this configuration:

modelBuilder.Entity<Parent>(b =>
        {
            b.HasDiscriminator<string>("Discriminator")
                .HasValue<ChildOne>("one")
                .HasValue<ChildTwo>("two");
        });

        modelBuilder.Entity<ChildTwo>(b =>
        {
            b.OwnsOne(x => x.Name, nameBuilder =>
            {
                nameBuilder.Property(x => x.FirstName);
                nameBuilder.Property(x => x.LastName);
            });
        });

leads to an exception when the following code is run:

await using (var ctx = new MyDbContext())
{
    await ctx.Database.EnsureDeletedAsync();
    await ctx.Database.EnsureCreatedAsync();
}

await using (var ctx2 = new MyDbContext())
{
    ctx2.ChildOnes.Add(new ChildOne { Number = 2 });

    await ctx2.SaveChangesAsync();
}

await using (var ctx3 = new MyDbContext())
{
    var childOnes = await ctx3.ChildOnes.ToListAsync();
    var childTwos = await ctx3.ChildTwos.ToListAsync();
    // NEXT LINE CAUSES EXCEPTION
    var parents = await ctx3.Parents.ToListAsync();
}

Include stack traces

Stack trace shows the code thrown from user error during execution of SingleQueryingEnumerable:

System.ArgumentNullException
  HResult=0x80004003
  Message=Value cannot be null. (Parameter 'firstName')
  Source=EfCoreInheritanceProblem
  StackTrace:
   at EfCoreInheritanceProblem.Name..ctor(String firstName, String lastName) in Whatever\EfCoreInheritanceProblem\EfCoreInheritanceProblem\Entities.cs:line 7
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext()

Include provider and version information

EF Core version:
Database provider: Microsoft.EntityFrameworkCore.SqlServer, 7.0.9
Target framework: .NET 7.0
Operating system: Windows 11
IDE: Visual Studio 2022 17.6.5

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions