Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,22 @@ public Expression CreateMaterializeExpression(
// Creates a conditional expression that handles materialization of nullable complex types.
// For nullable complex types, the method checks if all scalar properties are null
// and returns default if they are, otherwise materializes the complex type instance.
// For required complex types, always materializes the instance even if all properties are null.
// If there's a required (non-nullable) property, only that property is checked for efficiency.
Expression HandleNullableComplexTypeMaterialization(
IComplexType complexType,
Type clrType,
Expression materializeExpression,
MethodCallExpression getValueBufferExpression)
{
// If the complex property is required, don't apply null-checking wrapper
if (!complexType.ComplexProperty.IsNullable)
{
return clrType.IsNullableType()
? Convert(materializeExpression, clrType)
: materializeExpression;
}

// Get all scalar properties of the complex type (including nested ones).
var allScalarProperties = complexType.GetFlattenedProperties().ToList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,81 @@ public class OptionalComplexProperty

#endregion Issue37337

#region NonOptionalComplexTypeViaLeftJoin

[ConditionalFact]
public virtual async Task Non_optional_complex_type_with_all_nullable_properties_via_left_join()
{
var contextFactory = await InitializeAsync<ContextNonOptionalComplexTypeViaLeftJoin>(
seed: context =>
{
context.Projects.Add(new ContextNonOptionalComplexTypeViaLeftJoin.Project
{
Properties = new List<ContextNonOptionalComplexTypeViaLeftJoin.ProjectProperty>
{
new ContextNonOptionalComplexTypeViaLeftJoin.ProjectLifetime
{
Lifetime = new ContextNonOptionalComplexTypeViaLeftJoin.Lifetime()
}
}
});
return context.SaveChangesAsync();
});

await using var context = contextFactory.CreateContext();

var project = await context.Projects.Include(p => p.Properties).SingleAsync();
var projectLifetime = (ContextNonOptionalComplexTypeViaLeftJoin.ProjectLifetime)project.Properties.Single();

Assert.NotNull(projectLifetime.Lifetime);
Assert.Null(projectLifetime.Lifetime.Start);
Assert.Null(projectLifetime.Lifetime.End);
}

private class ContextNonOptionalComplexTypeViaLeftJoin(DbContextOptions options) : DbContext(options)
{
public DbSet<Project> Projects { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProjectProperty>()
.HasOne(p => p.Project)
.WithMany(p => p.Properties)
.HasForeignKey(p => p.ProjectId);

modelBuilder.Entity<ProjectLifetime>()
.HasBaseType<ProjectProperty>()
.ComplexProperty(p => p.Lifetime)
.IsRequired(true);
}

public class Project
{
public int Id { get; set; }
public List<ProjectProperty> Properties { get; set; } = null!;
}

public class ProjectProperty
{
public int Id { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; } = null!;
}

public class ProjectLifetime : ProjectProperty
{
public Lifetime Lifetime { get; set; } = null!;
}

public class Lifetime
{
public DateTime? Start { get; init; }
public DateTime? End { get; init; }
}
}

#endregion NonOptionalComplexTypeViaLeftJoin

protected override string StoreName
=> "AdHocComplexTypeQueryTest";
}
Loading