Skip to content

Commit

Permalink
Throw if defining query is used on keyed entity type
Browse files Browse the repository at this point in the history
Fixes #17157
  • Loading branch information
ajcvickers committed Aug 19, 2019
1 parent 8d79f6d commit ad2bf07
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 21 deletions.
34 changes: 27 additions & 7 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public virtual void Validate(IModel model, IDiagnosticsLogger<DbLoggerCategory.M
ValidateFieldMapping(model, logger);
ValidateKeylessTypes(model, logger);
ValidateQueryFilters(model, logger);
ValidateDefiningQuery(model, logger);
ValidateData(model, logger);
LogShadowProperties(model, logger);
}
Expand Down Expand Up @@ -811,7 +812,7 @@ protected virtual void ValidateKeylessTypes([NotNull] IModel model, [NotNull] ID
if (entityType.FindPrimaryKey() != null)
{
throw new InvalidOperationException(
CoreStrings.NonKeylessEntityTypeDefiningQuery(entityType.DisplayName()));
CoreStrings.DefiningQueryWithKey(entityType.DisplayName()));
}
}
}
Expand All @@ -828,13 +829,32 @@ protected virtual void ValidateQueryFilters([NotNull] IModel model, [NotNull] ID

foreach (var entityType in model.GetEntityTypes())
{
if (entityType.GetQueryFilter() != null)
if (entityType.GetQueryFilter() != null
&& entityType.BaseType != null)
{
if (entityType.BaseType != null)
{
throw new InvalidOperationException(
CoreStrings.BadFilterDerivedType(entityType.GetQueryFilter(), entityType.DisplayName()));
}
throw new InvalidOperationException(
CoreStrings.BadFilterDerivedType(entityType.GetQueryFilter(), entityType.DisplayName()));
}
}
}

/// <summary>
/// Validates the mapping/configuration of defining queries in the model.
/// </summary>
/// <param name="model"> The model to validate. </param>
/// <param name="logger"> The logger to use. </param>
protected virtual void ValidateDefiningQuery(
[NotNull] IModel model, [NotNull] IDiagnosticsLogger<DbLoggerCategory.Model.Validation> 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()));
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/EFCore/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,9 @@
<data name="BadFilterDerivedType" xml:space="preserve">
<value>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.</value>
</data>
<data name="DefiningQueryWithKey" xml:space="preserve">
<value>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.</value>
</data>
<data name="ConverterPropertyMismatch" xml:space="preserve">
<value>Converter for model type '{converterType}' cannot be used for '{entityType}.{propertyName}' because its type is '{propertyType}'.</value>
</data>
Expand Down Expand Up @@ -1120,9 +1123,6 @@
<data name="InvalidSetKeylessOperation" xml:space="preserve">
<value>The invoked method is cannot be used for the entity type '{entityType}' because it does not have a primary key.</value>
</data>
<data name="NonKeylessEntityTypeDefiningQuery" xml:space="preserve">
<value>The entity type '{entityType}' cannot have a defining query because it has a primary key. Only keyless entity types can have a defining query.</value>
</data>
<data name="DerivedEntityTypeHasNoKey" xml:space="preserve">
<value>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.</value>
</data>
Expand Down
9 changes: 9 additions & 0 deletions test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<A>().ToQuery(() => new List<A>().AsQueryable());

VerifyError(CoreStrings.DefiningQueryWithKey("A"), modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_shadow_entities()
{
Expand Down
3 changes: 0 additions & 3 deletions test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IProperty> properties) => throw new NotImplementedException();
public IEnumerable<IKey> GetKeys() => throw new NotImplementedException();
Expand Down

0 comments on commit ad2bf07

Please sign in to comment.