diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index c2e1bf2821d..f107a1caeea 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -343,6 +343,11 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy foreach (var property in baseType.GetDeclaredProperties()) { + if (property.IsPrimaryKey()) + { + continue; + } + properties.Add(property.Name, property); } @@ -351,8 +356,6 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy } } - var originalValueProperties = new Dictionary(properties); - var storeGeneratedProperties = storeObjectIdentifier.StoreObjectType switch { StoreObjectType.InsertStoredProcedure @@ -418,6 +421,7 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy } } + var originalValueProperties = new Dictionary(properties); var parameterNames = new HashSet(); foreach (var parameter in sproc.Parameters) { @@ -615,7 +619,7 @@ protected virtual void ValidateBoolsWithDefaults( static bool IsNotNullAndFalse(object? value) => value != null - && (!(value is bool asBool) || asBool); + && (value is not bool asBool || asBool); } /// diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs index 7bba844d50f..5ef70fa3d50 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs @@ -44,7 +44,7 @@ public StoreStoredProcedureParameter( /// public virtual StoreStoredProcedure StoredProcedure => (StoreStoredProcedure)Table; - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -52,7 +52,7 @@ public virtual StoreStoredProcedure StoredProcedure /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual ParameterDirection Direction { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -60,7 +60,7 @@ public virtual StoreStoredProcedure StoredProcedure /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual int Position { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureReturnValue.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureReturnValue.cs index 60f1226df99..68d6099c5b0 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureReturnValue.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureReturnValue.cs @@ -37,7 +37,7 @@ public StoreStoredProcedureReturnValue( /// public virtual StoreStoredProcedure StoredProcedure => (StoreStoredProcedure)Table; - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs index 16baa9ecb62..86b2ad4fea7 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs @@ -39,7 +39,7 @@ public virtual IStoreStoredProcedure StoreStoredProcedure /// public virtual StoreObjectIdentifier StoredProcedureIdentifier { get; } - + /// public virtual ITableMapping? TableMapping { get; } diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs index 8ef042c8f43..cd0c21498f4 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs @@ -77,7 +77,7 @@ public virtual bool IsInModel /// public virtual void SetRemovedFromModel() => _builder = null; - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -94,7 +94,7 @@ public override bool IsReadOnly /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual StoredProcedure StoredProcedure { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -110,7 +110,7 @@ public override bool IsReadOnly /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual string? PropertyName { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -172,7 +172,7 @@ public virtual string SetName(string name, ConfigurationSource configurationSour return name; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -181,7 +181,7 @@ public virtual string SetName(string name, ConfigurationSource configurationSour /// public virtual ConfigurationSource? GetNameConfigurationSource() => _nameConfigurationSource; - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -208,7 +208,7 @@ public virtual ParameterDirection SetDirection(ParameterDirection direction, Con RelationalStrings.StoredProcedureParameterInvalidConfiguration( nameof(Direction), Name, ((IReadOnlyStoredProcedure)StoredProcedure).GetStoreIdentifier()?.DisplayName())); } - + if (!IsValid(direction)) { throw new InvalidOperationException(RelationalStrings.StoredProcedureParameterInvalidDirection( @@ -276,28 +276,28 @@ IReadOnlyStoredProcedure IReadOnlyStoredProcedureParameter.StoredProcedure [DebuggerStepThrough] get => StoredProcedure; } - + /// IMutableStoredProcedure IMutableStoredProcedureParameter.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// IConventionStoredProcedure IConventionStoredProcedureParameter.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// IStoredProcedure IStoredProcedureParameter.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// IConventionStoredProcedureParameterBuilder IConventionStoredProcedureParameter.Builder { @@ -308,7 +308,7 @@ IConventionStoredProcedureParameterBuilder IConventionStoredProcedureParameter.B /// string IConventionStoredProcedureParameter.SetName(string name, bool fromDataAnnotation) => SetName(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); - + /// ParameterDirection IConventionStoredProcedureParameter.SetDirection(ParameterDirection direction, bool fromDataAnnotation) => SetDirection(direction, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs index 7f2e62b9335..15f1ba67344 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs @@ -25,7 +25,7 @@ public StoredProcedureParameterMapping( : base(property, storeParameter, storedProcedureMapping) { Parameter = parameter; - } + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs index 626edeea3a2..871e9dcb278 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs @@ -16,7 +16,7 @@ public class StoredProcedureResultColumn : IRuntimeStoredProcedureResultColumn { private string _name = "RowsAffected"; - + private ConfigurationSource? _nameConfigurationSource; private InternalStoredProcedureResultColumnBuilder? _builder; @@ -66,7 +66,7 @@ public virtual bool IsInModel /// public virtual void SetRemovedFromModel() => _builder = null; - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -83,7 +83,7 @@ public override bool IsReadOnly /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual StoredProcedure StoredProcedure { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -99,7 +99,7 @@ public override bool IsReadOnly /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual string? PropertyName { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -136,21 +136,21 @@ public virtual string Name _name = name; _nameConfigurationSource = configurationSource.Max(_nameConfigurationSource); - + return name; } - + if (configurationSource == ConfigurationSource.Explicit) { GetProperty().SetColumnName(name, ((IReadOnlyStoredProcedure)StoredProcedure).GetStoreIdentifier()!.Value); return name; } - + return ((IConventionProperty)GetProperty()).SetColumnName( name, ((IReadOnlyStoredProcedure)StoredProcedure).GetStoreIdentifier()!.Value, fromDataAnnotation: configurationSource == ConfigurationSource.DataAnnotation); } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -195,28 +195,28 @@ IReadOnlyStoredProcedure IReadOnlyStoredProcedureResultColumn.StoredProcedure [DebuggerStepThrough] get => StoredProcedure; } - + /// IMutableStoredProcedure IMutableStoredProcedureResultColumn.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// IConventionStoredProcedure IConventionStoredProcedureResultColumn.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// IStoredProcedure IStoredProcedureResultColumn.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// IConventionStoredProcedureResultColumnBuilder IConventionStoredProcedureResultColumn.Builder { diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs index a8c300038b8..dfaea6244da 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs @@ -34,7 +34,7 @@ public StoredProcedureResultColumnMapping( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IStoredProcedureResultColumn ResultColumn { get; } - + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs b/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs index 9eb2ba6073c..8bb74d15c2b 100644 --- a/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs +++ b/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs @@ -132,7 +132,7 @@ IEntityType IStoredProcedure.EntityType [DebuggerStepThrough] get => _name; } - + /// string IStoredProcedure.Name { @@ -160,7 +160,7 @@ IReadOnlyList IReadOnlyStoredProcedure.Parame [DebuggerStepThrough] get => _parameters; } - + /// IReadOnlyList IStoredProcedure.Parameters { @@ -172,12 +172,12 @@ IReadOnlyList IStoredProcedure.Parameters IReadOnlyStoredProcedureParameter? IReadOnlyStoredProcedure.FindParameter(string propertyName) => _parameters.FirstOrDefault((IReadOnlyStoredProcedureParameter p) => p.ForOriginalValue == false && p.PropertyName == propertyName); - + /// [DebuggerStepThrough] IStoredProcedureParameter? IStoredProcedure.FindParameter(string propertyName) => (IStoredProcedureParameter?)((IReadOnlyStoredProcedure)this).FindParameter(propertyName); - + /// IReadOnlyStoredProcedureParameter? IReadOnlyStoredProcedure.FindOriginalValueParameter(string propertyName) => _parameters.FirstOrDefault((IReadOnlyStoredProcedureParameter p) @@ -186,12 +186,12 @@ IReadOnlyList IStoredProcedure.Parameters /// IStoredProcedureParameter? IStoredProcedure.FindOriginalValueParameter(string propertyName) => (IStoredProcedureParameter?)((IReadOnlyStoredProcedure)this).FindOriginalValueParameter(propertyName); - + /// IReadOnlyStoredProcedureParameter? IReadOnlyStoredProcedure.FindRowsAffectedParameter() => _parameters.FirstOrDefault((IStoredProcedureParameter p) => p.ForRowsAffected); - + /// IStoredProcedureParameter? IStoredProcedure.FindRowsAffectedParameter() => (IStoredProcedureParameter?)((IReadOnlyStoredProcedure)this).FindRowsAffectedParameter(); @@ -202,7 +202,7 @@ IReadOnlyList IReadOnlyStoredProcedure.Res [DebuggerStepThrough] get => _resultColumns; } - + /// IReadOnlyList IStoredProcedure.ResultColumns { @@ -214,7 +214,7 @@ IReadOnlyList IStoredProcedure.ResultColumns IReadOnlyStoredProcedureResultColumn? IReadOnlyStoredProcedure.FindResultColumn(string propertyName) => _resultColumns.FirstOrDefault((IReadOnlyStoredProcedureResultColumn c) => c.PropertyName == propertyName); - + /// IStoredProcedureResultColumn? IStoredProcedure.FindResultColumn(string propertyName) => (IStoredProcedureResultColumn?)((IReadOnlyStoredProcedure)this).FindResultColumn(propertyName); @@ -223,7 +223,7 @@ IReadOnlyList IStoredProcedure.ResultColumns IReadOnlyStoredProcedureResultColumn? IReadOnlyStoredProcedure.FindRowsAffectedResultColumn() => _resultColumns.FirstOrDefault((IReadOnlyStoredProcedureResultColumn c) => c.ForRowsAffected); - + /// IStoredProcedureResultColumn? IStoredProcedure.FindRowsAffectedResultColumn() => (IStoredProcedureResultColumn?)((IReadOnlyStoredProcedure)this).FindRowsAffectedResultColumn(); @@ -234,7 +234,7 @@ IStoreStoredProcedure IStoredProcedure.StoreStoredProcedure [DebuggerStepThrough] get => _storeStoredProcedure!; } - + /// IStoreStoredProcedure IRuntimeStoredProcedure.StoreStoredProcedure { diff --git a/src/EFCore.Relational/Metadata/RuntimeStoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/RuntimeStoredProcedureParameter.cs index 786611dd15d..00ce88d3c9d 100644 --- a/src/EFCore.Relational/Metadata/RuntimeStoredProcedureParameter.cs +++ b/src/EFCore.Relational/Metadata/RuntimeStoredProcedureParameter.cs @@ -39,7 +39,7 @@ public RuntimeStoredProcedureParameter( _name = name; _direction = direction; } - + /// /// Gets the stored procedure to which this parameter belongs. /// @@ -72,56 +72,56 @@ IReadOnlyStoredProcedure IReadOnlyStoredProcedureParameter.StoredProcedure [DebuggerStepThrough] get => StoredProcedure; } - + /// IStoredProcedure IStoredProcedureParameter.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// string IReadOnlyStoredProcedureParameter.Name { [DebuggerStepThrough] get => _name; } - + /// string? IReadOnlyStoredProcedureParameter.PropertyName { [DebuggerStepThrough] get => _propertyName; } - + /// ParameterDirection IReadOnlyStoredProcedureParameter.Direction { [DebuggerStepThrough] get => _direction; } - + /// bool? IReadOnlyStoredProcedureParameter.ForOriginalValue { [DebuggerStepThrough] get => _forOriginalValue; } - + /// bool IReadOnlyStoredProcedureParameter.ForRowsAffected { [DebuggerStepThrough] get => _forRowsAffected; } - + /// IStoreStoredProcedureParameter IStoredProcedureParameter.StoreParameter { [DebuggerStepThrough] get => _storeParameter!; } - + /// IStoreStoredProcedureParameter IRuntimeStoredProcedureParameter.StoreParameter { diff --git a/src/EFCore.Relational/Metadata/RuntimeStoredProcedureResultColumn.cs b/src/EFCore.Relational/Metadata/RuntimeStoredProcedureResultColumn.cs index cb2aedc7621..0fc1d0d0ccd 100644 --- a/src/EFCore.Relational/Metadata/RuntimeStoredProcedureResultColumn.cs +++ b/src/EFCore.Relational/Metadata/RuntimeStoredProcedureResultColumn.cs @@ -33,7 +33,7 @@ public RuntimeStoredProcedureResultColumn( _forRowsAffected = forRowsAffected; _name = name; } - + /// /// Gets the stored procedure to which this parameter belongs. /// @@ -66,42 +66,42 @@ IReadOnlyStoredProcedure IReadOnlyStoredProcedureResultColumn.StoredProcedure [DebuggerStepThrough] get => StoredProcedure; } - + /// IStoredProcedure IStoredProcedureResultColumn.StoredProcedure { [DebuggerStepThrough] get => StoredProcedure; } - + /// string IReadOnlyStoredProcedureResultColumn.Name { [DebuggerStepThrough] get => _name; } - + /// string? IReadOnlyStoredProcedureResultColumn.PropertyName { [DebuggerStepThrough] get => _propertyName; } - + /// bool IReadOnlyStoredProcedureResultColumn.ForRowsAffected { [DebuggerStepThrough] get => _forRowsAffected; } - + /// IStoreStoredProcedureResultColumn IStoredProcedureResultColumn.StoreResultColumn { [DebuggerStepThrough] get => _storeResultColumn!; } - + /// IStoreStoredProcedureResultColumn IRuntimeStoredProcedureResultColumn.StoreResultColumn { diff --git a/src/EFCore.Relational/Update/ColumnModificationParameters.cs b/src/EFCore.Relational/Update/ColumnModificationParameters.cs index f58d11e7287..5c7f8cae0f1 100644 --- a/src/EFCore.Relational/Update/ColumnModificationParameters.cs +++ b/src/EFCore.Relational/Update/ColumnModificationParameters.cs @@ -76,7 +76,7 @@ public readonly record struct ColumnModificationParameters /// Indicates whether the column is part of a primary or alternate key. /// public bool IsKey { get; init; } - + /// /// The column. /// @@ -138,7 +138,7 @@ public ColumnModificationParameters( GenerateParameterName = null; Entry = null; } - + /// /// Creates a new instance. /// diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index e629cebace4..f3407b5bdc4 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -2968,6 +2968,21 @@ public virtual void Detects_missing_stored_procedure_parameters_in_TPT() modelBuilder); } + [ConditionalFact] + public virtual void Detects_missing_stored_procedure_parameters_for_abstract_properties_in_TPT() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity().UseTptMappingStrategy(); + modelBuilder.Entity>() + .UpdateUsingStoredProcedure("Update", s => s + .HasParameter(a => a.Id)); + + VerifyError( + RelationalStrings.StoredProcedurePropertiesNotMapped("Generic", "Update", "{'P0', 'P1', 'P2', 'P3'}"), + modelBuilder); + } + [ConditionalFact] public virtual void Detects_unmatched_stored_procedure_parameters_in_TPT() {