Skip to content
Open
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
41 changes: 14 additions & 27 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ protected virtual void ValidateEntityType(
ValidateInheritanceMapping(entityType, logger);
ValidateFieldMapping(entityType, logger);
ValidateQueryFilters(entityType, logger);
ValidateConstructorBindingAutoLoaded(entityType);

foreach (var property in entityType.GetDeclaredProperties())
{
Expand Down Expand Up @@ -139,30 +138,6 @@ protected virtual void ValidateEntityType(
LogShadowProperties(entityType, logger);
}

/// <summary>
/// Validates that no constructor-bound property is configured as not auto-loaded.
/// </summary>
/// <param name="structuralType">The structural type to validate.</param>
protected virtual void ValidateConstructorBindingAutoLoaded(ITypeBase structuralType)
{
if (structuralType.ConstructorBinding is null)
{
return;
}

var typeName = structuralType.DisplayName();

foreach (var consumedProperty in structuralType.ConstructorBinding.ParameterBindings
.SelectMany(p => p.ConsumedProperties))
{
if (consumedProperty is IProperty { IsAutoLoaded: false } property)
{
throw new InvalidOperationException(
CoreStrings.AutoLoadedConstructorProperty(property.Name, typeName));
}
}
}

/// <summary>
/// Validates inheritance mapping for an entity type.
/// </summary>
Expand Down Expand Up @@ -196,7 +171,8 @@ protected virtual void ValidateProperty(
}

/// <summary>
/// Validates that a property configured as not auto-loaded is not a key, foreign key, concurrency token or discriminator.
/// Validates that a property configured as not auto-loaded is not a key, foreign key, concurrency token, discriminator,
/// or consumed by a constructor binding.
/// </summary>
/// <param name="property">The property to validate.</param>
/// <param name="structuralType">The structural type containing the property.</param>
Expand Down Expand Up @@ -237,6 +213,18 @@ protected virtual void ValidateAutoLoaded(
throw new InvalidOperationException(
CoreStrings.AutoLoadedDiscriminatorProperty(property.Name, typeName));
}

foreach (var derivedType in structuralType.GetDerivedTypesInclusive())
{
if (derivedType.ConstructorBinding is not null
&& derivedType.ConstructorBinding.ParameterBindings
.SelectMany(p => p.ConsumedProperties)
.Contains(property))
{
throw new InvalidOperationException(
CoreStrings.AutoLoadedConstructorProperty(property.Name, derivedType.DisplayName()));
}
}
}

/// <summary>
Expand All @@ -251,7 +239,6 @@ protected virtual void ValidateComplexProperty(
var complexType = complexProperty.ComplexType;

ValidateChangeTrackingStrategy(complexType, logger);
ValidateConstructorBindingAutoLoaded(complexType);

foreach (var property in complexType.GetDeclaredProperties())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public Expression CreateMaterializeExpression(
bindingInfo.ServiceInstances.Add(instanceVariable);

var properties = new HashSet<IPropertyBase>(
structuralType.GetProperties().Cast<IPropertyBase>().Where(p => !p.IsShadowProperty() && p is not IProperty { IsAutoLoaded: false })
structuralType.GetProperties().Where(p => !p.IsShadowProperty() && p.IsAutoLoaded).Cast<IPropertyBase>()
.Concat(structuralType.GetComplexProperties().Where(p => !p.IsShadowProperty())));

var blockExpressions = new List<Expression>();
Expand Down
35 changes: 35 additions & 0 deletions test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2392,6 +2392,27 @@ public virtual void Detects_constructor_bound_property_not_auto_loaded()
modelBuilder);
}

[ConditionalFact]
public virtual void Detects_derived_constructor_bound_property_not_auto_loaded()
{
var modelBuilder = CreateConventionModelBuilder();
modelBuilder.Entity<AutoLoadBaseEntity>(
eb =>
{
eb.Property(e => e.Id);
eb.Property(e => e.Name);
});
modelBuilder.Entity<AutoLoadDerivedEntityWithConstructor>();

var model = modelBuilder.Model;
var property = model.FindEntityType(typeof(AutoLoadBaseEntity))!.FindProperty(nameof(AutoLoadBaseEntity.Name))!;
property.IsAutoLoaded = false;

VerifyError(
CoreStrings.AutoLoadedConstructorProperty(nameof(AutoLoadBaseEntity.Name), nameof(AutoLoadDerivedEntityWithConstructor)),
modelBuilder);
}

protected class AutoLoadEntity
{
public int Id { get; set; }
Expand Down Expand Up @@ -2420,4 +2441,18 @@ protected class AutoLoadDependent
public int Id { get; set; }
public int PrincipalId { get; set; }
}

protected class AutoLoadBaseEntity
{
public int Id { get; set; }
public string Name { get; set; } = null!;
}

protected class AutoLoadDerivedEntityWithConstructor : AutoLoadBaseEntity
{
public AutoLoadDerivedEntityWithConstructor(string name)
{
Name = name;
}
}
}
Loading