From 30bab91a6890f55dd8d4516b97f46304262280ed Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Sun, 18 Aug 2019 17:38:52 -0700 Subject: [PATCH] Throw if defining query is used on keyed entity type Fixes #17157 --- src/EFCore/Infrastructure/ModelValidator.cs | 32 +++++++++++++++---- src/EFCore/Properties/CoreStrings.Designer.cs | 8 +++++ src/EFCore/Properties/CoreStrings.resx | 3 ++ .../Infrastructure/ModelValidatorTest.cs | 9 ++++++ .../Metadata/Internal/EntityTypeTest.cs | 3 -- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index ce4b85d35fd..a48536fa859 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.DefiningQueryAndKey(entityType.DisplayName())); } } } diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index 122c6a0b152..c2788bc0e64 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 DefiningQueryAndKey([CanBeNull] object entityType) + => string.Format( + GetString("DefiningQueryAndKey", nameof(entityType)), + entityType); + /// /// Converter for model type '{converterType}' cannot be used for '{entityType}.{propertyName}' because its type is '{propertyType}'. /// diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 925419ea6e3..dc5f1cdd711 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}'. diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index 0ef7e9c682b..189540294af 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.DefiningQueryAndKey("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();