Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved exception messages #26398

Merged
merged 2 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1525,5 +1525,31 @@ protected virtual void ValidateIndexProperties(
}
}
}

/// <summary>
/// Throws an <see cref="InvalidOperationException"/> with a message containing provider-specific information, when
/// available, indicating possible reasons why the property cannot be mapped.
/// </summary>
/// <param name="propertyType">The property CLR type.</param>
/// <param name="entityType">The entity type.</param>
/// <param name="unmappedProperty">The property.</param>
protected override void ThrowPropertyNotMappedException(
string propertyType,
IConventionEntityType entityType,
IConventionProperty unmappedProperty)
{
var storeType = unmappedProperty.GetColumnType();
if (storeType != null)
{
throw new InvalidOperationException(
RelationalStrings.PropertyNotMapped(
propertyType,
entityType.DisplayName(),
unmappedProperty.Name,
storeType));
}

base.ThrowPropertyNotMappedException(propertyType, entityType, unmappedProperty);
}
}
}

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

3 changes: 3 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,9 @@
<data name="ProjectionMappingCountMismatch" xml:space="preserve">
<value>Unable to translate set operations when both sides don't assign values to the same properties in the nominal type. Please make sure that the same properties are included on both sides, and consider assigning default values if a property doesn't require a specific value.</value>
</data>
<data name="PropertyNotMapped" xml:space="preserve">
<value>The '{propertyType}' property '{entityType}.{property}' could not be mapped to the database type '{storeType}' because the database provider does not support mapping '{propertyType}' properties to '{storeType}' columns. Consider mapping to a different database type or converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.</value>
</data>
<data name="PropertyNotMappedToTable" xml:space="preserve">
<value>The property '{property}' on entity type '{entityType}' is not mapped to '{table}'.</value>
</data>
Expand Down
4 changes: 4 additions & 0 deletions src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,10 @@ public InternalEntityEntry PrepareToSave()
&& property.IsForeignKey()
&& _stateData.IsPropertyFlagged(property.GetIndex(), PropertyFlag.Unknown))
{
if (property.GetContainingForeignKeys().Any(fk => fk.IsOwnership))
{
throw new InvalidOperationException(CoreStrings.SaveOwnedWithoutOwner(entityType.DisplayName()));
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved
}
throw new InvalidOperationException(CoreStrings.UnknownKeyValue(entityType.DisplayName(), property.Name));
}
}
Expand Down
25 changes: 21 additions & 4 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ protected virtual void ValidatePropertyMapping(

if (unmappedProperty != null)
{
throw new InvalidOperationException(
CoreStrings.PropertyNotMapped(
entityType.DisplayName(), unmappedProperty.Name,
(unmappedProperty.GetValueConverter()?.ProviderClrType ?? unmappedProperty.ClrType).ShortDisplayName()));
ThrowPropertyNotMappedException(
(unmappedProperty.GetValueConverter()?.ProviderClrType ?? unmappedProperty.ClrType).ShortDisplayName(),
entityType,
unmappedProperty);
}

if (entityType.ClrType == Model.DefaultPropertyBagType)
Expand Down Expand Up @@ -287,6 +287,23 @@ protected virtual void ValidatePropertyMapping(
}
}

/// <summary>
/// Throws an <see cref="InvalidOperationException"/> with a message containing provider-specific information, when
/// available, indicating possible reasons why the property cannot be mapped.
/// </summary>
/// <param name="propertyType">The property CLR type.</param>
/// <param name="entityType">The entity type.</param>
/// <param name="unmappedProperty">The property.</param>
protected virtual void ThrowPropertyNotMappedException(
string propertyType,
IConventionEntityType entityType,
IConventionProperty unmappedProperty)
=> throw new InvalidOperationException(
CoreStrings.PropertyNotMapped(
propertyType,
entityType.DisplayName(),
unmappedProperty.Name));

/// <summary>
/// Returns a value indicating whether that target CLR type would correspond to an owned entity type.
/// </summary>
Expand Down
16 changes: 6 additions & 10 deletions src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,18 @@ private void GetBindings(
var constructorErrors = bindingFailures.SelectMany(f => f)
.GroupBy(f => (ConstructorInfo)f.Member)
.Select(
x => CoreStrings.ConstructorBindingFailed(
x => " " + CoreStrings.ConstructorBindingFailed(
string.Join("', '", x.Select(f => f.Name)),
entityType.DisplayName()
+ "("
+ string.Join(
", ", x.Key.GetParameters().Select(
y => y.ParameterType.ShortDisplayName() + " " + y.Name)
)
+ ")"
)
$"{entityType.DisplayName()}({string.Join(", ", ConstructConstructor(x))})")
);

IEnumerable<string> ConstructConstructor(IGrouping<ConstructorInfo, ParameterInfo> parameters)
=> parameters.Key.GetParameters().Select(y => $"{y.ParameterType.ShortDisplayName()} {y.Name}");

throw new InvalidOperationException(
CoreStrings.ConstructorNotFound(
entityType.DisplayName(),
string.Join("; ", constructorErrors)));
Environment.NewLine + string.Join(Environment.NewLine, constructorErrors) + Environment.NewLine));
}

if (foundBindings.Count > 1)
Expand Down
20 changes: 14 additions & 6 deletions src/EFCore/Properties/CoreStrings.Designer.cs

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

9 changes: 6 additions & 3 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -305,13 +305,13 @@
<value>Cannot create a relationship between '{newPrincipalNavigationSpecification}' and '{newDependentNavigationSpecification}' because a relationship already exists between '{existingPrincipalNavigationSpecification}' and '{existingDependentNavigationSpecification}'. Navigations can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation '{newDependentNavigationSpecification}' first in 'OnModelCreating'.</value>
</data>
<data name="ConstructorBindingFailed" xml:space="preserve">
<value>cannot bind '{failedBinds}' in '{parameters}'</value>
<value>Cannot bind '{failedBinds}' in '{parameters}'</value>
</data>
<data name="ConstructorConflict" xml:space="preserve">
<value>The constructors '{firstConstructor}' and '{secondConstructor}' have the same number of parameters, and can both be used by Entity Framework. The constructor to be used must be configured in 'OnModelCreating'.</value>
</data>
<data name="ConstructorNotFound" xml:space="preserve">
<value>No suitable constructor was found for entity type '{entityType}'. The following constructors had parameters that could not be bound to properties of the entity type: {constructors}.</value>
<value>No suitable constructor was found for entity type '{entityType}'. The following constructors had parameters that could not be bound to properties of the entity type: {constructors}Note that only mapped properties can be bound to constructor parameters. Navigations to related entities, including references to owned types, cannot be bound.</value>
</data>
<data name="ContextDisposed" xml:space="preserve">
<value>Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.</value>
Expand Down Expand Up @@ -1290,7 +1290,7 @@
<value>The property '{1_entityType}.{0_property}' could not be found. Ensure that the property exists and has been included in the model.</value>
</data>
<data name="PropertyNotMapped" xml:space="preserve">
<value>The property '{entityType}.{property}' is of type '{propertyType}' which is not supported by the current database provider. Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.</value>
<value>The '{propertyType}' property '{entityType}.{property}' could not be mapped because the database provider does not support this type. Consider converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.</value>
</data>
<data name="PropertyReadOnlyAfterSave" xml:space="preserve">
<value>The property '{1_entityType}.{0_property}' is defined as read-only after it has been saved, but its value has been modified or marked as modified.</value>
Expand Down Expand Up @@ -1373,6 +1373,9 @@
<data name="RuntimeParameterMissingParameter" xml:space="preserve">
<value>While registering a runtime parameter, the lambda expression must have only one parameter which must be same as 'QueryCompilationContext.QueryContextParameter' expression.</value>
</data>
<data name="SaveOwnedWithoutOwner" xml:space="preserve">
<value>Cannot save instance of '{entityType}' because it is an owned entity without any reference to its owner. Owned entities can only be saved as part of an aggregate also including the owner entity.</value>
</data>
<data name="SavepointsNotSupported" xml:space="preserve">
<value>Savepoints are not supported by the database provider in use.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public void Throws_when_added_property_is_not_mapped_to_store()

Assert.Equal(
CoreStrings.PropertyNotMapped(
typeof(NonPrimitiveAsPropertyEntity).ShortDisplayName(), "LongProperty", typeof(Tuple<long>).ShortDisplayName()),
typeof(Tuple<long>).ShortDisplayName(),
typeof(NonPrimitiveAsPropertyEntity).ShortDisplayName(),
"LongProperty"),
Assert.Throws<InvalidOperationException>(() => Validate(modelBuilder)).Message);
}

Expand All @@ -33,9 +35,11 @@ public void Throws_when_added_property_is_not_mapped_to_store_even_if_configured
.HasColumnType("some_int_mapping");

Assert.Equal(
CoreStrings.PropertyNotMapped(
typeof(NonPrimitiveNonNavigationAsPropertyEntity).ShortDisplayName(), "LongProperty",
typeof(Tuple<long>).ShortDisplayName()),
RelationalStrings.PropertyNotMapped(
typeof(Tuple<long>).ShortDisplayName(),
typeof(NonPrimitiveNonNavigationAsPropertyEntity).ShortDisplayName(),
"LongProperty",
"some_int_mapping"),
Assert.Throws<InvalidOperationException>(() => Validate(modelBuilder)).Message);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public override void Detects_key_property_which_cannot_be_compared()
});

VerifyError(
CoreStrings.PropertyNotMapped(nameof(WithNonComparableKey), nameof(WithNonComparableKey.Id), nameof(NotComparable)),
CoreStrings.PropertyNotMapped(nameof(NotComparable), nameof(WithNonComparableKey), nameof(WithNonComparableKey.Id)),
modelBuilder);
}

Expand All @@ -47,7 +47,7 @@ public override void Detects_unique_index_property_which_cannot_be_compared()

VerifyError(
CoreStrings.PropertyNotMapped(
nameof(WithNonComparableUniqueIndex), nameof(WithNonComparableUniqueIndex.Index), nameof(NotComparable)),
nameof(NotComparable), nameof(WithNonComparableUniqueIndex), nameof(WithNonComparableUniqueIndex.Index)),
modelBuilder);
}

Expand All @@ -64,7 +64,7 @@ public override void Ignores_normal_property_which_cannot_be_compared()

VerifyError(
CoreStrings.PropertyNotMapped(
nameof(WithNonComparableNormalProperty), nameof(WithNonComparableNormalProperty.Foo), nameof(NotComparable)),
nameof(NotComparable), nameof(WithNonComparableNormalProperty), nameof(WithNonComparableNormalProperty.Foo)),
modelBuilder);
}

Expand Down
Loading