-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
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