diff --git a/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs index 3a5f3b5826e..16d89c0f017 100644 --- a/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs @@ -822,7 +822,7 @@ public virtual EntityTypeBuilder HasQueryFilter(LambdaExpression? filter) /// The same builder instance so that multiple configuration calls can be chained. public virtual EntityTypeBuilder HasQueryFilter(string filterKey, LambdaExpression? filter) { - Builder.HasQueryFilter(new QueryFilter(filterKey, filter)); + Builder.HasQueryFilter(new QueryFilter(filterKey, filter, ConfigurationSource.Explicit)); return this; } diff --git a/src/EFCore/Metadata/Internal/QueryFilter.cs b/src/EFCore/Metadata/Internal/QueryFilter.cs index 460e7065daf..1187c5983ef 100644 --- a/src/EFCore/Metadata/Internal/QueryFilter.cs +++ b/src/EFCore/Metadata/Internal/QueryFilter.cs @@ -8,7 +8,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal; /// /// The key of the query filter. /// The expression representing the filter. -public class QueryFilter(string? key, LambdaExpression? expression) : IQueryFilter +/// The configuration source of the query filter. +public class QueryFilter(string? key, LambdaExpression? expression, ConfigurationSource configurationSource) : IQueryFilter { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -32,7 +33,7 @@ public class QueryFilter(string? key, LambdaExpression? expression) : IQueryFilt /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual ConfigurationSource? ConfigurationSource { get; set; } + public virtual ConfigurationSource ConfigurationSource { get; set; } = configurationSource; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -41,27 +42,7 @@ public class QueryFilter(string? key, LambdaExpression? expression) : IQueryFilt /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public QueryFilter(LambdaExpression? expression, ConfigurationSource configurationSource) - : this(null, expression) - => ConfigurationSource = configurationSource; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public QueryFilter(string key, LambdaExpression? expression, ConfigurationSource configurationSource) - : this(key, expression) - => ConfigurationSource = configurationSource; - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public QueryFilter(LambdaExpression? expression) - : this(null, expression) + : this(null, expression, configurationSource) { } @@ -72,10 +53,9 @@ public QueryFilter(LambdaExpression? expression) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public QueryFilter(string key, LambdaExpression? expression, bool fromDataAnnotation) - : this(key, expression) - => ConfigurationSource = fromDataAnnotation - ? Metadata.ConfigurationSource.DataAnnotation - : Metadata.ConfigurationSource.Convention; + : this(key, expression, fromDataAnnotation ? Metadata.ConfigurationSource.DataAnnotation : Metadata.ConfigurationSource.Convention) + { + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -84,8 +64,7 @@ public QueryFilter(string key, LambdaExpression? expression, bool fromDataAnnota /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public QueryFilter(LambdaExpression? expression, bool fromDataAnnotation) - : this(expression) - => ConfigurationSource = fromDataAnnotation - ? Metadata.ConfigurationSource.DataAnnotation - : Metadata.ConfigurationSource.Convention; + : this(null, expression, fromDataAnnotation ? Metadata.ConfigurationSource.DataAnnotation : Metadata.ConfigurationSource.Convention) + { + } } diff --git a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs index d6ad38bd6e1..e96a0e82c7f 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs @@ -3392,6 +3392,31 @@ public void Can_replace_named_query_filter_only_with_lower_or_equal_source() Assert.NotEqual(filterExpression2, entityBuilder.Metadata.FindDeclaredQueryFilter(filterKey).Expression); } + [ConditionalFact] + public void Can_override_named_query_filter_from_convention_with_explicit_configuration() + { + // This test verifies that named query filters set by conventions can be overridden by explicit configuration + var modelBuilder = CreateModelBuilder(); + var entityBuilder = modelBuilder.Entity(typeof(Order), ConfigurationSource.Explicit); + + LambdaExpression conventionFilter = (Order o) => o.Id == 1; + LambdaExpression explicitFilter = (Order o) => o.Id == 2; + const string filterKey = "testFilter"; + + // Convention sets a named query filter + entityBuilder.HasQueryFilter(new QueryFilter(filterKey, conventionFilter, ConfigurationSource.Convention)); + Assert.Same(conventionFilter, entityBuilder.Metadata.FindDeclaredQueryFilter(filterKey).Expression); + Assert.Equal(ConfigurationSource.Convention, entityBuilder.Metadata.GetQueryFilterConfigurationSource(filterKey)); + + // Public API should be able to override it + var publicBuilder = new EntityTypeBuilder(entityBuilder.Metadata); + publicBuilder.HasQueryFilter(filterKey, explicitFilter); + + // Verify the filter was replaced with the explicit one + Assert.Same(explicitFilter, entityBuilder.Metadata.FindDeclaredQueryFilter(filterKey).Expression); + Assert.Equal(ConfigurationSource.Explicit, entityBuilder.Metadata.GetQueryFilterConfigurationSource(filterKey)); + } + private static TestLogger CreateTestLogger() => new() { EnabledFor = LogLevel.Warning };