diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index ce4b85d35fd..587bdaad6e3 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -72,6 +72,7 @@ public virtual void Validate(IModel model, IDiagnosticsLogger + /// Validates the mapping/configuration of defining queries in the model. + /// + /// The model to validate. + /// The logger to use. + protected virtual void ValidateDefiningQuery( + [NotNull] IModel model, [NotNull] IDiagnosticsLogger logger) + { + Check.NotNull(model, nameof(model)); + + foreach (var entityType in model.GetEntityTypes()) + { + if (entityType.GetDefiningQuery() != null + && entityType.FindPrimaryKey() != null) + { + throw new InvalidOperationException( + CoreStrings.DefiningQueryWithKey(entityType.DisplayName())); } } } diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index 122c6a0b152..adaff17964d 100644 --- a/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/EFCore/Properties/CoreStrings.Designer.cs @@ -1630,6 +1630,14 @@ public static string BadFilterDerivedType([CanBeNull] object filter, [CanBeNull] GetString("BadFilterDerivedType", nameof(filter), nameof(entityType)), filter, entityType); + /// + /// The entity type '{entityType}' cannot use 'ToQuery' to create a defining query because it also defines a primary key. Defining queries can only be used to back entity types without keys. + /// + public static string DefiningQueryWithKey([CanBeNull] object entityType) + => string.Format( + GetString("DefiningQueryWithKey", nameof(entityType)), + entityType); + /// /// Converter for model type '{converterType}' cannot be used for '{entityType}.{propertyName}' because its type is '{propertyType}'. /// @@ -1970,14 +1978,6 @@ public static string InvalidSetKeylessOperation([CanBeNull] object entityType) GetString("InvalidSetKeylessOperation", nameof(entityType)), entityType); - /// - /// The entity type '{entityType}' cannot have a defining query because it has a primary key. Only keyless entity types can have a defining query. - /// - public static string NonKeylessEntityTypeDefiningQuery([CanBeNull] object entityType) - => string.Format( - GetString("NonKeylessEntityTypeDefiningQuery", nameof(entityType)), - entityType); - /// /// A '{derivedType}' cannot be configured as keyless because it is a derived type. The root type '{rootType}' must be configured as keyless. If you did not intend for '{rootType}' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model. /// diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 925419ea6e3..852db4e5ba3 100644 --- a/src/EFCore/Properties/CoreStrings.resx +++ b/src/EFCore/Properties/CoreStrings.resx @@ -891,6 +891,9 @@ The filter expression '{filter}' cannot be specified for entity type '{entityType}'. A filter may only be applied to the root entity type in a hierarchy. + + The entity type '{entityType}' cannot use 'ToQuery' to create a defining query because it also defines a primary key. Defining queries can only be used to back entity types without keys. + Converter for model type '{converterType}' cannot be used for '{entityType}.{propertyName}' because its type is '{propertyType}'. @@ -1120,9 +1123,6 @@ The invoked method is cannot be used for the entity type '{entityType}' because it does not have a primary key. - - The entity type '{entityType}' cannot have a defining query because it has a primary key. Only keyless entity types can have a defining query. - A '{derivedType}' cannot be configured as keyless because it is a derived type. The root type '{rootType}' must be configured as keyless. If you did not intend for '{rootType}' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model. diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index 0ef7e9c682b..a963aaadd41 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs @@ -31,6 +31,15 @@ public virtual void Detects_filter_on_derived_type() VerifyError(CoreStrings.BadFilterDerivedType(entityTypeD.GetQueryFilter(), entityTypeD.DisplayName()), modelBuilder.Model); } + [ConditionalFact] + public virtual void Detects_defining_query_on_keyed_entity_type() + { + var modelBuilder = CreateConventionalModelBuilder(); + modelBuilder.Entity().ToQuery(() => new List().AsQueryable()); + + VerifyError(CoreStrings.DefiningQueryWithKey("A"), modelBuilder.Model); + } + [ConditionalFact] public virtual void Detects_shadow_entities() { diff --git a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs index cf50680d7a5..c1eb9c2c974 100644 --- a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs @@ -65,12 +65,9 @@ private class FakeEntityType : IEntityType public string Name { get; } public Type ClrType { get; } public IEntityType BaseType { get; } - public bool IsKeyless { get; } public string DefiningNavigationName { get; } public IEntityType DefiningEntityType { get; } public LambdaExpression QueryFilter { get; } - public LambdaExpression DefiningQuery { get; } - public bool IsQueryType { get; } public IKey FindPrimaryKey() => throw new NotImplementedException(); public IKey FindKey(IReadOnlyList properties) => throw new NotImplementedException(); public IEnumerable GetKeys() => throw new NotImplementedException();