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

[5.0] Add validation for seed data with skip navigations #22908

Merged
merged 1 commit into from
Oct 7, 2020
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
13 changes: 8 additions & 5 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,29 +1104,32 @@ protected virtual void ValidateData([NotNull] IModel model, [NotNull] IDiagnosti
keyValues[i] = seedDatum[key.Properties[i].Name];
}

foreach (var navigation in entityType.GetNavigations())
foreach (var navigation in entityType.GetNavigations().Concat<INavigationBase>(entityType.GetSkipNavigations()))
{
if (seedDatum.TryGetValue(navigation.Name, out var value)
&& ((navigation.IsCollection && value is IEnumerable collection && collection.Any())
|| (!navigation.IsCollection && value != null)))
{
var foreignKey = navigation is INavigation nav
? nav.ForeignKey
: ((ISkipNavigation)navigation).ForeignKey;
if (sensitiveDataLogged)
{
throw new InvalidOperationException(
CoreStrings.SeedDatumNavigationSensitive(
entityType.DisplayName(),
string.Join(", ", key.Properties.Select((p, i) => p.Name + ":" + keyValues[i])),
navigation.Name,
navigation.TargetEntityType.DisplayName(),
navigation.ForeignKey.Properties.Format()));
foreignKey.DeclaringEntityType.DisplayName(),
foreignKey.Properties.Format()));
}

throw new InvalidOperationException(
CoreStrings.SeedDatumNavigation(
entityType.DisplayName(),
navigation.Name,
navigation.TargetEntityType.DisplayName(),
navigation.ForeignKey.Properties.Format()));
foreignKey.DeclaringEntityType.DisplayName(),
foreignKey.Properties.Format()));
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/EFCore/Metadata/Internal/EntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2944,7 +2944,10 @@ public virtual IEnumerable<IDictionary<string, object>> GetSeedData(bool provide

var data = new List<Dictionary<string, object>>();
var valueConverters = new Dictionary<string, ValueConverter>(StringComparer.Ordinal);
var properties = this.GetPropertiesAndNavigations().ToDictionary(p => p.Name);
var properties = GetProperties()
.Concat<IPropertyBase>(GetNavigations())
.Concat(GetSkipNavigations())
.ToDictionary(p => p.Name);
foreach (var rawSeed in _data)
{
var seed = new Dictionary<string, object>(StringComparer.Ordinal);
Expand All @@ -2958,13 +2961,10 @@ public virtual IEnumerable<IDictionary<string, object>> GetSeedData(bool provide
{
ValueConverter valueConverter = null;
if (providerValues
&& propertyBase is IProperty property
&& !valueConverters.TryGetValue(propertyBase.Name, out valueConverter))
{
if (propertyBase is IProperty property)
{
valueConverter = property.GetTypeMapping().Converter;
}

valueConverter = property.GetTypeMapping().Converter;
valueConverters[propertyBase.Name] = valueConverter;
}

Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/Properties/CoreStrings.Designer.cs

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

4 changes: 2 additions & 2 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1302,10 +1302,10 @@
<value>The seed entity for entity type '{entityType}' cannot be added because no value was provided for the required property '{property}'.</value>
</data>
<data name="SeedDatumNavigation" xml:space="preserve">
<value>The seed entity for entity type '{entityType}' cannot be added because it has the navigation '{navigation}' set. To seed relationships, add the related entity seed to '{relatedEntityType}' and specify the foreign key values {foreignKeyProperties}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.</value>
<value>The seed entity for entity type '{entityType}' cannot be added because it has the navigation '{navigation}' set. To seed relationships, add the entity seed to '{relatedEntityType}' and specify the foreign key values {foreignKeyProperties}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.</value>
</data>
<data name="SeedDatumNavigationSensitive" xml:space="preserve">
<value>The seed entity for entity type '{entityType}' with the key value '{keyValue}' cannot be added because it has the navigation '{navigation}' set. To seed relationships, add the related entity seed to '{relatedEntityType}' and specify the foreign key values {foreignKeyProperties}.</value>
<value>The seed entity for entity type '{entityType}' with the key value '{keyValue}' cannot be added because it has the navigation '{navigation}' set. To seed relationships, add the entity seed to '{relatedEntityType}' and specify the foreign key values {foreignKeyProperties}.</value>
</data>
<data name="SeedDatumSignedNumericValue" xml:space="preserve">
<value>The seed entity for entity type '{entityType}' cannot be added because a non-zero value is required for property '{property}'. Consider providing a negative value to avoid collisions with non-seed data.</value>
Expand Down
31 changes: 31 additions & 0 deletions test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,37 @@ public virtual void Detects_reference_navigations_in_seeds(bool sensitiveDataLog
modelBuilder.Model);
}

[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
public virtual void Detects_reference_navigations_in_seeds2(bool sensitiveDataLoggingEnabled)
{
var modelBuilder = CreateConventionalModelBuilder(sensitiveDataLoggingEnabled);
modelBuilder.Entity<Order>(
e =>
{
e.HasMany(o => o.Products)
.WithMany(p => p.Orders);
e.HasData(
new Order { Id = 1, Products = new List<Product> { new Product() } });
});

VerifyError(
sensitiveDataLoggingEnabled
? CoreStrings.SeedDatumNavigationSensitive(
nameof(Order),
$"{nameof(Order.Id)}:1",
nameof(Order.Products),
"OrderProduct (Dictionary<string, object>)",
"{'OrdersId'}")
: CoreStrings.SeedDatumNavigation(
nameof(Order),
nameof(Order.Products),
"OrderProduct (Dictionary<string, object>)",
"{'OrdersId'}"),
modelBuilder.Model);
}

[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
Expand Down