diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index a61e3593523..1329ff21646 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -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()) { @@ -139,30 +138,6 @@ protected virtual void ValidateEntityType( LogShadowProperties(entityType, logger); } - /// - /// Validates that no constructor-bound property is configured as not auto-loaded. - /// - /// The structural type to validate. - 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)); - } - } - } - /// /// Validates inheritance mapping for an entity type. /// @@ -196,7 +171,8 @@ protected virtual void ValidateProperty( } /// - /// 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. /// /// The property to validate. /// The structural type containing the property. @@ -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())); + } + } } /// @@ -251,7 +239,6 @@ protected virtual void ValidateComplexProperty( var complexType = complexProperty.ComplexType; ValidateChangeTrackingStrategy(complexType, logger); - ValidateConstructorBindingAutoLoaded(complexType); foreach (var property in complexType.GetDeclaredProperties()) { diff --git a/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs index 93a6e1b7304..2bbe52d34a4 100644 --- a/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs +++ b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs @@ -75,7 +75,7 @@ public Expression CreateMaterializeExpression( bindingInfo.ServiceInstances.Add(instanceVariable); var properties = new HashSet( - structuralType.GetProperties().Cast().Where(p => !p.IsShadowProperty() && p is not IProperty { IsAutoLoaded: false }) + structuralType.GetProperties().Where(p => !p.IsShadowProperty() && p.IsAutoLoaded).Cast() .Concat(structuralType.GetComplexProperties().Where(p => !p.IsShadowProperty()))); var blockExpressions = new List(); diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index f91c1a1297d..15de51f6f04 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs @@ -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( + eb => + { + eb.Property(e => e.Id); + eb.Property(e => e.Name); + }); + modelBuilder.Entity(); + + 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; } @@ -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; + } + } }