diff --git a/src/EFCore.Design/Design/Internal/CSharpHelper.cs b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
index 878df46c822..67edc61b5c0 100644
--- a/src/EFCore.Design/Design/Internal/CSharpHelper.cs
+++ b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
@@ -1358,8 +1358,7 @@ private static bool IsIdentifierPartCharacter(char ch)
return ch < 'A'
? ch >= '0'
&& ch <= '9'
- : ch <= 'Z'
- || ch == '_';
+ : ch <= 'Z';
}
if (ch <= 'z')
diff --git a/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs b/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
index b3b5753082a..4329a6da959 100644
--- a/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
+++ b/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
@@ -35,7 +35,8 @@ public class AnnotationCodeGenerator : IAnnotationCodeGenerator
RelationalAnnotationNames.InsertStoredProcedure,
RelationalAnnotationNames.UpdateStoredProcedure,
RelationalAnnotationNames.MappingFragments,
- RelationalAnnotationNames.RelationalOverrides
+ RelationalAnnotationNames.RelationalOverrides,
+ RelationalAnnotationNames.ParameterDirection
};
#region MethodInfos
diff --git a/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs
index 1ea54b25765..fc24006ed32 100644
--- a/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs
+++ b/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs
@@ -310,6 +310,9 @@ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCod
annotations.Remove(RelationalAnnotationNames.ViewMappings);
annotations.Remove(RelationalAnnotationNames.SqlQueryMappings);
annotations.Remove(RelationalAnnotationNames.FunctionMappings);
+ annotations.Remove(RelationalAnnotationNames.InsertStoredProcedureMappings);
+ annotations.Remove(RelationalAnnotationNames.DeleteStoredProcedureMappings);
+ annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureMappings);
annotations.Remove(RelationalAnnotationNames.DefaultMappings);
}
else
@@ -360,6 +363,42 @@ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCod
GenerateSimpleAnnotation(RelationalAnnotationNames.Triggers, triggersVariable, parameters);
}
+
+ if (annotations.TryGetAndRemove(
+ RelationalAnnotationNames.InsertStoredProcedure,
+ out StoredProcedure insertStoredProcedure))
+ {
+ var sprocVariable = Dependencies.CSharpHelper.Identifier("insertSproc", parameters.ScopeVariables, capitalize: false);
+
+ Create(insertStoredProcedure, sprocVariable, parameters);
+
+ GenerateSimpleAnnotation(RelationalAnnotationNames.InsertStoredProcedure, sprocVariable, parameters);
+ parameters.MainBuilder.AppendLine();
+ }
+
+ if (annotations.TryGetAndRemove(
+ RelationalAnnotationNames.DeleteStoredProcedure,
+ out StoredProcedure deleteStoredProcedure))
+ {
+ var sprocVariable = Dependencies.CSharpHelper.Identifier("deleteSproc", parameters.ScopeVariables, capitalize: false);
+
+ Create(deleteStoredProcedure, sprocVariable, parameters);
+
+ GenerateSimpleAnnotation(RelationalAnnotationNames.DeleteStoredProcedure, sprocVariable, parameters);
+ parameters.MainBuilder.AppendLine();
+ }
+
+ if (annotations.TryGetAndRemove(
+ RelationalAnnotationNames.UpdateStoredProcedure,
+ out StoredProcedure updateStoredProcedure))
+ {
+ var sprocVariable = Dependencies.CSharpHelper.Identifier("updateSproc", parameters.ScopeVariables, capitalize: false);
+
+ Create(updateStoredProcedure, sprocVariable, parameters);
+
+ GenerateSimpleAnnotation(RelationalAnnotationNames.UpdateStoredProcedure, sprocVariable, parameters);
+ parameters.MainBuilder.AppendLine();
+ }
}
base.Generate(entityType, parameters);
@@ -439,6 +478,49 @@ private void Create(ITrigger trigger, string triggersVariable, CSharpRuntimeAnno
public virtual void Generate(ITrigger trigger, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
=> GenerateSimpleAnnotations(parameters);
+ private void Create(IStoredProcedure storedProcedure, string sprocVariable, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ {
+ AddNamespace(typeof(RuntimeStoredProcedure), parameters.Namespaces);
+ var code = Dependencies.CSharpHelper;
+ var mainBuilder = parameters.MainBuilder;
+ mainBuilder
+ .Append("var ").Append(sprocVariable).AppendLine(" = new RuntimeStoredProcedure(").IncrementIndent()
+ .Append(parameters.TargetName).AppendLine(",")
+ .Append(code.Literal(storedProcedure.Name)).AppendLine(",")
+ .Append(code.Literal(storedProcedure.Schema)).AppendLine(",")
+ .Append(code.Literal(storedProcedure.AreTransactionsSuppressed))
+ .AppendLine(");")
+ .DecrementIndent()
+ .AppendLine();
+
+ foreach (var parameter in storedProcedure.Parameters)
+ {
+ mainBuilder.Append(sprocVariable).Append(".AddParameter(")
+ .Append(code.Literal(parameter))
+ .AppendLine(");");
+ }
+
+ foreach (var resultColumn in storedProcedure.ResultColumns)
+ {
+ mainBuilder.Append(sprocVariable).Append(".AddResultColumn(")
+ .Append(code.Literal(resultColumn))
+ .AppendLine(");");
+ }
+
+ CreateAnnotations(
+ storedProcedure,
+ Generate,
+ parameters with { TargetName = sprocVariable });
+ }
+
+ ///
+ /// Generates code to create the given annotations.
+ ///
+ /// The stored procedure to which the annotations are applied.
+ /// Additional parameters used during code generation.
+ public virtual void Generate(IStoredProcedure storedProcedure, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ => GenerateSimpleAnnotations(parameters);
+
///
/// Generates code to create the given annotations.
///
@@ -457,6 +539,11 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
annotations.Remove(RelationalAnnotationNames.ViewColumnMappings);
annotations.Remove(RelationalAnnotationNames.SqlQueryColumnMappings);
annotations.Remove(RelationalAnnotationNames.FunctionColumnMappings);
+ annotations.Remove(RelationalAnnotationNames.InsertStoredProcedureParameterMappings);
+ annotations.Remove(RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings);
+ annotations.Remove(RelationalAnnotationNames.DeleteStoredProcedureParameterMappings);
+ annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureParameterMappings);
+ annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings);
annotations.Remove(RelationalAnnotationNames.DefaultColumnMappings);
}
else
@@ -472,7 +559,7 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
AddNamespace(typeof(StoreObjectDictionary), parameters.Namespaces);
AddNamespace(typeof(StoreObjectIdentifier), parameters.Namespaces);
var overridesVariable = Dependencies.CSharpHelper.Identifier("overrides", parameters.ScopeVariables, capitalize: false);
- parameters.MainBuilder
+ parameters.MainBuilder.AppendLine()
.Append("var ").Append(overridesVariable).AppendLine(" = new StoreObjectDictionary();");
foreach (var overrides in tableOverrides.GetValues())
@@ -481,6 +568,7 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
}
GenerateSimpleAnnotation(RelationalAnnotationNames.RelationalOverrides, overridesVariable, parameters);
+ parameters.MainBuilder.AppendLine();
}
}
@@ -617,6 +705,21 @@ private static void AppendLiteral(StoreObjectIdentifier storeObject, IndentedStr
builder
.Append("DbFunction(").Append(code.Literal(storeObject.Name)).Append(")");
break;
+ case StoreObjectType.InsertStoredProcedure:
+ builder
+ .Append("InsertStoredProcedure(").Append(code.Literal(storeObject.Name))
+ .Append(", ").Append(code.Literal(storeObject.Schema)).Append(")");
+ break;
+ case StoreObjectType.DeleteStoredProcedure:
+ builder
+ .Append("DeleteStoredProcedure(").Append(code.Literal(storeObject.Name))
+ .Append(", ").Append(code.Literal(storeObject.Schema)).Append(")");
+ break;
+ case StoreObjectType.UpdateStoredProcedure:
+ builder
+ .Append("UpdateStoredProcedure(").Append(code.Literal(storeObject.Name))
+ .Append(", ").Append(code.Literal(storeObject.Schema)).Append(")");
+ break;
default:
Check.DebugFail("Unexpected StoreObjectType: " + storeObject.StoreObjectType);
break;
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToTable.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToTable.cs
index 904d53978a2..81d3639ca0a 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToTable.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToTable.cs
@@ -138,16 +138,7 @@ public static EntityTypeBuilder ToTable(
string name,
Action> buildAction)
where TEntity : class
- {
- Check.NotNull(name, nameof(name));
- Check.NotNull(buildAction, nameof(buildAction));
-
- entityTypeBuilder.Metadata.SetTableName(name);
- entityTypeBuilder.Metadata.SetSchema(null);
- buildAction(new (StoreObjectIdentifier.Table(name, null), entityTypeBuilder));
-
- return entityTypeBuilder;
- }
+ => ToTable(entityTypeBuilder, name, null, buildAction);
///
/// Configures the table that the entity type maps to when targeting a relational database.
@@ -195,7 +186,7 @@ public static EntityTypeBuilder ToTable(
entityTypeBuilder.Metadata.SetTableName(name);
entityTypeBuilder.Metadata.SetSchema(schema);
- buildAction(new (StoreObjectIdentifier.Table(name, schema), entityTypeBuilder));
+ buildAction(new(StoreObjectIdentifier.Table(name, entityTypeBuilder.Metadata.GetSchema()), entityTypeBuilder));
return entityTypeBuilder;
}
@@ -243,7 +234,7 @@ public static EntityTypeBuilder ToTable(
entityTypeBuilder.Metadata.SetTableName(name);
entityTypeBuilder.Metadata.SetSchema(schema);
- buildAction(new (StoreObjectIdentifier.Table(name, schema), entityTypeBuilder));
+ buildAction(new(StoreObjectIdentifier.Table(name, entityTypeBuilder.Metadata.GetSchema()), entityTypeBuilder));
return entityTypeBuilder;
}
@@ -374,16 +365,7 @@ public static OwnedNavigationBuilder ToTable> buildAction)
where TOwnerEntity : class
where TDependentEntity : class
- {
- Check.NotNull(name, nameof(name));
- Check.NotNull(buildAction, nameof(buildAction));
-
- ownedNavigationBuilder.OwnedEntityType.SetTableName(name);
- ownedNavigationBuilder.OwnedEntityType.SetSchema(null);
- buildAction(new (StoreObjectIdentifier.Table(name, null), ownedNavigationBuilder));
-
- return ownedNavigationBuilder;
- }
+ => ToTable(ownedNavigationBuilder, name, null, buildAction);
///
/// Configures the table that the entity type maps to when targeting a relational database.
@@ -432,7 +414,7 @@ public static OwnedNavigationBuilder ToTable(
ownedNavigationBuilder.OwnedEntityType.SetTableName(name);
ownedNavigationBuilder.OwnedEntityType.SetSchema(schema);
- buildAction(new (StoreObjectIdentifier.Table(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.Table(name, ownedNavigationBuilder.OwnedEntityType.GetSchema()), ownedNavigationBuilder));
return ownedNavigationBuilder;
}
@@ -485,7 +467,7 @@ public static OwnedNavigationBuilder ToTable SplitToTable(
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new(StoreObjectIdentifier.Table(name, schema), entityTypeBuilder));
+ buildAction(new(StoreObjectIdentifier.Table(name, schema ?? entityTypeBuilder.Metadata.GetDefaultSchema()), entityTypeBuilder));
return entityTypeBuilder;
}
@@ -642,7 +624,7 @@ public static OwnedNavigationBuilder SplitToTable(
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new (StoreObjectIdentifier.Table(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.Table(name, schema ?? ownedNavigationBuilder.OwnedEntityType.GetDefaultSchema()), ownedNavigationBuilder));
return ownedNavigationBuilder;
}
@@ -673,7 +655,7 @@ public static OwnedNavigationBuilder SplitToTabl
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new (StoreObjectIdentifier.Table(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.Table(name, schema ?? ownedNavigationBuilder.OwnedEntityType.GetDefaultSchema()), ownedNavigationBuilder));
return ownedNavigationBuilder;
}
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToView.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToView.cs
index 0f1981767f2..ca5a2f51261 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToView.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.ToView.cs
@@ -145,7 +145,7 @@ public static EntityTypeBuilder ToView(
entityTypeBuilder.Metadata.SetViewName(name);
entityTypeBuilder.Metadata.SetViewSchema(schema);
entityTypeBuilder.Metadata.SetAnnotation(RelationalAnnotationNames.ViewDefinitionSql, null);
- buildAction(new(StoreObjectIdentifier.View(name, schema), entityTypeBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, entityTypeBuilder.Metadata.GetViewSchema()), entityTypeBuilder));
return entityTypeBuilder;
}
@@ -175,7 +175,7 @@ public static EntityTypeBuilder ToView(
entityTypeBuilder.Metadata.SetViewName(name);
entityTypeBuilder.Metadata.SetViewSchema(schema);
entityTypeBuilder.Metadata.SetAnnotation(RelationalAnnotationNames.ViewDefinitionSql, null);
- buildAction(new(StoreObjectIdentifier.View(name, schema), entityTypeBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, entityTypeBuilder.Metadata.GetViewSchema()), entityTypeBuilder));
return entityTypeBuilder;
}
@@ -317,7 +317,7 @@ public static OwnedNavigationBuilder ToView(
ownedNavigationBuilder.OwnedEntityType.SetViewName(name);
ownedNavigationBuilder.OwnedEntityType.SetViewSchema(schema);
ownedNavigationBuilder.OwnedEntityType.SetAnnotation(RelationalAnnotationNames.ViewDefinitionSql, null);
- buildAction(new(StoreObjectIdentifier.View(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, ownedNavigationBuilder.OwnedEntityType.GetViewSchema()), ownedNavigationBuilder));
return ownedNavigationBuilder;
}
@@ -349,7 +349,7 @@ public static OwnedNavigationBuilder ToView SplitToView(
///
/// See Modeling entity types and relationships for more information and examples.
///
- /// The builder for the entity type being configured.
+ /// The builder for the entity type being configured.
/// The name of the view.
/// The schema of the view.
/// An action that performs configuration of the view.
/// The same builder instance so that multiple calls can be chained.
public static EntityTypeBuilder SplitToView(
- this EntityTypeBuilder ownedNavigationBuilder,
+ this EntityTypeBuilder entityTypeBuilder,
string name,
string? schema,
Action buildAction)
@@ -412,9 +412,9 @@ public static EntityTypeBuilder SplitToView(
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new(StoreObjectIdentifier.View(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, schema ?? entityTypeBuilder.Metadata.GetDefaultViewSchema()), entityTypeBuilder));
- return ownedNavigationBuilder;
+ return entityTypeBuilder;
}
///
@@ -441,7 +441,7 @@ public static EntityTypeBuilder SplitToView(
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new(StoreObjectIdentifier.View(name, schema), entityTypeBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, schema ?? entityTypeBuilder.Metadata.GetDefaultViewSchema()), entityTypeBuilder));
return entityTypeBuilder;
}
@@ -506,7 +506,7 @@ public static OwnedNavigationBuilder SplitToView(
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new (StoreObjectIdentifier.View(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, schema ?? ownedNavigationBuilder.OwnedEntityType.GetDefaultViewSchema()), ownedNavigationBuilder));
return ownedNavigationBuilder;
}
@@ -537,7 +537,7 @@ public static OwnedNavigationBuilder SplitToView
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(buildAction, nameof(buildAction));
- buildAction(new (StoreObjectIdentifier.View(name, schema), ownedNavigationBuilder));
+ buildAction(new(StoreObjectIdentifier.View(name, schema ?? ownedNavigationBuilder.OwnedEntityType.GetDefaultViewSchema()), ownedNavigationBuilder));
return ownedNavigationBuilder;
}
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.UseSproc.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.UseSproc.cs
index b07449295a9..d90fa8d75a8 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.UseSproc.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.UseSproc.cs
@@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore;
public static partial class RelationalEntityTypeBuilderExtensions
{
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -34,7 +34,7 @@ public static EntityTypeBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -56,7 +56,7 @@ public static EntityTypeBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -80,7 +80,7 @@ public static EntityTypeBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -102,7 +102,7 @@ public static EntityTypeBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -126,7 +126,7 @@ public static EntityTypeBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -152,7 +152,7 @@ public static EntityTypeBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -172,7 +172,7 @@ public static OwnedNavigationBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -194,7 +194,7 @@ public static OwnedNavigationBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -218,7 +218,7 @@ public static OwnedNavigationBuilder UpdateUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -242,7 +242,7 @@ public static OwnedNavigationBuilder UpdateUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -268,7 +268,7 @@ public static OwnedNavigationBuilder UpdateUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -296,7 +296,7 @@ public static OwnedNavigationBuilder UpdateUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for updates when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for updates when targeting a relational database.
///
/// The builder for the entity type being configured.
/// Indicates whether the configuration was specified using a data annotation.
@@ -310,7 +310,7 @@ public static OwnedNavigationBuilder UpdateUsing
entityTypeBuilder.Metadata, StoreObjectType.UpdateStoredProcedure, fromDataAnnotation);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -330,7 +330,7 @@ public static EntityTypeBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -352,7 +352,7 @@ public static EntityTypeBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -376,7 +376,7 @@ public static EntityTypeBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -398,7 +398,7 @@ public static EntityTypeBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -422,7 +422,7 @@ public static EntityTypeBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -448,7 +448,7 @@ public static EntityTypeBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -468,7 +468,7 @@ public static OwnedNavigationBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -490,7 +490,7 @@ public static OwnedNavigationBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -514,7 +514,7 @@ public static OwnedNavigationBuilder DeleteUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -538,7 +538,7 @@ public static OwnedNavigationBuilder DeleteUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -564,7 +564,7 @@ public static OwnedNavigationBuilder DeleteUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -592,7 +592,7 @@ public static OwnedNavigationBuilder DeleteUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for deletes when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for deletes when targeting a relational database.
///
/// The builder for the entity type being configured.
/// Indicates whether the configuration was specified using a data annotation.
@@ -606,7 +606,7 @@ public static OwnedNavigationBuilder DeleteUsing
entityTypeBuilder.Metadata, StoreObjectType.DeleteStoredProcedure, fromDataAnnotation);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -626,7 +626,7 @@ public static EntityTypeBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -648,7 +648,7 @@ public static EntityTypeBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -672,7 +672,7 @@ public static EntityTypeBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -694,7 +694,7 @@ public static EntityTypeBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -718,7 +718,7 @@ public static EntityTypeBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -744,7 +744,7 @@ public static EntityTypeBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -764,7 +764,7 @@ public static OwnedNavigationBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -786,7 +786,7 @@ public static OwnedNavigationBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -810,7 +810,7 @@ public static OwnedNavigationBuilder InsertUsingStoredProcedure(
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -834,7 +834,7 @@ public static OwnedNavigationBuilder InsertUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -860,7 +860,7 @@ public static OwnedNavigationBuilder InsertUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
///
/// See Modeling entity types and relationships and
@@ -888,7 +888,7 @@ public static OwnedNavigationBuilder InsertUsing
buildAction);
///
- /// Configures the stored procedure that the entity type would use for inserts when targeting a relational database.
+ /// Configures the stored procedure that the entity type uses for inserts when targeting a relational database.
///
/// The builder for the entity type being configured.
/// Indicates whether the configuration was specified using a data annotation.
@@ -927,8 +927,16 @@ private static EntityTypeBuilder UseStoredProcedure(
{
Check.NotNull(buildAction, nameof(buildAction));
+ var entityType = entityTypeBuilder.Metadata;
+ if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy
+ && !entityType.ClrType.IsInstantiable())
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.AbstractTpc(entityType.DisplayName(), name ?? sprocType.ToString()));
+ }
+
var sprocBuilder = InternalStoredProcedureBuilder.HasStoredProcedure(
- entityTypeBuilder.Metadata, sprocType, name, schema);
+ entityType, sprocType, name, schema);
buildAction(new(sprocBuilder.Metadata, entityTypeBuilder));
return entityTypeBuilder;
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
index c68b51e8a07..68ec8c0e4db 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
@@ -54,7 +54,7 @@ public static class RelationalEntityTypeExtensions
/// The default name of the table to which the entity type would be mapped.
public static string? GetDefaultTableName(this IReadOnlyEntityType entityType, bool truncate = true)
{
- if (entityType.GetDiscriminatorPropertyName() != null
+ if ((entityType.GetMappingStrategy() ?? RelationalAnnotationNames.TphMappingStrategy) == RelationalAnnotationNames.TphMappingStrategy
&& entityType.BaseType != null)
{
return entityType.GetRootType().GetTableName();
@@ -67,7 +67,7 @@ public static class RelationalEntityTypeExtensions
return ownership.PrincipalEntityType.GetTableName();
}
- var name = entityType.ShortName();
+ var name = entityType.HasSharedClrType ? entityType.ShortName() : entityType.ClrType.ShortDisplayName();
if (entityType.HasSharedClrType
&& ownership != null
#pragma warning disable EF1001 // Internal EF Core API usage.
@@ -593,7 +593,7 @@ public static IEnumerable GetFunctionMappings(this IEntityType
/// The entity type.
/// The stored procedure to which the entity type is mapped.
public static IMutableStoredProcedure? GetDeleteStoredProcedure(this IMutableEntityType entityType)
- => StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.DeleteStoredProcedure);
+ => (IMutableStoredProcedure?)StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.DeleteStoredProcedure);
///
/// Returns the stored procedure to which the entity type is mapped for deletes
@@ -602,7 +602,7 @@ public static IEnumerable GetFunctionMappings(this IEntityType
/// The entity type.
/// The stored procedure to which the entity type is mapped.
public static IConventionStoredProcedure? GetDeleteStoredProcedure(this IConventionEntityType entityType)
- => StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.DeleteStoredProcedure);
+ => (IConventionStoredProcedure?)StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.DeleteStoredProcedure);
///
/// Returns the stored procedure to which the entity type is mapped for deletes
@@ -672,7 +672,7 @@ public static IMutableStoredProcedure SetDeleteStoredProcedure(this IMutableEnti
/// The entity type.
/// The stored procedure to which the entity type is mapped.
public static IMutableStoredProcedure? GetInsertStoredProcedure(this IMutableEntityType entityType)
- => StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.InsertStoredProcedure);
+ => (IMutableStoredProcedure?)StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.InsertStoredProcedure);
///
/// Returns the stored procedure to which the entity type is mapped for inserts
@@ -681,7 +681,7 @@ public static IMutableStoredProcedure SetDeleteStoredProcedure(this IMutableEnti
/// The entity type.
/// The stored procedure to which the entity type is mapped.
public static IConventionStoredProcedure? GetInsertStoredProcedure(this IConventionEntityType entityType)
- => StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.InsertStoredProcedure);
+ => (IConventionStoredProcedure?)StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.InsertStoredProcedure);
///
/// Returns the stored procedure to which the entity type is mapped for inserts
@@ -751,7 +751,7 @@ public static IMutableStoredProcedure SetInsertStoredProcedure(this IMutableEnti
/// The entity type.
/// The stored procedure to which the entity type is mapped.
public static IMutableStoredProcedure? GetUpdateStoredProcedure(this IMutableEntityType entityType)
- => StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.UpdateStoredProcedure);
+ => (IMutableStoredProcedure?)StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.UpdateStoredProcedure);
///
/// Returns the stored procedure to which the entity type is mapped for updates
@@ -760,7 +760,7 @@ public static IMutableStoredProcedure SetInsertStoredProcedure(this IMutableEnti
/// The entity type.
/// The stored procedure to which the entity type is mapped.
public static IConventionStoredProcedure? GetUpdateStoredProcedure(this IConventionEntityType entityType)
- => StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.UpdateStoredProcedure);
+ => (IConventionStoredProcedure?)StoredProcedure.FindStoredProcedure(entityType, StoreObjectType.UpdateStoredProcedure);
///
/// Returns the stored procedure to which the entity type is mapped for updates
@@ -814,6 +814,36 @@ public static IMutableStoredProcedure SetUpdateStoredProcedure(this IMutableEnti
public static ConfigurationSource? GetUpdateStoredProcedureConfigurationSource(this IConventionEntityType entityType)
=> StoredProcedure.GetStoredProcedureConfigurationSource(entityType, StoreObjectType.UpdateStoredProcedure);
+ ///
+ /// Returns the insert stored procedures to which the entity type is mapped.
+ ///
+ /// The entity type.
+ /// The insert stored procedures to which the entity type is mapped.
+ public static IEnumerable GetInsertStoredProcedureMappings(this IEntityType entityType)
+ => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.InsertStoredProcedureMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the delete stored procedures to which the entity type is mapped.
+ ///
+ /// The entity type.
+ /// The delete stored procedures to which the entity type is mapped.
+ public static IEnumerable GetDeleteStoredProcedureMappings(this IEntityType entityType)
+ => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.DeleteStoredProcedureMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the update stored procedures to which the entity type is mapped.
+ ///
+ /// The entity type.
+ /// The update stored procedures to which the entity type is mapped.
+ public static IEnumerable GetUpdateStoredProcedureMappings(this IEntityType entityType)
+ => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.UpdateStoredProcedureMappings)
+ ?? Enumerable.Empty();
+
#endregion
#region Check constraint
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs
index 50a4276b20b..0aa7b023fe9 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
@@ -357,6 +358,54 @@ public static bool CanSetIsFixedLength(
bool fromDataAnnotation = false)
=> propertyBuilder.CanSetAnnotation(RelationalAnnotationNames.IsFixedLength, fixedLength, fromDataAnnotation);
+ ///
+ /// Sets the direction of the stored procedure parameter.
+ ///
+ /// The builder for the property being configured.
+ /// The direction.
+ /// The identifier of the stored procedure.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the configuration was applied,
+ /// otherwise.
+ ///
+ public static IConventionPropertyBuilder? HasDirection(
+ this IConventionPropertyBuilder propertyBuilder,
+ ParameterDirection direction,
+ in StoreObjectIdentifier storeObject,
+ bool fromDataAnnotation = false)
+ {
+ if (!propertyBuilder.CanSetDirection(direction, storeObject, fromDataAnnotation))
+ {
+ return null;
+ }
+
+ propertyBuilder.Metadata.SetDirection(direction, storeObject, fromDataAnnotation);
+ return propertyBuilder;
+ }
+
+ ///
+ /// Returns a value indicating whether the given direction can be configured on the corresponding stored procedure parameter.
+ ///
+ /// The builder for the property being configured.
+ /// The direction.
+ /// The identifier of the stored procedure.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be mapped to the given column.
+ public static bool CanSetDirection(
+ this IConventionPropertyBuilder propertyBuilder,
+ ParameterDirection direction,
+ in StoreObjectIdentifier storeObject,
+ bool fromDataAnnotation = false)
+ {
+ var overrides = (IConventionRelationalPropertyOverrides?)RelationalPropertyOverrides.Find(
+ propertyBuilder.Metadata, storeObject);
+ return overrides == null
+ || (fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention)
+ .Overrides(overrides.GetDirectionConfigurationSource())
+ || overrides.Direction == direction;
+ }
+
///
/// Configures the default value expression for the column that the property maps to when targeting a
/// relational database.
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
index 921b60e5cc7..1467e0e1479 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
@@ -4,6 +4,7 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
@@ -508,6 +509,56 @@ public static IEnumerable GetFunctionColumnMappings(this
RelationalAnnotationNames.FunctionColumnMappings)
?? Enumerable.Empty();
+ ///
+ /// Returns the insert stored procedure result columns to which the property is mapped.
+ ///
+ /// The property.
+ /// The insert stored procedure result columns to which the property is mapped.
+ public static IEnumerable GetInsertStoredProcedureResultColumnMappings(this IProperty property)
+ => (IEnumerable?)property.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the insert stored procedure parameters to which the property is mapped.
+ ///
+ /// The property.
+ /// The insert stored procedure parameters to which the property is mapped.
+ public static IEnumerable GetInsertStoredProcedureParameterMappings(this IProperty property)
+ => (IEnumerable?)property.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.InsertStoredProcedureParameterMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the delete stored procedure parameters to which the property is mapped.
+ ///
+ /// The property.
+ /// The delete stored procedure parameters to which the property is mapped.
+ public static IEnumerable GetDeleteStoredProcedureParameterMappings(this IProperty property)
+ => (IEnumerable?)property.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.DeleteStoredProcedureParameterMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the update stored procedure result columns to which the property is mapped.
+ ///
+ /// The property.
+ /// The update stored procedure result columns to which the property is mapped.
+ public static IEnumerable GetUpdateStoredProcedureResultColumnMappings(this IProperty property)
+ => (IEnumerable?)property.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the update stored procedure parameters to which the property is mapped.
+ ///
+ /// The property.
+ /// The update stored procedure parameters to which the property is mapped.
+ public static IEnumerable GetUpdateStoredProcedureParameterMappings(this IProperty property)
+ => (IEnumerable?)property.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.UpdateStoredProcedureParameterMappings)
+ ?? Enumerable.Empty();
+
///
/// Returns the column corresponding to this property if it's mapped to the given table-like store object.
///
@@ -557,6 +608,26 @@ public static IEnumerable GetFunctionColumnMappings(this
}
}
+ return null;
+ case StoreObjectType.InsertStoredProcedure:
+ foreach (var mapping in property.GetInsertStoredProcedureResultColumnMappings())
+ {
+ if (mapping.TableMapping.Table.Name == storeObject.Name && mapping.TableMapping.Table.Schema == storeObject.Schema)
+ {
+ return mapping.Column;
+ }
+ }
+
+ return null;
+ case StoreObjectType.UpdateStoredProcedure:
+ foreach (var mapping in property.GetUpdateStoredProcedureResultColumnMappings())
+ {
+ if (mapping.TableMapping.Table.Name == storeObject.Name && mapping.TableMapping.Table.Schema == storeObject.Schema)
+ {
+ return mapping.Column;
+ }
+ }
+
return null;
default:
throw new NotSupportedException(storeObject.StoreObjectType.ToString());
@@ -1100,6 +1171,53 @@ private static bool IsOptionalSharingDependent(
return optional ?? (entityType.BaseType != null && entityType.FindDiscriminatorProperty() != null);
}
+ ///
+ /// Gets the direction of the corresponding stored procedure parameter.
+ ///
+ /// The property.
+ /// The identifier of the stored procedure containing the parameter.
+ ///
+ /// The direction of the corresponding stored procedure parameter.
+ ///
+ public static System.Data.ParameterDirection GetDirection(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
+ => property.FindOverrides(storeObject)?.Direction ?? System.Data.ParameterDirection.Input;
+
+ ///
+ /// Sets the direction of the corresponding stored procedure parameter.
+ ///
+ /// The property.
+ /// The direction to set.
+ /// The identifier of the stored procedure containing the parameter.
+ public static void SetDirection(
+ this IMutableProperty property,
+ System.Data.ParameterDirection? direction,
+ in StoreObjectIdentifier storeObject)
+ => property.GetOrCreateOverrides(storeObject).Direction = direction;
+
+ ///
+ /// Sets the direction of the corresponding stored procedure parameter.
+ ///
+ /// The property.
+ /// The direction to set.
+ /// The identifier of the stored procedure containing the parameter.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The configured value.
+ public static System.Data.ParameterDirection? SetDirection(
+ this IConventionProperty property,
+ System.Data.ParameterDirection? direction,
+ in StoreObjectIdentifier storeObject,
+ bool fromDataAnnotation = false)
+ => property.GetOrCreateOverrides(storeObject, fromDataAnnotation).SetDirection(direction, fromDataAnnotation);
+
+ ///
+ /// Gets the for the stored procedure parameter direction.
+ ///
+ /// The property.
+ /// The identifier of the stored procedure containing the parameter.
+ /// The for the stored procedure parameter direction.
+ public static ConfigurationSource? GetDirectionConfigurationSource(this IConventionProperty property, in StoreObjectIdentifier storeObject)
+ => property.FindOverrides(storeObject)?.GetDirectionConfigurationSource();
+
///
/// Returns the comment for the column this property is mapped to.
///
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index 53b5bd3d93a..0b8cb90315f 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -276,7 +277,7 @@ static void AddSproc(
private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy)
{
var entityType = sproc.EntityType;
- var storeObjectIdentifier = ((StoredProcedure)sproc).CreateIdentifier()!.Value;
+ var storeObjectIdentifier = sproc.GetStoreIdentifier();
var primaryKey = entityType.FindPrimaryKey();
if (primaryKey == null)
@@ -311,18 +312,43 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy
}
else if (mappingStrategy == RelationalAnnotationNames.TptMappingStrategy)
{
- if (entityType.BaseType != null)
+ var baseType = entityType.BaseType;
+ if (baseType != null)
{
foreach (var property in primaryKey.Properties)
{
properties.Add(property.Name, property);
}
+
+ while (baseType != null && baseType.IsAbstract())
+ {
+ if (StoredProcedure.FindDeclaredStoredProcedure(baseType, storeObjectIdentifier.StoreObjectType) != null)
+ {
+ break;
+ }
+
+ foreach (var property in baseType.GetDeclaredProperties())
+ {
+ properties.Add(property.Name, property);
+ }
+
+ baseType = baseType.BaseType;
+ }
}
}
+ var storeGeneratedProperties = storeObjectIdentifier.StoreObjectType switch
+ {
+ StoreObjectType.InsertStoredProcedure
+ => properties.Where(p => (p.Value.ValueGenerated & ValueGenerated.OnAdd) != 0).ToDictionary(p => p.Key, p => p.Value),
+ StoreObjectType.UpdateStoredProcedure
+ => properties.Where(p => (p.Value.ValueGenerated & ValueGenerated.OnUpdate) != 0).ToDictionary(p => p.Key, p => p.Value),
+ _ => new Dictionary()
+ };
+
foreach (var resultColumn in sproc.ResultColumns)
{
- if (!properties!.TryGetValue(resultColumn, out var property))
+ if (!properties.TryGetValue(resultColumn, out var property))
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureResultColumnNotFound(
@@ -332,7 +358,8 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy
switch (storeObjectIdentifier.StoreObjectType)
{
case StoreObjectType.InsertStoredProcedure:
- if ((property.ValueGenerated & ValueGenerated.OnAdd) == 0)
+ case StoreObjectType.UpdateStoredProcedure:
+ if (!storeGeneratedProperties.Remove(property.Name))
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureResultColumnNotGenerated(
@@ -344,44 +371,65 @@ private static void ValidateSproc(IStoredProcedure sproc, string mappingStrategy
throw new InvalidOperationException(
RelationalStrings.StoredProcedureResultColumnDelete(
entityType.DisplayName(), resultColumn, storeObjectIdentifier.DisplayName()));
- case StoreObjectType.UpdateStoredProcedure:
- if ((property.ValueGenerated & ValueGenerated.OnUpdate) == 0)
- {
- throw new InvalidOperationException(
- RelationalStrings.StoredProcedureResultColumnNotGenerated(
- entityType.DisplayName(), resultColumn, storeObjectIdentifier.DisplayName()));
- }
-
+ default:
+ Check.DebugFail("Unexpected stored procedure type: " + storeObjectIdentifier.StoreObjectType);
break;
}
}
foreach (var parameter in sproc.Parameters)
{
- if (!properties!.TryGetAndRemove(parameter, out IProperty property))
+ if (!properties.TryGetAndRemove(parameter, out IProperty property))
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureParameterNotFound(
parameter, entityType.DisplayName(), storeObjectIdentifier.DisplayName()));
}
- if (storeObjectIdentifier.StoreObjectType == StoreObjectType.DeleteStoredProcedure
- && !property.IsPrimaryKey()
- && !property.IsConcurrencyToken)
+ switch (storeObjectIdentifier.StoreObjectType)
{
- throw new InvalidOperationException(
- RelationalStrings.StoredProcedureDeleteNonKeyProperty(
- entityType.DisplayName(), parameter, storeObjectIdentifier.DisplayName()));
+ case StoreObjectType.InsertStoredProcedure:
+ case StoreObjectType.UpdateStoredProcedure:
+ if (property.GetDirection(storeObjectIdentifier) != ParameterDirection.Input
+ && !storeGeneratedProperties.Remove(property.Name))
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.StoredProcedureOutputParameterNotGenerated(
+ entityType.DisplayName(), parameter, storeObjectIdentifier.DisplayName()));
+ }
+
+ break;
+ case StoreObjectType.DeleteStoredProcedure:
+ if (!property.IsPrimaryKey()
+ && !property.IsConcurrencyToken)
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.StoredProcedureDeleteNonKeyProperty(
+ entityType.DisplayName(), parameter, storeObjectIdentifier.DisplayName()));
+ }
+
+ break;
+ default:
+ Check.DebugFail("Unexpected stored procedure type: " + storeObjectIdentifier.StoreObjectType);
+ break;
}
}
+
+ if (storeGeneratedProperties.Count > 0)
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.StoredProcedureGeneratedPropertiesNotMapped(
+ entityType.DisplayName(),
+ storeObjectIdentifier.DisplayName(),
+ storeGeneratedProperties.Values.Format()));
+ }
foreach (var resultColumn in sproc.ResultColumns)
{
- properties!.Remove(resultColumn);
+ properties.Remove(resultColumn);
}
- if (properties != null
- && properties.Count > 0)
+ if (properties.Count > 0)
{
foreach (var property in properties.Values.ToList())
{
@@ -1729,7 +1777,7 @@ private static void ValidateTphMapping(IEntityType rootEntityType, StoreObjectTy
var isSproc = storeObjectType == StoreObjectType.DeleteStoredProcedure
|| storeObjectType == StoreObjectType.InsertStoredProcedure
|| storeObjectType == StoreObjectType.UpdateStoredProcedure;
- var rootSproc = isSproc ? StoredProcedure.GetDeclaredStoredProcedure(rootEntityType, storeObjectType) : null;
+ var rootSproc = isSproc ? StoredProcedure.FindDeclaredStoredProcedure(rootEntityType, storeObjectType) : null;
var rootId = StoreObjectIdentifier.Create(rootEntityType, storeObjectType);
foreach (var entityType in rootEntityType.GetDerivedTypes())
{
@@ -1743,7 +1791,7 @@ private static void ValidateTphMapping(IEntityType rootEntityType, StoreObjectTy
{
if (rootSproc != null)
{
- var sproc = StoredProcedure.GetDeclaredStoredProcedure(entityType, storeObjectType);
+ var sproc = StoredProcedure.FindDeclaredStoredProcedure(entityType, storeObjectType);
if (sproc != null
&& sproc != rootSproc)
{
diff --git a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder.cs b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder.cs
index 4c02be8b994..c817902a03c 100644
--- a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder.cs
@@ -65,6 +65,22 @@ public virtual ViewColumnBuilder Property(string propertyName)
public virtual ViewColumnBuilder Property(string propertyName)
=> new(MappingFragment.StoreObject, OwnedNavigationBuilder.Property(propertyName));
+ ///
+ /// Adds or updates an annotation on the view. If an annotation with the key specified in
+ /// already exists, its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual OwnedNavigationSplitViewBuilder HasAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation, nameof(annotation));
+
+ ((EntityTypeMappingFragment)MappingFragment).Builder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
OwnedNavigationBuilder IInfrastructure.Instance => OwnedNavigationBuilder;
#region Hidden System.Object members
diff --git a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder``.cs b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder``.cs
index bb27021503c..bd186465668 100644
--- a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder``.cs
+++ b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationSplitViewBuilder``.cs
@@ -44,6 +44,17 @@ private OwnedNavigationBuilder OwnedNavigationBu
public virtual ViewColumnBuilder Property(Expression> propertyExpression)
=> new(MappingFragment.StoreObject, OwnedNavigationBuilder.Property(propertyExpression));
+ ///
+ /// Adds or updates an annotation on the view. If an annotation with the key specified in
+ /// already exists, its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual OwnedNavigationSplitViewBuilder HasAnnotation(
+ string annotation, object? value)
+ => (OwnedNavigationSplitViewBuilder)base.HasAnnotation(annotation, value);
+
OwnedNavigationBuilder IInfrastructure>.Instance
=> OwnedNavigationBuilder;
}
diff --git a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder.cs b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder.cs
index 9de2c4a730d..52f8d56ef16 100644
--- a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder.cs
@@ -70,7 +70,7 @@ public virtual OwnedNavigationStoredProcedureBuilder HasParameter(string propert
public virtual OwnedNavigationStoredProcedureBuilder HasParameter(string propertyName, Action buildAction)
{
Builder.HasParameter(propertyName, ConfigurationSource.Explicit);
- buildAction(new(((StoredProcedure)Metadata).CreateIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
+ buildAction(new(Metadata.GetStoreIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
return this;
}
@@ -128,7 +128,7 @@ public virtual OwnedNavigationStoredProcedureBuilder HasResultColumn(
string propertyName, Action buildAction)
{
Builder.HasResultColumn(propertyName, ConfigurationSource.Explicit);
- buildAction(new(((StoredProcedure)Metadata).CreateIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
+ buildAction(new(Metadata.GetStoreIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
return this;
}
diff --git a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder``.cs b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder``.cs
index 1db167f39b9..9ef38f2d803 100644
--- a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder``.cs
+++ b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationStoredProcedureBuilder``.cs
@@ -80,7 +80,7 @@ public virtual OwnedNavigationStoredProcedureBuilder buildAction)
{
Builder.HasParameter(propertyExpression, ConfigurationSource.Explicit);
- buildAction(new(((StoredProcedure)Metadata).CreateIdentifier()!.Value, CreatePropertyBuilder(propertyExpression)));
+ buildAction(new(Metadata.GetStoreIdentifier()!.Value, CreatePropertyBuilder(propertyExpression)));
return this;
}
@@ -131,7 +131,7 @@ public virtual OwnedNavigationStoredProcedureBuilder buildAction)
{
Builder.HasResultColumn(propertyExpression, ConfigurationSource.Explicit);
- buildAction(new(((StoredProcedure)Metadata).CreateIdentifier()!.Value, CreatePropertyBuilder(propertyExpression)));
+ buildAction(new(Metadata.GetStoreIdentifier()!.Value, CreatePropertyBuilder(propertyExpression)));
return this;
}
diff --git a/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder.cs b/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder.cs
index 00e9f14a992..7b7d950a196 100644
--- a/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder.cs
@@ -65,6 +65,22 @@ public virtual ViewColumnBuilder Property(string propertyName)
public virtual ViewColumnBuilder Property(string propertyName)
=> new(MappingFragment.StoreObject, EntityTypeBuilder.Property(propertyName));
+ ///
+ /// Adds or updates an annotation on the view. If an annotation with the key specified in
+ /// already exists, its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual SplitViewBuilder HasAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation, nameof(annotation));
+
+ ((EntityTypeMappingFragment)MappingFragment).Builder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
EntityTypeBuilder IInfrastructure.Instance => EntityTypeBuilder;
#region Hidden System.Object members
diff --git a/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder`.cs b/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder`.cs
index d260bd10490..6e25474ce5f 100644
--- a/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder`.cs
+++ b/src/EFCore.Relational/Metadata/Builders/SplitViewBuilder`.cs
@@ -37,5 +37,15 @@ private EntityTypeBuilder EntityTypeBuilder
public virtual ViewColumnBuilder Property(Expression> propertyExpression)
=> new(MappingFragment.StoreObject, EntityTypeBuilder.Property(propertyExpression));
+ ///
+ /// Adds or updates an annotation on the view. If an annotation with the key specified in
+ /// already exists, its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual SplitViewBuilder HasAnnotation(string annotation, object? value)
+ => (SplitViewBuilder)base.HasAnnotation(annotation, value);
+
EntityTypeBuilder IInfrastructure>.Instance => EntityTypeBuilder;
}
diff --git a/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs b/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs
index 880ad9aef4c..2862c434d98 100644
--- a/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs
@@ -68,7 +68,7 @@ public virtual StoredProcedureBuilder HasParameter(
string propertyName, Action buildAction)
{
Builder.HasParameter(propertyName, ConfigurationSource.Explicit);
- buildAction(new(((StoredProcedure)Metadata).CreateIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
+ buildAction(new(Metadata.GetStoreIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
return this;
}
@@ -144,7 +144,7 @@ public virtual StoredProcedureBuilder HasResultColumn(
string propertyName, Action buildAction)
{
Builder.HasResultColumn(propertyName, ConfigurationSource.Explicit);
- buildAction(new(((StoredProcedure)Metadata).CreateIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
+ buildAction(new(Metadata.GetStoreIdentifier()!.Value, CreatePropertyBuilder(propertyName)));
return this;
}
diff --git a/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder`.cs b/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder`.cs
index c918f6ef75d..f5a22972b9f 100644
--- a/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder`.cs
+++ b/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder`.cs
@@ -104,7 +104,7 @@ public virtual StoredProcedureBuilder HasParameter HasResultColumn
+ /// Configures the stored procedure parameter as both an input and output parameter.
+ ///
+ ///
+ /// See Modeling entity types and relationships and
+ /// Saving data with EF Core for more information and examples.
+ ///
+ /// The same builder instance so that further configuration calls can be chained.
+ public virtual StoredProcedureParameterBuilder IsInputOutput()
+ {
+ ((IMutableRelationalPropertyOverrides)InternalOverrides).Direction = ParameterDirection.InputOutput;
+
+ return this;
+ }
+
+ ///
+ /// Configures the stored procedure parameter as an output parameter.
+ ///
+ ///
+ /// See Modeling entity types and relationships and
+ /// Saving data with EF Core for more information and examples.
+ ///
+ /// The same builder instance so that further configuration calls can be chained.
+ public virtual StoredProcedureParameterBuilder IsOutput()
+ {
+ ((IMutableRelationalPropertyOverrides)InternalOverrides).Direction = ParameterDirection.Output;
+
+ return this;
+ }
+
///
/// Adds or updates an annotation on the property for a specific stored procedure.
/// If an annotation with the key specified in
diff --git a/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder.cs b/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder.cs
index dcf250de03e..78dc79f57da 100644
--- a/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder.cs
@@ -62,6 +62,23 @@ public virtual ViewColumnBuilder HasColumnName(string? name)
return this;
}
+ ///
+ /// Adds or updates an annotation on the property for a specific view.
+ /// If an annotation with the key specified in
+ /// already exists, its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ViewColumnBuilder HasAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation, nameof(annotation));
+
+ InternalOverrides.Builder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
PropertyBuilder IInfrastructure.Instance => PropertyBuilder;
#region Hidden System.Object members
diff --git a/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder`.cs b/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder`.cs
index 7617aaca701..535e59b3b2d 100644
--- a/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder`.cs
+++ b/src/EFCore.Relational/Metadata/Builders/ViewColumnBuilder`.cs
@@ -35,5 +35,16 @@ private PropertyBuilder PropertyBuilder
public new virtual ViewColumnBuilder HasColumnName(string? name)
=> (ViewColumnBuilder)base.HasColumnName(name);
+ ///
+ /// Adds or updates an annotation on the property for a specific view.
+ /// If an annotation with the key specified in
+ /// already exists, its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ViewColumnBuilder HasAnnotation(string annotation, object? value)
+ => (ViewColumnBuilder)base.HasAnnotation(annotation, value);
+
PropertyBuilder IInfrastructure>.Instance => PropertyBuilder;
}
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
index 2f145d18fad..3e14e8158f8 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
@@ -122,6 +122,9 @@ protected override void ProcessEntityTypeAnnotations(
annotations.Remove(RelationalAnnotationNames.ViewMappings);
annotations.Remove(RelationalAnnotationNames.SqlQueryMappings);
annotations.Remove(RelationalAnnotationNames.FunctionMappings);
+ annotations.Remove(RelationalAnnotationNames.InsertStoredProcedureMappings);
+ annotations.Remove(RelationalAnnotationNames.DeleteStoredProcedureMappings);
+ annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureMappings);
annotations.Remove(RelationalAnnotationNames.DefaultMappings);
}
else
@@ -171,6 +174,42 @@ protected override void ProcessEntityTypeAnnotations(
annotations[RelationalAnnotationNames.Triggers] = runtimeTriggers;
}
+
+ if (annotations.TryGetValue(RelationalAnnotationNames.InsertStoredProcedure, out var insertStoredProcedure))
+ {
+ var runtimeSproc = Create((IStoredProcedure)insertStoredProcedure!, runtimeEntityType);
+
+ CreateAnnotations(
+ (IStoredProcedure)insertStoredProcedure!, runtimeSproc,
+ static (convention, annotations, source, target, runtime)
+ => convention.ProcessStoredProcedureAnnotations(annotations, source, target, runtime));
+
+ annotations[RelationalAnnotationNames.InsertStoredProcedure] = runtimeSproc;
+ }
+
+ if (annotations.TryGetValue(RelationalAnnotationNames.DeleteStoredProcedure, out var deleteStoredProcedure))
+ {
+ var runtimeSproc = Create((IStoredProcedure)deleteStoredProcedure!, runtimeEntityType);
+
+ CreateAnnotations(
+ (IStoredProcedure)deleteStoredProcedure!, runtimeSproc,
+ static (convention, annotations, source, target, runtime)
+ => convention.ProcessStoredProcedureAnnotations(annotations, source, target, runtime));
+
+ annotations[RelationalAnnotationNames.DeleteStoredProcedure] = runtimeSproc;
+ }
+
+ if (annotations.TryGetValue(RelationalAnnotationNames.UpdateStoredProcedure, out var updateStoredProcedure))
+ {
+ var runtimeSproc = Create((IStoredProcedure)updateStoredProcedure!, runtimeEntityType);
+
+ CreateAnnotations(
+ (IStoredProcedure)updateStoredProcedure!, runtimeSproc,
+ static (convention, annotations, source, target, runtime)
+ => convention.ProcessStoredProcedureAnnotations(annotations, source, target, runtime));
+
+ annotations[RelationalAnnotationNames.UpdateStoredProcedure] = runtimeSproc;
+ }
}
}
@@ -315,6 +354,11 @@ protected override void ProcessPropertyAnnotations(
annotations.Remove(RelationalAnnotationNames.ViewColumnMappings);
annotations.Remove(RelationalAnnotationNames.SqlQueryColumnMappings);
annotations.Remove(RelationalAnnotationNames.FunctionColumnMappings);
+ annotations.Remove(RelationalAnnotationNames.InsertStoredProcedureParameterMappings);
+ annotations.Remove(RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings);
+ annotations.Remove(RelationalAnnotationNames.DeleteStoredProcedureParameterMappings);
+ annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureParameterMappings);
+ annotations.Remove(RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings);
annotations.Remove(RelationalAnnotationNames.DefaultColumnMappings);
}
else
@@ -430,7 +474,7 @@ private static RuntimeTrigger Create(ITrigger trigger, RuntimeEntityType runtime
=> new(runtimeEntityType, trigger.ModelName, trigger.Name, trigger.TableName, trigger.TableSchema);
///
- /// Updates the function annotations that will be set on the read-only object.
+ /// Updates the trigger annotations that will be set on the read-only object.
///
/// The annotations to be processed.
/// The source trigger.
@@ -443,4 +487,25 @@ protected virtual void ProcessTriggerAnnotations(
bool runtime)
{
}
+
+ private static RuntimeStoredProcedure Create(IStoredProcedure storedProcedure, RuntimeEntityType runtimeEntityType)
+ => new(runtimeEntityType,
+ storedProcedure.Name,
+ storedProcedure.Schema,
+ storedProcedure.AreTransactionsSuppressed);
+
+ ///
+ /// Updates the stored procedure annotations that will be set on the read-only object.
+ ///
+ /// The annotations to be processed.
+ /// The source stored procedure.
+ /// The target stored procedure that will contain the annotations.
+ /// Indicates whether the given annotations are runtime annotations.
+ protected virtual void ProcessStoredProcedureAnnotations(
+ Dictionary annotations,
+ IStoredProcedure storedProcedure,
+ RuntimeStoredProcedure runtimeStoredProcedure,
+ bool runtime)
+ {
+ }
}
diff --git a/src/EFCore.Relational/Metadata/IColumn.cs b/src/EFCore.Relational/Metadata/IColumn.cs
index 99685f72dc3..dd54cc67a9f 100644
--- a/src/EFCore.Relational/Metadata/IColumn.cs
+++ b/src/EFCore.Relational/Metadata/IColumn.cs
@@ -160,19 +160,8 @@ public virtual ValueComparer ProviderValueComparer
///
/// An entity type.
/// The property mapping or if not found.
- public virtual IColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
- {
- for (var i = 0; i < PropertyMappings.Count; i++)
- {
- var mapping = PropertyMappings[i];
- if (mapping.Property.DeclaringEntityType.IsAssignableFrom(entityType))
- {
- return mapping;
- }
- }
-
- return null;
- }
+ new IColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
+ => (IColumnMapping?)((IColumnBase)this).FindColumnMapping(entityType);
///
///
diff --git a/src/EFCore.Relational/Metadata/IColumnBase.cs b/src/EFCore.Relational/Metadata/IColumnBase.cs
index 4588f10abcd..565bee56ee4 100644
--- a/src/EFCore.Relational/Metadata/IColumnBase.cs
+++ b/src/EFCore.Relational/Metadata/IColumnBase.cs
@@ -40,4 +40,23 @@ public interface IColumnBase : IAnnotatable
/// Gets the property mappings.
///
IReadOnlyList PropertyMappings { get; }
+
+ ///
+ /// Returns the property mapping for the given entity type.
+ ///
+ /// An entity type.
+ /// The property mapping or if not found.
+ public virtual IColumnMappingBase? FindColumnMapping(IReadOnlyEntityType entityType)
+ {
+ for (var i = 0; i < PropertyMappings.Count; i++)
+ {
+ var mapping = PropertyMappings[i];
+ if (mapping.Property.DeclaringEntityType.IsAssignableFrom(entityType))
+ {
+ return mapping;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/EFCore.Relational/Metadata/IConventionRelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/IConventionRelationalPropertyOverrides.cs
index fff6a25c68a..687b4e490b7 100644
--- a/src/EFCore.Relational/Metadata/IConventionRelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/IConventionRelationalPropertyOverrides.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
+
namespace Microsoft.EntityFrameworkCore.Metadata;
///
@@ -48,4 +50,20 @@ public interface IConventionRelationalPropertyOverrides : IReadOnlyRelationalPro
///
/// The configuration source for .
ConfigurationSource? GetColumnNameConfigurationSource();
+
+ ///
+ /// Sets the direction of the stored procedure parameter.
+ ///
+ /// The direction.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The configured value.
+ ParameterDirection? SetDirection(ParameterDirection? direction, bool fromDataAnnotation = false)
+ => ((ParameterDirection?)SetAnnotation(RelationalAnnotationNames.ParameterDirection, direction, fromDataAnnotation)?.Value);
+
+ ///
+ /// Returns the configuration source for .
+ ///
+ /// The configuration source for .
+ ConfigurationSource? GetDirectionConfigurationSource()
+ => FindAnnotation(RelationalAnnotationNames.ParameterDirection)?.GetConfigurationSource();
}
diff --git a/src/EFCore.Relational/Metadata/IConventionStoredProcedure.cs b/src/EFCore.Relational/Metadata/IConventionStoredProcedure.cs
index 2c83f9883ac..1e83d739f97 100644
--- a/src/EFCore.Relational/Metadata/IConventionStoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/IConventionStoredProcedure.cs
@@ -6,30 +6,30 @@
namespace Microsoft.EntityFrameworkCore.Metadata;
///
-/// Represents a relational database function in a model in
+/// Represents a stored procedure in a model in
/// the form that can be mutated while the model is being built.
///
public interface IConventionStoredProcedure : IReadOnlyStoredProcedure, IConventionAnnotatable
{
///
- /// Gets the entity type in which this function is defined.
+ /// Gets the entity type in which this stored procedure is defined.
///
new IConventionEntityType EntityType { get; }
///
- /// Gets the builder that can be used to configure this function.
+ /// Gets the builder that can be used to configure this stored procedure.
///
/// If the function has been removed from the model.
new IConventionStoredProcedureBuilder Builder { get; }
///
- /// Gets the configuration source for this function.
+ /// Gets the configuration source for this stored procedure.
///
/// The configuration source for this function.
ConfigurationSource GetConfigurationSource();
///
- /// Sets the name of the function in the database.
+ /// Sets the name of the stored procedure in the database.
///
/// The name of the function in the database.
/// Indicates whether the configuration was specified using a data annotation.
@@ -43,7 +43,7 @@ public interface IConventionStoredProcedure : IReadOnlyStoredProcedure, IConvent
ConfigurationSource? GetNameConfigurationSource();
///
- /// Sets the schema of the function in the database.
+ /// Sets the schema of the stored procedure in the database.
///
/// The schema of the function in the database.
/// Indicates whether the configuration was specified using a data annotation.
diff --git a/src/EFCore.Relational/Metadata/IFunctionColumn.cs b/src/EFCore.Relational/Metadata/IFunctionColumn.cs
index 6ff09a72e94..7c6608e6a9a 100644
--- a/src/EFCore.Relational/Metadata/IFunctionColumn.cs
+++ b/src/EFCore.Relational/Metadata/IFunctionColumn.cs
@@ -23,6 +23,14 @@ public interface IFunctionColumn : IColumnBase
///
new IReadOnlyList PropertyMappings { get; }
+ ///
+ /// Returns the property mapping for the given entity type.
+ ///
+ /// An entity type.
+ /// The property mapping or if not found.
+ new IFunctionColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
+ => (IFunctionColumnMapping?)((IColumnBase)this).FindColumnMapping(entityType);
+
///
///
/// Creates a human-readable representation of the given metadata.
diff --git a/src/EFCore.Relational/Metadata/IFunctionMapping.cs b/src/EFCore.Relational/Metadata/IFunctionMapping.cs
index e6560eac15c..9925059082c 100644
--- a/src/EFCore.Relational/Metadata/IFunctionMapping.cs
+++ b/src/EFCore.Relational/Metadata/IFunctionMapping.cs
@@ -59,9 +59,9 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt
builder.Append("FunctionMapping: ");
}
- builder.Append(EntityType.Name).Append(" - ");
+ builder.Append(EntityType.DisplayName()).Append(" - ");
- builder.Append(Table.Name);
+ builder.Append(StoreFunction.Name);
if (IsDefaultFunctionMapping)
{
diff --git a/src/EFCore.Relational/Metadata/IMutableRelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/IMutableRelationalPropertyOverrides.cs
index 45063bdce75..badb6b3eb2f 100644
--- a/src/EFCore.Relational/Metadata/IMutableRelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/IMutableRelationalPropertyOverrides.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
+
namespace Microsoft.EntityFrameworkCore.Metadata;
///
@@ -21,6 +23,15 @@ public interface IMutableRelationalPropertyOverrides : IReadOnlyRelationalProper
///
new string? ColumnName { get; set; }
+ ///
+ /// Gets or sets the direction of the stored procedure parameter.
+ ///
+ new ParameterDirection? Direction
+ {
+ get => ((ParameterDirection?)this[RelationalAnnotationNames.ParameterDirection]);
+ set => SetAnnotation(RelationalAnnotationNames.ParameterDirection, value);
+ }
+
///
/// Removes the column name override.
///
diff --git a/src/EFCore.Relational/Metadata/IMutableStoredProcedure.cs b/src/EFCore.Relational/Metadata/IMutableStoredProcedure.cs
index 093d6f2a66b..25643f7622c 100644
--- a/src/EFCore.Relational/Metadata/IMutableStoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/IMutableStoredProcedure.cs
@@ -6,23 +6,23 @@
namespace Microsoft.EntityFrameworkCore.Metadata;
///
-/// Represents a relational database function in an model in
+/// Represents a stored procedure in a model in
/// the form that can be mutated while the model is being built.
///
public interface IMutableStoredProcedure : IReadOnlyStoredProcedure, IMutableAnnotatable
{
///
- /// Gets or sets the name of the function in the database.
+ /// Gets or sets the name of the stored procedure in the database.
///
new string? Name { get; [param: NotNull] set; }
///
- /// Gets or sets the schema of the function in the database.
+ /// Gets or sets the schema of the stored procedure in the database.
///
new string? Schema { get; set; }
///
- /// Gets the entity type in which this function is defined.
+ /// Gets the entity type in which this stored procedure is defined.
///
new IMutableEntityType EntityType { get; }
diff --git a/src/EFCore.Relational/Metadata/IReadOnlyRelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/IReadOnlyRelationalPropertyOverrides.cs
index 07dad4656a7..3d5cf0bb282 100644
--- a/src/EFCore.Relational/Metadata/IReadOnlyRelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/IReadOnlyRelationalPropertyOverrides.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
using System.Text;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -33,6 +34,12 @@ public interface IReadOnlyRelationalPropertyOverrides : IReadOnlyAnnotatable
///
bool IsColumnNameOverridden { get; }
+ ///
+ /// Gets the direction of the stored procedure parameter.
+ ///
+ ParameterDirection? Direction
+ => ((ParameterDirection?)this[RelationalAnnotationNames.ParameterDirection]);
+
///
///
/// Creates a human-readable representation of the given metadata.
diff --git a/src/EFCore.Relational/Metadata/IReadOnlyStoredProcedure.cs b/src/EFCore.Relational/Metadata/IReadOnlyStoredProcedure.cs
index 23739096e18..ba911ba0411 100644
--- a/src/EFCore.Relational/Metadata/IReadOnlyStoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/IReadOnlyStoredProcedure.cs
@@ -31,6 +31,36 @@ public interface IReadOnlyStoredProcedure : IReadOnlyAnnotatable
/// The configured value.
bool AreTransactionsSuppressed { get; }
+ ///
+ /// Returns the store identifier of this stored procedure.
+ ///
+ /// The store identifier. if there is no corresponding store object.
+ StoreObjectIdentifier? GetStoreIdentifier()
+ {
+ var name = Name;
+ if (name == null)
+ {
+ return null;
+ }
+
+ if (EntityType.GetInsertStoredProcedure() == this)
+ {
+ return StoreObjectIdentifier.InsertStoredProcedure(name, Schema);
+ }
+
+ if (EntityType.GetDeleteStoredProcedure() == this)
+ {
+ return StoreObjectIdentifier.DeleteStoredProcedure(name, Schema);
+ }
+
+ if (EntityType.GetUpdateStoredProcedure() == this)
+ {
+ return StoreObjectIdentifier.UpdateStoredProcedure(name, Schema);
+ }
+
+ return null;
+ }
+
///
/// Gets the names of properties mapped to parameters for this stored procedure.
///
diff --git a/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs b/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs
index 6e8cdb7a00e..024c470f1e2 100644
--- a/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs
+++ b/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs
@@ -92,6 +92,30 @@ public interface IRelationalAnnotationProvider
/// Whether the model should contain design-time configuration.
IEnumerable For(IFunctionColumn column, bool designTime);
+ ///
+ /// Gets provider-specific annotations for the given .
+ ///
+ /// The stored procedure.
+ /// The annotations.
+ /// Whether the model should contain design-time configuration.
+ IEnumerable For(IStoreStoredProcedure storedProcedure, bool designTime);
+
+ ///
+ /// Gets provider-specific annotations for the given .
+ ///
+ /// The parameter.
+ /// The annotations.
+ /// Whether the model should contain design-time configuration.
+ IEnumerable For(IStoreStoredProcedureParameter parameter, bool designTime);
+
+ ///
+ /// Gets provider-specific annotations for the given .
+ ///
+ /// The result column.
+ /// The annotations.
+ /// Whether the model should contain design-time configuration.
+ IEnumerable For(IStoreStoredProcedureResultColumn column, bool designTime);
+
///
/// Gets provider-specific annotations for the given .
///
diff --git a/src/EFCore.Relational/Metadata/IRelationalModel.cs b/src/EFCore.Relational/Metadata/IRelationalModel.cs
index 2d1afdb1cd6..befc25d803a 100644
--- a/src/EFCore.Relational/Metadata/IRelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/IRelationalModel.cs
@@ -46,6 +46,11 @@ IEnumerable Sequences
///
IEnumerable Functions { get; }
+ ///
+ /// Returns all stored procedures contained in the model.
+ ///
+ IEnumerable StoredProcedures { get; }
+
///
/// Returns the database collation.
///
@@ -93,9 +98,17 @@ IEnumerable Sequences
/// The name of the function.
/// The schema of the function.
/// A list of parameter types.
- /// The or if no function with the given name was defined.
+ /// The or if no function with the given name was found.
IStoreFunction? FindFunction(string name, string? schema, IReadOnlyList parameters);
+ ///
+ /// Finds a with the name.
+ ///
+ /// The name of the stored procedure.
+ /// The schema of the stored procedure.
+ /// The or if no stored procedure with the given name was found.
+ IStoreStoredProcedure? FindStoredProcedure(string name, string? schema);
+
///
///
/// Creates a human-readable representation of the given metadata.
diff --git a/src/EFCore.Relational/Metadata/ISqlQueryColumn.cs b/src/EFCore.Relational/Metadata/ISqlQueryColumn.cs
index e07d84a5b66..022c86733a7 100644
--- a/src/EFCore.Relational/Metadata/ISqlQueryColumn.cs
+++ b/src/EFCore.Relational/Metadata/ISqlQueryColumn.cs
@@ -24,6 +24,14 @@ public interface ISqlQueryColumn : IColumnBase
///
new IReadOnlyList PropertyMappings { get; }
+ ///
+ /// Returns the property mapping for the given entity type.
+ ///
+ /// An entity type.
+ /// The property mapping or if not found.
+ new ISqlQueryColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
+ => (ISqlQueryColumnMapping?)((IColumnBase)this).FindColumnMapping(entityType);
+
///
///
/// Creates a human-readable representation of the given metadata.
diff --git a/src/EFCore.Relational/Metadata/IStoreFunction.cs b/src/EFCore.Relational/Metadata/IStoreFunction.cs
index 280ae3e5f63..3f875d38189 100644
--- a/src/EFCore.Relational/Metadata/IStoreFunction.cs
+++ b/src/EFCore.Relational/Metadata/IStoreFunction.cs
@@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata;
public interface IStoreFunction : ITableBase
{
///
- /// Gets the associated s.
+ /// Gets the associated model functions.
///
IEnumerable DbFunctions { get; }
diff --git a/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs b/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs
new file mode 100644
index 00000000000..b78c5b8c360
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/IStoreStoredProcedure.cs
@@ -0,0 +1,134 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// Represents a stored procedure in a database.
+///
+public interface IStoreStoredProcedure : ITableBase
+{
+ ///
+ /// Gets the associated model stored procedures.
+ ///
+ IEnumerable StoredProcedures { get; }
+
+ ///
+ /// Gets the entity type mappings.
+ ///
+ new IEnumerable EntityTypeMappings { get; }
+
+ ///
+ /// Gets the parameters for this stored procedures.
+ ///
+ IEnumerable Parameters { get; }
+
+ ///
+ /// Gets the parameter with the given name. Returns
+ /// if no parameter with the given name is defined for the returned row set.
+ ///
+ IStoreStoredProcedureParameter? FindParameter(string name);
+
+ ///
+ /// Gets the parameter mapped to the given property. Returns
+ /// if no parameter is mapped to the given property.
+ ///
+ IStoreStoredProcedureParameter? FindParameter(IProperty property);
+
+ ///
+ /// Gets the columns defined for the returned row set.
+ ///
+ IEnumerable ResultColumns { get; }
+
+ ///
+ /// Gets the result column with the given name. Returns
+ /// if no result column with the given name is defined for the returned row set.
+ ///
+ IStoreStoredProcedureResultColumn? FindResultColumn(string name);
+
+ ///
+ /// Gets the result column mapped to the given property. Returns
+ /// if no result column is mapped to the given property.
+ ///
+ IStoreStoredProcedureResultColumn? FindResultColumn(IProperty property);
+
+ ///
+ ///
+ /// Creates a human-readable representation of the given metadata.
+ ///
+ ///
+ /// Warning: Do not rely on the format of the returned string.
+ /// It is designed for debugging only and may change arbitrarily between releases.
+ ///
+ ///
+ /// Options for generating the string.
+ /// The number of indent spaces to use before each new line.
+ /// A human-readable representation.
+ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, int indent = 0)
+ {
+ var builder = new StringBuilder();
+ var indentString = new string(' ', indent);
+
+ try
+ {
+ builder
+ .Append(indentString)
+ .Append("StoreStoredProcedure: ");
+
+ if (Schema != null)
+ {
+ builder
+ .Append(Schema)
+ .Append('.');
+ }
+
+ builder.Append(Name);
+
+ if ((options & MetadataDebugStringOptions.SingleLine) == 0)
+ {
+ var parameters = Parameters.ToList();
+ if (parameters.Count != 0)
+ {
+ builder.AppendLine().Append(indentString).Append(" Parameters: ");
+ foreach (var parameter in parameters)
+ {
+ builder.AppendLine().Append(parameter.ToDebugString(options, indent + 4));
+ }
+ }
+
+ var resultColumns = ResultColumns.ToList();
+ if (resultColumns.Count != 0)
+ {
+ builder.AppendLine().Append(indentString).Append(" ResultColumns: ");
+ foreach (var column in resultColumns)
+ {
+ builder.AppendLine().Append(column.ToDebugString(options, indent + 4));
+ }
+ }
+
+ var mappings = EntityTypeMappings.ToList();
+ if (mappings.Count != 0)
+ {
+ builder.AppendLine().Append(indentString).Append(" EntityTypeMappings: ");
+ foreach (var mapping in mappings)
+ {
+ builder.AppendLine().Append(mapping.ToDebugString(options, indent + 4));
+ }
+ }
+
+ if ((options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(AnnotationsToDebugString(indent: indent + 2));
+ }
+ }
+ }
+ catch (Exception exception)
+ {
+ builder.AppendLine().AppendLine(CoreStrings.DebugViewError(exception.Message));
+ }
+
+ return builder.ToString();
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/IStoreStoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/IStoreStoredProcedureParameter.cs
new file mode 100644
index 00000000000..de157b328e4
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/IStoreStoredProcedureParameter.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Data;
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// Represents a parameter in a stored procedure.
+///
+public interface IStoreStoredProcedureParameter : IColumnBase
+{
+ ///
+ /// Gets the containing stored procedure.
+ ///
+ IStoreStoredProcedure StoredProcedure { get; }
+
+ ///
+ /// Gets the property mappings.
+ ///
+ new IReadOnlyList PropertyMappings { get; }
+
+ ///
+ /// Gets the direction of the parameter.
+ ///
+ ParameterDirection Direction { get; }
+
+ ///
+ /// Returns the property mapping for the given entity type.
+ ///
+ /// An entity type.
+ /// The property mapping or if not found.
+ IStoredProcedureParameterMapping? FindParameterMapping(IReadOnlyEntityType entityType)
+ => (IStoredProcedureParameterMapping?)FindColumnMapping(entityType);
+
+ ///
+ ///
+ /// Creates a human-readable representation of the given metadata.
+ ///
+ ///
+ /// Warning: Do not rely on the format of the returned string.
+ /// It is designed for debugging only and may change arbitrarily between releases.
+ ///
+ ///
+ /// Options for generating the string.
+ /// The number of indent spaces to use before each new line.
+ /// A human-readable representation.
+ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, int indent = 0)
+ {
+ var builder = new StringBuilder();
+ var indentString = new string(' ', indent);
+
+ builder.Append(indentString);
+
+ var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0;
+ if (singleLine)
+ {
+ builder.Append($"StoredProcedureParameter: {Table.Name}.");
+ }
+
+ builder.Append(Name).Append(" (");
+ builder.Append(StoreType).Append(')');
+ builder.Append(IsNullable ? " Nullable" : " NonNullable");
+ builder.Append(')');
+
+ if (Direction != ParameterDirection.Input)
+ {
+ builder.Append(" ").Append(Direction);
+ }
+
+ if (!singleLine && (options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(AnnotationsToDebugString(indent + 2));
+ }
+
+ return builder.ToString();
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/IStoreStoredProcedureResultColumn.cs b/src/EFCore.Relational/Metadata/IStoreStoredProcedureResultColumn.cs
new file mode 100644
index 00000000000..4efca1fcbc9
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/IStoreStoredProcedureResultColumn.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// Represents a result column in a stored procedure.
+///
+public interface IStoreStoredProcedureResultColumn : IColumnBase
+{
+ ///
+ /// Gets the containing stored procedure.
+ ///
+ IStoreStoredProcedure StoredProcedure { get; }
+
+ ///
+ /// Gets the property mappings.
+ ///
+ new IReadOnlyList PropertyMappings { get; }
+
+ ///
+ /// Returns the property mapping for the given entity type.
+ ///
+ /// An entity type.
+ /// The property mapping or if not found.
+ new IStoredProcedureResultColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
+ => (IStoredProcedureResultColumnMapping?)((IColumnBase)this).FindColumnMapping(entityType);
+
+ ///
+ ///
+ /// Creates a human-readable representation of the given metadata.
+ ///
+ ///
+ /// Warning: Do not rely on the format of the returned string.
+ /// It is designed for debugging only and may change arbitrarily between releases.
+ ///
+ ///
+ /// Options for generating the string.
+ /// The number of indent spaces to use before each new line.
+ /// A human-readable representation.
+ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, int indent = 0)
+ {
+ var builder = new StringBuilder();
+ var indentString = new string(' ', indent);
+
+ builder.Append(indentString);
+
+ var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0;
+ if (singleLine)
+ {
+ builder.Append($"StoredProcedureResultColumn: {Table.Name}.");
+ }
+
+ builder.Append(Name).Append(" (");
+ builder.Append(StoreType).Append(')');
+ builder.Append(IsNullable ? " Nullable" : " NonNullable");
+ builder.Append(')');
+
+ if (!singleLine && (options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(AnnotationsToDebugString(indent + 2));
+ }
+
+ return builder.ToString();
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/IStoredProcedure.cs b/src/EFCore.Relational/Metadata/IStoredProcedure.cs
index 2e571045add..3b0cf61e6cf 100644
--- a/src/EFCore.Relational/Metadata/IStoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/IStoredProcedure.cs
@@ -4,25 +4,30 @@
namespace Microsoft.EntityFrameworkCore.Metadata;
///
-/// Represents a relational database function in a model.
+/// Represents a stored procedure in a model.
///
-///
-/// See Database functions for more information and examples.
-///
public interface IStoredProcedure : IReadOnlyStoredProcedure, IAnnotatable
{
///
/// Gets the name of the stored procedure in the database.
///
new string Name { get; }
-
+
///
- /// Gets the entity type in which this function is defined.
+ /// Gets the entity type in which this stored procedure is defined.
///
new IEntityType EntityType { get; }
- /////
- ///// Gets the associated .
- /////
- //IStoreFunction StoreFunction { get; }
+ ///
+ /// Gets the associated database stored procedure.
+ ///
+ IStoreStoredProcedure StoreStoredProcedure { get; }
+
+ ///
+ /// Returns the store identifier of this stored procedure.
+ ///
+ /// The store identifier.
+ new StoreObjectIdentifier GetStoreIdentifier()
+ => ((IReadOnlyStoredProcedure)this).GetStoreIdentifier()!.Value;
+
}
diff --git a/src/EFCore.Relational/Metadata/IStoredProcedureMapping.cs b/src/EFCore.Relational/Metadata/IStoredProcedureMapping.cs
new file mode 100644
index 00000000000..5a29707021a
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/IStoredProcedureMapping.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// Represents entity type mapping to a stored procedure.
+///
+public interface IStoredProcedureMapping : ITableMappingBase
+{
+ ///
+ /// Gets the target stored procedure in the database.
+ ///
+ IStoreStoredProcedure StoreStoredProcedure { get; }
+
+ ///
+ /// Gets the target stored procedure in the model.
+ ///
+ IStoredProcedure StoredProcedure { get; }
+
+ ///
+ /// Gets the stored procedure identifier including whether it's used for insert, delete or update.
+ ///
+ StoreObjectIdentifier StoredProcedureIdentifier { get; }
+
+ ///
+ /// Gets the parameter mappings corresponding to the target stored procedure.
+ ///
+ IEnumerable ParameterMappings { get; }
+
+ ///
+ /// Gets the result column mappings corresponding to the target stored procedure.
+ ///
+ IEnumerable ResultColumnMappings { get; }
+
+ ///
+ ///
+ /// Creates a human-readable representation of the given metadata.
+ ///
+ ///
+ /// Warning: Do not rely on the format of the returned string.
+ /// It is designed for debugging only and may change arbitrarily between releases.
+ ///
+ ///
+ /// Options for generating the string.
+ /// The number of indent spaces to use before each new line.
+ /// A human-readable representation.
+ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, int indent = 0)
+ {
+ var builder = new StringBuilder();
+ var indentString = new string(' ', indent);
+
+ builder.Append(indentString);
+
+ var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0;
+ if (singleLine)
+ {
+ builder.Append("StoredProcedureMapping: ");
+ }
+
+ builder.Append(EntityType.DisplayName()).Append(" - ");
+
+ builder.Append(StoreStoredProcedure.Name);
+
+ builder.Append(" Type:").Append(StoredProcedureIdentifier.StoreObjectType);
+
+ if (IncludesDerivedTypes)
+ {
+ builder.Append(" IncludesDerivedTypes");
+ }
+
+ if (!singleLine && (options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(AnnotationsToDebugString(indent + 2));
+ }
+
+ return builder.ToString();
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/IStoredProcedureParameterMapping.cs b/src/EFCore.Relational/Metadata/IStoredProcedureParameterMapping.cs
new file mode 100644
index 00000000000..f3f38d5817c
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/IStoredProcedureParameterMapping.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// Represents property mapping to a stored procedure parameter.
+///
+public interface IStoredProcedureParameterMapping : IColumnMappingBase
+{
+ ///
+ /// Gets the target parameter.
+ ///
+ IStoreStoredProcedureParameter Parameter { get; }
+
+ ///
+ /// Gets the containing stored procedure mapping.
+ ///
+ IStoredProcedureMapping StoredProcedureMapping { get; }
+
+ ///
+ ///
+ /// Creates a human-readable representation of the given metadata.
+ ///
+ ///
+ /// Warning: Do not rely on the format of the returned string.
+ /// It is designed for debugging only and may change arbitrarily between releases.
+ ///
+ ///
+ /// Options for generating the string.
+ /// The number of indent spaces to use before each new line.
+ /// A human-readable representation.
+ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, int indent = 0)
+ {
+ var builder = new StringBuilder();
+ var indentString = new string(' ', indent);
+
+ builder.Append(indentString);
+
+ var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0;
+ if (singleLine)
+ {
+ builder.Append("StoredProcedureParameterMapping: ");
+ }
+
+ builder.Append(Property.Name).Append(" - ");
+
+ builder.Append(Column.Name);
+
+ if (!singleLine && (options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(AnnotationsToDebugString(indent + 2));
+ }
+
+ return builder.ToString();
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/IStoredProcedureResultColumnMapping.cs b/src/EFCore.Relational/Metadata/IStoredProcedureResultColumnMapping.cs
new file mode 100644
index 00000000000..fc4ce0ed160
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/IStoredProcedureResultColumnMapping.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// Represents property mapping to a stored procedure result column.
+///
+public interface IStoredProcedureResultColumnMapping : IColumnMappingBase
+{
+ ///
+ /// Gets the target column.
+ ///
+ new IStoreStoredProcedureResultColumn Column { get; }
+
+ ///
+ /// Gets the containing stored procedure mapping.
+ ///
+ IStoredProcedureMapping StoredProcedureMapping { get; }
+
+ ///
+ ///
+ /// Creates a human-readable representation of the given metadata.
+ ///
+ ///
+ /// Warning: Do not rely on the format of the returned string.
+ /// It is designed for debugging only and may change arbitrarily between releases.
+ ///
+ ///
+ /// Options for generating the string.
+ /// The number of indent spaces to use before each new line.
+ /// A human-readable representation.
+ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, int indent = 0)
+ {
+ var builder = new StringBuilder();
+ var indentString = new string(' ', indent);
+
+ builder.Append(indentString);
+
+ var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0;
+ if (singleLine)
+ {
+ builder.Append("StoredProcedureResultColumnMapping: ");
+ }
+
+ builder.Append(Property.Name).Append(" - ");
+
+ builder.Append(Column.Name);
+
+ if (!singleLine && (options & MetadataDebugStringOptions.IncludeAnnotations) != 0)
+ {
+ builder.Append(AnnotationsToDebugString(indent + 2));
+ }
+
+ return builder.ToString();
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/IViewColumn.cs b/src/EFCore.Relational/Metadata/IViewColumn.cs
index a43bb1e76b8..40a82aa2b20 100644
--- a/src/EFCore.Relational/Metadata/IViewColumn.cs
+++ b/src/EFCore.Relational/Metadata/IViewColumn.cs
@@ -23,6 +23,14 @@ public interface IViewColumn : IColumnBase
///
new IReadOnlyList PropertyMappings { get; }
+ ///
+ /// Returns the property mapping for the given entity type.
+ ///
+ /// An entity type.
+ /// The property mapping or if not found.
+ new IViewColumnMapping? FindColumnMapping(IReadOnlyEntityType entityType)
+ => (IViewColumnMapping?)((IColumnBase)this).FindColumnMapping(entityType);
+
///
///
/// Creates a human-readable representation of the given metadata.
diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
index 0e6f337e43c..b1878d8a0ae 100644
--- a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
+++ b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
@@ -25,6 +25,7 @@ public class DbFunction : ConventionAnnotatable, IMutableDbFunction, IConvention
private RelationalTypeMapping? _typeMapping;
private Func, SqlExpression>? _translation;
private InternalDbFunctionBuilder? _builder;
+ private IStoreFunction? _storeFunction;
private ConfigurationSource _configurationSource;
private ConfigurationSource? _schemaConfigurationSource;
@@ -642,15 +643,6 @@ public virtual IReadOnlyList Parameters
public virtual DbFunctionParameter? FindParameter(string name)
=> Parameters.SingleOrDefault(p => p.Name == 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
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DisallowNull]
- public virtual IStoreFunction? StoreFunction { get; set; }
-
///
/// 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
@@ -767,11 +759,11 @@ bool IConventionDbFunction.SetIsNullable(bool nullable, bool fromDataAnnotation)
///
IStoreFunction IDbFunction.StoreFunction
- => StoreFunction!; // Relational model creation ensures StoreFunction is populated
+ => _storeFunction!; // Relational model creation ensures StoreFunction is populated
IStoreFunction IRuntimeDbFunction.StoreFunction
{
- get => StoreFunction!;
- set => StoreFunction = value;
+ get => _storeFunction!;
+ set => _storeFunction = value;
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs
index ec3c11cb122..43770ef97d0 100644
--- a/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs
+++ b/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs
@@ -20,8 +20,8 @@ public class FunctionColumnMapping : ColumnMappingBase, IFunctionColumnMapping
public FunctionColumnMapping(
IProperty property,
FunctionColumn column,
- FunctionMapping viewMapping)
- : base(property, column, viewMapping)
+ FunctionMapping functionMapping)
+ : base(property, column, functionMapping)
{
}
diff --git a/src/EFCore.Relational/Metadata/Internal/IRuntimeStoredProcedure.cs b/src/EFCore.Relational/Metadata/Internal/IRuntimeStoredProcedure.cs
new file mode 100644
index 00000000000..29c667a548e
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/IRuntimeStoredProcedure.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public interface IRuntimeStoredProcedure : IStoredProcedure
+{
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ new IStoreStoredProcedure StoreStoredProcedure { get; set; }
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs
index 834f6d9f868..b4bebbfdef0 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs
@@ -38,7 +38,7 @@ public static InternalStoredProcedureBuilder HasStoredProcedure(
string? name = null,
string? schema = null)
{
- var sproc = StoredProcedure.GetDeclaredStoredProcedure(entityType, sprocType);
+ var sproc = (StoredProcedure?)StoredProcedure.FindDeclaredStoredProcedure(entityType, sprocType);
if (sproc == null)
{
sproc = name == null
@@ -69,7 +69,7 @@ public static InternalStoredProcedureBuilder HasStoredProcedure(
StoreObjectType sprocType,
bool fromDataAnnotation)
{
- var sproc = StoredProcedure.GetDeclaredStoredProcedure(entityType, sprocType);
+ var sproc = (StoredProcedure?)StoredProcedure.FindDeclaredStoredProcedure(entityType, sprocType);
if (sproc == null)
{
sproc = StoredProcedure.SetStoredProcedure(entityType, sprocType, fromDataAnnotation);
@@ -156,57 +156,6 @@ public virtual bool CanSetName(string? name, ConfigurationSource configurationSo
public virtual bool CanSetSchema(string? schema, ConfigurationSource configurationSource)
=> configurationSource.Overrides(Metadata.GetSchemaConfigurationSource())
|| Metadata.Schema == schema;
-
- ///
- /// 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
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual PropertyBuilder CreatePropertyBuilder(EntityTypeBuilder entityTypeBuilder, string propertyName)
- {
- var entityType = entityTypeBuilder.Metadata;
- var property = entityType.FindProperty(propertyName);
- if (property == null)
- {
- property = entityType.GetDerivedTypes().SelectMany(et => et.GetDeclaredProperties())
- .FirstOrDefault(p => p.Name == propertyName);
- }
-
- if (property == null)
- {
- throw new InvalidOperationException(CoreStrings.PropertyNotFound(propertyName, entityType.DisplayName()));
- }
-
-#pragma warning disable EF1001 // Internal EF Core API usage.
- return new ModelBuilder(entityType.Model)
-#pragma warning restore EF1001 // Internal EF Core API usage.
- .Entity(property.DeclaringEntityType.Name)
- .Property(property.ClrType, propertyName);
- }
-
- ///
- /// 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
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual PropertyBuilder CreatePropertyBuilder(
- EntityTypeBuilder entityTypeBuilder,
- Expression> propertyExpression)
- where TDerivedEntity : class
- {
- var memberInfo = propertyExpression.GetMemberAccess();
- var entityType = entityTypeBuilder.Metadata;
- if (entityType.ClrType != typeof(TDerivedEntity))
- {
-#pragma warning disable EF1001 // Internal EF Core API usage.
- entityTypeBuilder = new ModelBuilder(entityType.Model).Entity(typeof(TDerivedEntity));
-#pragma warning restore EF1001 // Internal EF Core API usage.
- }
-
- return entityTypeBuilder.Property(memberInfo.GetMemberType(), memberInfo.Name);
- }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index d01f84c0462..534d3160ffc 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -81,6 +81,15 @@ public override bool IsReadOnly
public virtual SortedDictionary<(string, string?, IReadOnlyList), StoreFunction> Functions { get; }
= new(NamedListComparer.Instance);
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual SortedDictionary<(string, string?), StoreStoredProcedure> StoredProcedures { get; }
+ = new();
+
///
public virtual ITable? FindTable(string name, string? schema)
=> Tables.TryGetValue((name, schema), out var table)
@@ -105,6 +114,12 @@ public override bool IsReadOnly
? function
: null;
+ ///
+ public virtual IStoreStoredProcedure? FindStoredProcedure(string name, string? schema)
+ => StoredProcedures.TryGetValue((name, schema), out var storedProcedure)
+ ? storedProcedure
+ : 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
@@ -144,6 +159,8 @@ public static IRelationalModel Create(
AddSqlQueries(databaseModel, entityType);
AddMappedFunctions(databaseModel, entityType);
+
+ AddStoredProcedures(databaseModel, entityType);
}
AddTvfs(databaseModel);
@@ -215,9 +232,9 @@ public static IRelationalModel Create(
}
}
- foreach (var query in databaseModel.Queries.Values)
+ if (relationalAnnotationProvider != null)
{
- if (relationalAnnotationProvider != null)
+ foreach (var query in databaseModel.Queries.Values)
{
foreach (SqlQueryColumn queryColumn in query.Columns.Values)
{
@@ -226,11 +243,8 @@ public static IRelationalModel Create(
query.AddAnnotations(relationalAnnotationProvider.For(query, designTime));
}
- }
- foreach (var function in databaseModel.Functions.Values)
- {
- if (relationalAnnotationProvider != null)
+ foreach (var function in databaseModel.Functions.Values)
{
foreach (FunctionColumn functionColumn in function.Columns.Values)
{
@@ -239,10 +253,22 @@ public static IRelationalModel Create(
function.AddAnnotations(relationalAnnotationProvider.For(function, designTime));
}
- }
- if (relationalAnnotationProvider != null)
- {
+ foreach (var storedProcedure in databaseModel.StoredProcedures.Values)
+ {
+ foreach (StoreStoredProcedureParameter parameter in storedProcedure.Parameters)
+ {
+ parameter.AddAnnotations(relationalAnnotationProvider.For(parameter, designTime));
+ }
+
+ foreach (StoreStoredProcedureResultColumn resultColumn in storedProcedure.ResultColumns)
+ {
+ resultColumn.AddAnnotations(relationalAnnotationProvider.For(resultColumn, designTime));
+ }
+
+ storedProcedure.AddAnnotations(relationalAnnotationProvider.For(storedProcedure, designTime));
+ }
+
foreach (var sequence in ((IRelationalModel)databaseModel).Sequences)
{
((AnnotatableBase)sequence).AddAnnotations(relationalAnnotationProvider.For(sequence, designTime));
@@ -273,7 +299,8 @@ private static void AddDefaultMappings(RelationalModel databaseModel, IEntityTyp
databaseModel.DefaultTables.Add(mappedTableName, defaultTable);
}
- var tableMapping = new TableMappingBase(entityType, defaultTable, includesDerivedTypes: !isTpc && mappedType == entityType);
+ var tableMapping = new TableMappingBase(
+ entityType, defaultTable, includesDerivedTypes: !isTpc && mappedType == entityType);
foreach (var property in entityType.GetProperties())
{
@@ -353,7 +380,7 @@ private static void AddTables(RelationalModel databaseModel, IEntityType entityT
if (mappedTableName == null)
{
- if (isTpc)
+ if (isTpc || mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)
{
break;
}
@@ -394,7 +421,7 @@ private static void AddTables(RelationalModel databaseModel, IEntityType entityT
tableMappings.Reverse();
}
- private static TableMapping CreateTableMapping(
+ private static void CreateTableMapping(
IEntityType entityType,
IEntityType mappedType,
StoreObjectIdentifier mappedTable,
@@ -413,7 +440,7 @@ private static TableMapping CreateTableMapping(
{
IsSplitEntityTypePrincipal = isSplitEntityTypePrincipal
};
-
+
foreach (var property in mappedType.GetProperties())
{
var columnName = property.GetColumnName(mappedTable);
@@ -456,8 +483,6 @@ private static TableMapping CreateTableMapping(
tableMappings.Add(tableMapping);
table.EntityTypeMappings.Add(tableMapping);
}
-
- return tableMapping;
}
private static void AddViews(RelationalModel databaseModel, IEntityType entityType)
@@ -482,7 +507,7 @@ private static void AddViews(RelationalModel databaseModel, IEntityType entityTy
if (mappedViewName == null)
{
- if (isTpc)
+ if (isTpc || mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)
{
break;
}
@@ -630,10 +655,7 @@ private static void AddSqlQueries(RelationalModel databaseModel, IEntityType ent
databaseModel.Queries.Add(mappedQuery.Name, sqlQuery);
}
- var queryMapping = new SqlQueryMapping(entityType, sqlQuery, includesDerivedTypes: true)
- {
- IsDefaultSqlQueryMapping = true
- };
+ var queryMapping = new SqlQueryMapping(entityType, sqlQuery, includesDerivedTypes: true) { IsDefaultSqlQueryMapping = true };
foreach (var property in mappedType.GetProperties())
{
@@ -793,8 +815,10 @@ private static FunctionMapping CreateFunctionMapping(
var column = (FunctionColumn?)storeFunction.FindColumn(columnName);
if (column == null)
{
- column = new FunctionColumn(columnName, property.GetColumnType(mappedFunction), storeFunction);
- column.IsNullable = property.IsColumnNullable(mappedFunction);
+ column = new FunctionColumn(columnName, property.GetColumnType(mappedFunction), storeFunction)
+ {
+ IsNullable = property.IsColumnNullable(mappedFunction)
+ };
storeFunction.Columns.Add(columnName, column);
}
else if (!property.IsColumnNullable(mappedFunction))
@@ -846,6 +870,284 @@ private static StoreFunction GetOrCreateStoreFunction(IRuntimeDbFunction dbFunct
return storeFunction;
}
+ private static void AddStoredProcedures(RelationalModel databaseModel, IEntityType entityType)
+ {
+ var mappedType = entityType;
+
+ Check.DebugAssert(
+ entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.InsertStoredProcedureMappings) == null, "not null");
+ var insertStoredProcedureMappings = new List();
+
+ Check.DebugAssert(
+ entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.DeleteStoredProcedureMappings) == null, "not null");
+ var deleteStoredProcedureMappings = new List();
+
+ Check.DebugAssert(
+ entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.UpdateStoredProcedureMappings) == null, "not null");
+ var updateStoredProcedureMappings = new List();
+
+ var mappingStrategy = entityType.GetMappingStrategy();
+ var isTpc = mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy;
+ while (mappedType != null)
+ {
+ var insertSproc = (IRuntimeStoredProcedure?)mappedType.GetInsertStoredProcedure();
+ if (insertSproc != null
+ && insertStoredProcedureMappings != null)
+ {
+ CreateStoredProcedureMapping(
+ entityType,
+ mappedType,
+ insertSproc,
+ databaseModel,
+ insertStoredProcedureMappings,
+ includesDerivedTypes: !isTpc && mappedType == entityType);
+ }
+ else if (entityType == mappedType)
+ {
+ insertStoredProcedureMappings = null;
+ }
+
+ var deleteSproc = (IRuntimeStoredProcedure?)mappedType.GetDeleteStoredProcedure();
+ if (deleteSproc != null
+ && deleteStoredProcedureMappings != null)
+ {
+ CreateStoredProcedureMapping(
+ entityType,
+ mappedType,
+ deleteSproc,
+ databaseModel,
+ deleteStoredProcedureMappings,
+ includesDerivedTypes: !isTpc && mappedType == entityType);
+ }
+ else if (entityType == mappedType)
+ {
+ deleteStoredProcedureMappings = null;
+ }
+
+ var updateSproc = (IRuntimeStoredProcedure?)mappedType.GetUpdateStoredProcedure();
+ if (updateSproc != null
+ && updateStoredProcedureMappings != null)
+ {
+ CreateStoredProcedureMapping(
+ entityType,
+ mappedType,
+ updateSproc,
+ databaseModel,
+ updateStoredProcedureMappings,
+ includesDerivedTypes: !isTpc && mappedType == entityType);
+ }
+ else if (entityType == mappedType)
+ {
+ updateStoredProcedureMappings = null;
+ }
+
+ if (isTpc || mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)
+ {
+ break;
+ }
+
+ mappedType = mappedType.BaseType;
+ }
+
+ if (insertStoredProcedureMappings?.Count > 0)
+ {
+ insertStoredProcedureMappings.Reverse();
+ entityType.SetRuntimeAnnotation(RelationalAnnotationNames.InsertStoredProcedureMappings, insertStoredProcedureMappings);
+ }
+
+ if (deleteStoredProcedureMappings?.Count > 0)
+ {
+ deleteStoredProcedureMappings.Reverse();
+ entityType.SetRuntimeAnnotation(RelationalAnnotationNames.DeleteStoredProcedureMappings, deleteStoredProcedureMappings);
+ }
+
+ if (updateStoredProcedureMappings?.Count > 0)
+ {
+ updateStoredProcedureMappings.Reverse();
+ entityType.SetRuntimeAnnotation(RelationalAnnotationNames.UpdateStoredProcedureMappings, updateStoredProcedureMappings);
+ }
+ }
+
+ private static void CreateStoredProcedureMapping(
+ IEntityType entityType,
+ IEntityType mappedType,
+ IRuntimeStoredProcedure storedProcedure,
+ RelationalModel model,
+ List storedProcedureMappings,
+ bool includesDerivedTypes)
+ {
+ var storeStoredProcedure = GetOrCreateStoreStoredProcedure(storedProcedure, model);
+
+ var identifier = storedProcedure.GetStoreIdentifier();
+ var storedProcedureMapping = new StoredProcedureMapping(
+ entityType, storeStoredProcedure, storedProcedure, includesDerivedTypes);
+ var (parameterMappingAnnotationName, columnMappingAnnotationName) = identifier.StoreObjectType switch
+ {
+ StoreObjectType.InsertStoredProcedure
+ => (RelationalAnnotationNames.InsertStoredProcedureParameterMappings,
+ RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings),
+ StoreObjectType.DeleteStoredProcedure
+ => (RelationalAnnotationNames.DeleteStoredProcedureParameterMappings, ""),
+ StoreObjectType.UpdateStoredProcedure
+ => (RelationalAnnotationNames.UpdateStoredProcedureParameterMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings),
+ _ => throw new Exception("Unexpected stored procedure type: " + identifier.StoreObjectType)
+ };
+
+ foreach (var parameter in storedProcedure.Parameters)
+ {
+ var property = mappedType.FindProperty(parameter);
+ if (property == null)
+ {
+ Check.DebugAssert(
+ entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy,
+ "Expected TPH for " + entityType.DisplayName());
+
+ if (entityType.BaseType == null)
+ {
+ foreach (var derivedProperty in entityType.GetDerivedProperties())
+ {
+ if (derivedProperty.Name == parameter)
+ {
+ GetOrCreateStoreStoredProcedureParameter(derivedProperty, storeStoredProcedure, identifier);
+ break;
+ }
+ }
+ }
+
+ continue;
+ }
+
+ var storeParameter = GetOrCreateStoreStoredProcedureParameter(property, storeStoredProcedure, identifier);
+
+ var columnMapping = new StoredProcedureParameterMapping(property, storeParameter, storedProcedureMapping);
+ storedProcedureMapping.AddParameterMapping(columnMapping);
+ storeParameter.AddPropertyMapping(columnMapping);
+
+ if (property.FindRuntimeAnnotationValue(parameterMappingAnnotationName)
+ is not SortedSet columnMappings)
+ {
+ columnMappings = new SortedSet(ColumnMappingBaseComparer.Instance);
+ property.AddRuntimeAnnotation(parameterMappingAnnotationName, columnMappings);
+ }
+
+ columnMappings.Add(columnMapping);
+ }
+
+ foreach (var resultColumn in storedProcedure.ResultColumns)
+ {
+ var property = mappedType.FindProperty(resultColumn);
+ if (property == null)
+ {
+ Check.DebugAssert(
+ entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy,
+ "Expected TPH for " + entityType.DisplayName());
+
+ if (entityType.BaseType == null)
+ {
+ foreach (var derivedProperty in entityType.GetDerivedProperties())
+ {
+ if (derivedProperty.Name == resultColumn)
+ {
+ GetOrCreateStoreStoredProcedureResultColumn(derivedProperty, storeStoredProcedure, identifier);
+ break;
+ }
+ }
+ }
+
+ continue;
+ }
+
+ var column = GetOrCreateStoreStoredProcedureResultColumn(property, storeStoredProcedure, identifier);
+
+ var columnMapping = new StoredProcedureResultColumnMapping(property, column, storedProcedureMapping);
+ storedProcedureMapping.AddColumnMapping(columnMapping);
+ column.AddPropertyMapping(columnMapping);
+
+ if (property.FindRuntimeAnnotationValue(columnMappingAnnotationName)
+ is not SortedSet columnMappings)
+ {
+ columnMappings = new SortedSet(ColumnMappingBaseComparer.Instance);
+ property.AddRuntimeAnnotation(columnMappingAnnotationName, columnMappings);
+ }
+
+ columnMappings.Add(columnMapping);
+ }
+
+ storedProcedureMappings.Add(storedProcedureMapping);
+ storeStoredProcedure.EntityTypeMappings.Add(storedProcedureMapping);
+
+ static StoreStoredProcedure GetOrCreateStoreStoredProcedure(
+ IRuntimeStoredProcedure storedProcedure,
+ RelationalModel model)
+ {
+ var storeStoredProcedure = (StoreStoredProcedure?)storedProcedure.StoreStoredProcedure;
+ if (storeStoredProcedure == null)
+ {
+ storeStoredProcedure = (StoreStoredProcedure?)model.FindStoredProcedure(storedProcedure.Name, storedProcedure.Schema);
+ if (storeStoredProcedure == null)
+ {
+ storeStoredProcedure = new StoreStoredProcedure(storedProcedure, model);
+ model.StoredProcedures.Add((storeStoredProcedure.Name, storeStoredProcedure.Schema), storeStoredProcedure);
+ }
+ else
+ {
+ storedProcedure.StoreStoredProcedure = storeStoredProcedure;
+ storeStoredProcedure.StoredProcedures.Add(storedProcedure);
+ }
+ }
+
+ return storeStoredProcedure;
+ }
+
+ static StoreStoredProcedureParameter GetOrCreateStoreStoredProcedureParameter(
+ IProperty property,
+ StoreStoredProcedure storeStoredProcedure,
+ StoreObjectIdentifier identifier)
+ {
+ var columnName = property.GetColumnName(identifier)!;
+ var storeParameter = (StoreStoredProcedureParameter?)storeStoredProcedure.FindParameter(columnName);
+ if (storeParameter == null)
+ {
+ storeParameter = new StoreStoredProcedureParameter(
+ columnName,
+ property.GetColumnType(identifier),
+ storeStoredProcedure,
+ property.GetDirection(identifier)) { IsNullable = property.IsColumnNullable(identifier) };
+ storeStoredProcedure.AddParameter(storeParameter);
+ }
+ else if (!property.IsColumnNullable(identifier))
+ {
+ storeParameter.IsNullable = false;
+ }
+
+ return storeParameter;
+ }
+
+ static StoreStoredProcedureResultColumn GetOrCreateStoreStoredProcedureResultColumn(
+ IProperty property,
+ StoreStoredProcedure storeStoredProcedure,
+ StoreObjectIdentifier identifier)
+ {
+ var columnName = property.GetColumnName(identifier)!;
+ var column = (StoreStoredProcedureResultColumn?)storeStoredProcedure.FindResultColumn(columnName);
+ if (column == null)
+ {
+ column = new StoreStoredProcedureResultColumn(columnName, property.GetColumnType(identifier), storeStoredProcedure)
+ {
+ IsNullable = property.IsColumnNullable(identifier)
+ };
+ storeStoredProcedure.AddResultColumn(column);
+ }
+ else if (!property.IsColumnNullable(identifier))
+ {
+ column.IsNullable = false;
+ }
+
+ return column;
+ }
+ }
+
private static void PopulateTableConfiguration(Table table, bool designTime)
{
var storeObject = StoreObjectIdentifier.Table(table.Name, table.Schema);
@@ -1003,7 +1305,7 @@ private static void PopulateRowInternalForeignKeys(TableBase tab
{
entityTypeMapping.IsSharedTablePrincipal = false;
}
-
+
var entityType = entityTypeMapping.EntityType;
mappedEntityTypes.Add(entityType);
var primaryKey = entityType.FindPrimaryKey();
@@ -1295,6 +1597,12 @@ IEnumerable IRelationalModel.Functions
get => Functions.Values;
}
+ IEnumerable IRelationalModel.StoredProcedures
+ {
+ [DebuggerStepThrough]
+ get => StoredProcedures.Values;
+ }
+
IEnumerable IRelationalModel.Queries
{
[DebuggerStepThrough]
diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs
new file mode 100644
index 00000000000..74132283939
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs
@@ -0,0 +1,159 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class StoreStoredProcedure : TableBase, IStoreStoredProcedure
+{
+ private readonly SortedDictionary _parametersSet;
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public StoreStoredProcedure(IRuntimeStoredProcedure sproc, RelationalModel model)
+ : base(sproc.Name, sproc.Schema, model)
+ {
+ StoredProcedures = new(StoredProcedureComparer.Instance) { { sproc } };
+
+ sproc.StoreStoredProcedure = this;
+
+ _parametersSet = new(StringComparer.Ordinal);
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual SortedSet StoredProcedures { 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual List Parameters { get; protected set; }
+ = new();
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void AddParameter(IStoreStoredProcedureParameter parameter)
+ {
+ _parametersSet[parameter.Name] = parameter;
+ Parameters.Add(parameter);
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IStoreStoredProcedureParameter? FindParameter(string name)
+ => _parametersSet.TryGetValue(name, out var parameter)
+ ? parameter
+ : 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IStoreStoredProcedureParameter? FindParameter(IProperty property)
+ => property.GetInsertStoredProcedureParameterMappings()
+ .Concat(property.GetDeleteStoredProcedureParameterMappings())
+ .Concat(property.GetUpdateStoredProcedureParameterMappings())
+ .FirstOrDefault(cm => cm.StoredProcedureMapping.StoreStoredProcedure == this)
+ ?.Parameter;
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual List ResultColumns { get; protected set; }
+ = new();
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void AddResultColumn(IStoreStoredProcedureResultColumn column)
+ {
+ Columns[column.Name] = column;
+ ResultColumns.Add(column);
+ }
+
+ ///
+ public override IColumnBase? FindColumn(IProperty property)
+ => property.GetInsertStoredProcedureResultColumnMappings()
+ .Concat(property.GetUpdateStoredProcedureResultColumnMappings())
+ .FirstOrDefault(cm => cm.StoredProcedureMapping.StoreStoredProcedure == this)
+ ?.Column;
+
+ ///
+ [DebuggerStepThrough]
+ public virtual IStoreStoredProcedureResultColumn? FindResultColumn(string name)
+ => (IStoreStoredProcedureResultColumn?)base.FindColumn(name);
+
+ ///
+ [DebuggerStepThrough]
+ public virtual IStoreStoredProcedureResultColumn? FindResultColumn(IProperty property)
+ => (IStoreStoredProcedureResultColumn?)FindColumn(property);
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoreStoredProcedure)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ IEnumerable IStoreStoredProcedure.StoredProcedures
+ {
+ [DebuggerStepThrough]
+ get => StoredProcedures;
+ }
+
+ ///
+ IEnumerable IStoreStoredProcedure.EntityTypeMappings
+ {
+ [DebuggerStepThrough]
+ get => EntityTypeMappings.Cast();
+ }
+
+ ///
+ IEnumerable IStoreStoredProcedure.Parameters
+ {
+ [DebuggerStepThrough]
+ get => Parameters;
+ }
+
+ ///
+ IEnumerable IStoreStoredProcedure.ResultColumns
+ {
+ [DebuggerStepThrough]
+ get => ResultColumns;
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs
new file mode 100644
index 00000000000..ddfa4297778
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureParameter.cs
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Data;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class StoreStoredProcedureParameter
+ : ColumnBase, IStoreStoredProcedureParameter
+{
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public StoreStoredProcedureParameter(
+ string name,
+ string type,
+ StoreStoredProcedure storedProcedure,
+ ParameterDirection direction)
+ : base(name, type, storedProcedure)
+ {
+ Direction = direction;
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual StoreStoredProcedure StoredProcedure
+ => (StoreStoredProcedure)Table;
+
+ ///
+ 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoreStoredProcedureParameter)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ IStoreStoredProcedure IStoreStoredProcedureParameter.StoredProcedure
+ {
+ [DebuggerStepThrough]
+ get => StoredProcedure;
+ }
+
+ ///
+ IReadOnlyList IStoreStoredProcedureParameter.PropertyMappings
+ {
+ [DebuggerStepThrough]
+ get => PropertyMappings;
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs
new file mode 100644
index 00000000000..f1f16053304
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class StoreStoredProcedureResultColumn
+ : ColumnBase, IStoreStoredProcedureResultColumn
+{
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public StoreStoredProcedureResultColumn(string name, string type, StoreStoredProcedure storedProcedure)
+ : base(name, type, storedProcedure)
+ {
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoreStoredProcedureResultColumn)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ IStoreStoredProcedure IStoreStoredProcedureResultColumn.StoredProcedure
+ {
+ [DebuggerStepThrough]
+ get => StoredProcedure;
+ }
+
+ ///
+ IReadOnlyList IStoreStoredProcedureResultColumn.PropertyMappings
+ {
+ [DebuggerStepThrough]
+ get => PropertyMappings;
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs
index 07c0ccb556f..6841e9ed6b4 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs
@@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class StoredProcedure :
- ConventionAnnotatable, IStoredProcedure, IMutableStoredProcedure, IConventionStoredProcedure
+ ConventionAnnotatable, IRuntimeStoredProcedure, IMutableStoredProcedure, IConventionStoredProcedure
{
private readonly List _parameters = new();
private readonly HashSet _parametersSet = new();
@@ -20,6 +20,7 @@ public class StoredProcedure :
private string? _name;
private InternalStoredProcedureBuilder? _builder;
private bool _areTransactionsSuppressed;
+ private IStoreStoredProcedure? _storeStoredProcedure;
private ConfigurationSource _configurationSource;
private ConfigurationSource? _schemaConfigurationSource;
@@ -86,11 +87,11 @@ public override bool IsReadOnly
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public static StoredProcedure? FindStoredProcedure(
+ public static IStoredProcedure? FindStoredProcedure(
IReadOnlyEntityType entityType,
StoreObjectType sprocType)
{
- var storedProcedure = GetDeclaredStoredProcedure(entityType, sprocType);
+ var storedProcedure = FindDeclaredStoredProcedure(entityType, sprocType);
if (storedProcedure != null)
{
return storedProcedure;
@@ -112,12 +113,12 @@ public override bool IsReadOnly
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public static StoredProcedure? GetDeclaredStoredProcedure(
+ public static IStoredProcedure? FindDeclaredStoredProcedure(
IReadOnlyEntityType entityType,
StoreObjectType sprocType)
{
var sprocAnnotation = entityType.FindAnnotation(GetAnnotationName(sprocType));
- return sprocAnnotation != null ? (StoredProcedure?)sprocAnnotation.Value : null;
+ return sprocAnnotation != null ? (IStoredProcedure?)sprocAnnotation.Value : null;
}
///
@@ -130,13 +131,13 @@ public static StoredProcedure SetStoredProcedure(
IMutableEntityType entityType,
StoreObjectType sprocType)
{
- var oldId = GetDeclaredStoredProcedure(entityType, sprocType)?.CreateIdentifier();
+ var oldId = FindDeclaredStoredProcedure(entityType, sprocType)?.GetStoreIdentifier();
var sproc = new StoredProcedure(entityType, ConfigurationSource.Explicit);
entityType.SetAnnotation(GetAnnotationName(sprocType), sproc);
if (oldId != null)
{
- UpdateOverrides(oldId.Value, sproc.CreateIdentifier(), (IConventionEntityType)entityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)sproc).GetStoreIdentifier(), (IConventionEntityType)entityType);
}
return sproc;
@@ -154,14 +155,14 @@ public static StoredProcedure SetStoredProcedure(
string name,
string? schema)
{
- var oldId = GetDeclaredStoredProcedure(entityType, sprocType)?.CreateIdentifier();
+ var oldId = FindDeclaredStoredProcedure(entityType, sprocType)?.GetStoreIdentifier();
var sproc = new StoredProcedure(entityType, ConfigurationSource.Explicit);
entityType.SetAnnotation(GetAnnotationName(sprocType), sproc);
sproc.SetName(name, schema, ConfigurationSource.Explicit, skipOverrides: true);
if (oldId != null)
{
- UpdateOverrides(oldId.Value, sproc.CreateIdentifier(), (IConventionEntityType)entityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)sproc).GetStoreIdentifier(), (IConventionEntityType)entityType);
}
return sproc;
@@ -178,7 +179,7 @@ public static StoredProcedure SetStoredProcedure(
StoreObjectType sprocType,
bool fromDataAnnotation)
{
- var oldId = GetDeclaredStoredProcedure(entityType, sprocType)?.CreateIdentifier();
+ var oldId = FindDeclaredStoredProcedure(entityType, sprocType)?.GetStoreIdentifier();
var sproc = new StoredProcedure(
(IMutableEntityType)entityType,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
@@ -187,7 +188,7 @@ public static StoredProcedure SetStoredProcedure(
if (oldId != null
&& sproc != null)
{
- UpdateOverrides(oldId.Value, sproc.CreateIdentifier(), entityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)sproc).GetStoreIdentifier(), entityType);
}
return sproc;
@@ -206,7 +207,7 @@ public static StoredProcedure SetStoredProcedure(
string? schema,
bool fromDataAnnotation)
{
- var oldId = GetDeclaredStoredProcedure(entityType, sprocType)?.CreateIdentifier();
+ var oldId = FindDeclaredStoredProcedure(entityType, sprocType)?.GetStoreIdentifier();
var configurationSource = fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention;
var sproc = new StoredProcedure((IMutableEntityType)entityType, configurationSource);
sproc = (StoredProcedure?)entityType.SetAnnotation(GetAnnotationName(sprocType), sproc)?.Value;
@@ -216,7 +217,7 @@ public static StoredProcedure SetStoredProcedure(
if (oldId != null
&& sproc != null)
{
- UpdateOverrides(oldId.Value, sproc.CreateIdentifier(), entityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)sproc).GetStoreIdentifier(), entityType);
}
return sproc;
@@ -230,7 +231,7 @@ public static StoredProcedure SetStoredProcedure(
///
public static IMutableStoredProcedure? RemoveStoredProcedure(IMutableEntityType entityType, StoreObjectType sprocType)
{
- var oldId = GetDeclaredStoredProcedure(entityType, sprocType)?.CreateIdentifier();
+ var oldId = FindDeclaredStoredProcedure(entityType, sprocType)?.GetStoreIdentifier();
var sproc = (IMutableStoredProcedure?)entityType.RemoveAnnotation(GetAnnotationName(sprocType))?.Value;
if (oldId != null
@@ -250,7 +251,7 @@ public static StoredProcedure SetStoredProcedure(
///
public static IConventionStoredProcedure? RemoveStoredProcedure(IConventionEntityType entityType, StoreObjectType sprocType)
{
- var oldId = GetDeclaredStoredProcedure(entityType, sprocType)?.CreateIdentifier();
+ var oldId = FindDeclaredStoredProcedure(entityType, sprocType)?.GetStoreIdentifier();
var sproc = (IConventionStoredProcedure?)entityType.RemoveAnnotation(GetAnnotationName(sprocType))?.Value;
if (oldId != null
@@ -282,38 +283,6 @@ private static string GetAnnotationName(StoreObjectType sprocType)
_ => throw new InvalidOperationException("Unsopported sproc type " + sprocType)
};
- ///
- /// 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
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual StoreObjectIdentifier? CreateIdentifier()
- {
- var name = Name;
- if (name == null)
- {
- return null;
- }
-
- if (EntityType.GetInsertStoredProcedure() == this)
- {
- return StoreObjectIdentifier.InsertStoredProcedure(name, Schema);
- }
-
- if (EntityType.GetDeleteStoredProcedure() == this)
- {
- return StoreObjectIdentifier.DeleteStoredProcedure(name, Schema);
- }
-
- if (EntityType.GetUpdateStoredProcedure() == this)
- {
- return StoreObjectIdentifier.UpdateStoredProcedure(name, Schema);
- }
-
- return null;
- }
-
///
[DebuggerStepThrough]
public virtual ConfigurationSource GetConfigurationSource()
@@ -346,7 +315,7 @@ public virtual string? Schema
{
EnsureMutable();
- var oldId = CreateIdentifier();
+ var oldId = ((IReadOnlyStoredProcedure)this).GetStoreIdentifier();
_schema = schema;
@@ -354,7 +323,7 @@ public virtual string? Schema
if (oldId != null)
{
- UpdateOverrides(oldId.Value, CreateIdentifier(), (IConventionEntityType)EntityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)this).GetStoreIdentifier(), (IConventionEntityType)EntityType);
}
return schema;
@@ -378,6 +347,12 @@ public virtual string? Name
private string? GetDefaultName()
{
+ var tableName = EntityType.GetTableName() ?? EntityType.GetDefaultTableName();
+ if (tableName == null)
+ {
+ return null;
+ }
+
string? suffix;
if (EntityType.GetInsertStoredProcedure() == this)
{
@@ -396,12 +371,6 @@ public virtual string? Name
return null;
}
- var tableName = EntityType.GetDefaultTableName();
- if (tableName == null)
- {
- return null;
- }
-
return tableName + suffix;
}
@@ -415,7 +384,7 @@ public virtual string? Name
{
EnsureMutable();
- var oldId = CreateIdentifier();
+ var oldId = ((IReadOnlyStoredProcedure)this).GetStoreIdentifier();
_name = name;
@@ -425,7 +394,7 @@ public virtual string? Name
if (oldId != null)
{
- UpdateOverrides(oldId.Value, CreateIdentifier(), (IConventionEntityType)EntityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)this).GetStoreIdentifier(), (IConventionEntityType)EntityType);
}
return name;
@@ -441,7 +410,7 @@ public virtual void SetName(string? name, string? schema, ConfigurationSource co
{
EnsureMutable();
- var oldId = CreateIdentifier();
+ var oldId = ((IReadOnlyStoredProcedure)this).GetStoreIdentifier();
_name = name;
@@ -456,7 +425,7 @@ public virtual void SetName(string? name, string? schema, ConfigurationSource co
if (!skipOverrides
&& oldId != null)
{
- UpdateOverrides(oldId.Value, CreateIdentifier(), (IConventionEntityType)EntityType);
+ UpdateOverrides(oldId.Value, ((IReadOnlyStoredProcedure)this).GetStoreIdentifier(), (IConventionEntityType)EntityType);
}
}
@@ -568,15 +537,6 @@ public virtual bool AddResultColumn(string propertyName)
return false;
}
- /////
- ///// 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
- ///// any release. You should only use it directly in your code with extreme caution and knowing that
- ///// doing so can result in application failures when updating to a new Entity Framework Core release.
- /////
- //[DisallowNull]
- //public virtual IStoreFunction? StoreFunction { get; set; }
-
///
/// 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
@@ -633,9 +593,16 @@ IEntityType IStoredProcedure.EntityType
get => (IEntityType)EntityType;
}
- /////
- //IStoreFunction IDbFunction.StoreFunction
- // => StoreFunction!; // Relational model creation ensures StoreFunction is populated
+ ///
+ IStoreStoredProcedure IStoredProcedure.StoreStoredProcedure
+ => _storeStoredProcedure!; // Relational model creation ensures StoreStoredProcedure is populated
+
+ ///
+ IStoreStoredProcedure IRuntimeStoredProcedure.StoreStoredProcedure
+ {
+ get => _storeStoredProcedure!;
+ set => _storeStoredProcedure = value;
+ }
///
[DebuggerStepThrough]
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs
new file mode 100644
index 00000000000..edb4e5d6be6
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs
@@ -0,0 +1,118 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+// Sealed for perf
+public sealed class StoredProcedureComparer : IEqualityComparer, IComparer
+{
+ private StoredProcedureComparer()
+ {
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public static readonly StoredProcedureComparer Instance = new();
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public int Compare(IStoredProcedure? x, IStoredProcedure? y)
+ {
+ if (ReferenceEquals(x, y))
+ {
+ return 0;
+ }
+
+ if (x is null)
+ {
+ return -1;
+ }
+
+ if (y is null)
+ {
+ return 1;
+ }
+
+ var xId = x.GetStoreIdentifier();
+ var yId = y.GetStoreIdentifier();
+
+ var result = 0;
+ result = xId.StoreObjectType.CompareTo(yId.StoreObjectType);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ result = EntityTypeFullNameComparer.Instance.Compare(x.EntityType, y.EntityType);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ result = StringComparer.Ordinal.Compare(xId.Name, yId.Name);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ result = StringComparer.Ordinal.Compare(xId.Schema, yId.Schema);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ result = x.Parameters.Count().CompareTo(y.Parameters.Count());
+ if (result != 0)
+ {
+ return result;
+ }
+
+ result = x.Parameters.Zip(y.Parameters, (xc, yc) => StringComparer.Ordinal.Compare(xc, yc))
+ .FirstOrDefault(r => r != 0);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ return x.ResultColumns.Zip(y.ResultColumns, (xc, yc) => StringComparer.Ordinal.Compare(xc, yc))
+ .FirstOrDefault(r => r != 0);
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public bool Equals(IStoredProcedure? x, IStoredProcedure? y)
+ => ReferenceEquals(x, y)
+ || (x is not null
+ && y is not null
+ && x.EntityType == y.EntityType
+ && x.GetStoreIdentifier() == y.GetStoreIdentifier()
+ && x.Parameters.SequenceEqual(y.Parameters)
+ && x.ResultColumns.SequenceEqual(y.ResultColumns));
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public int GetHashCode(IStoredProcedure obj)
+ => obj.GetStoreIdentifier().GetHashCode();
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs
new file mode 100644
index 00000000000..f6f38ca14f7
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureMapping.cs
@@ -0,0 +1,91 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class StoredProcedureMapping : TableMappingBase, IStoredProcedureMapping
+{
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public StoredProcedureMapping(
+ IEntityType entityType,
+ StoreStoredProcedure storeStoredProcedure,
+ IStoredProcedure storedProcedure,
+ bool includesDerivedTypes)
+ : base(entityType, storeStoredProcedure, includesDerivedTypes)
+ {
+ StoredProcedure = storedProcedure;
+ StoredProcedureIdentifier = storedProcedure.GetStoreIdentifier();
+ }
+
+ ///
+ public virtual IStoreStoredProcedure StoreStoredProcedure
+ => (StoreStoredProcedure)base.Table;
+
+ ///
+ public virtual IStoredProcedure StoredProcedure { get; }
+
+ ///
+ public virtual StoreObjectIdentifier StoredProcedureIdentifier { 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual List ParameterMappings { get; }
+ = new();
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual bool AddParameterMapping(IStoredProcedureParameterMapping parameterMapping)
+ {
+ if (ParameterMappings.IndexOf(parameterMapping, ColumnMappingBaseComparer.Instance) != -1)
+ {
+ return false;
+ }
+
+ ParameterMappings.Add(parameterMapping);
+ ParameterMappings.Sort(ColumnMappingBaseComparer.Instance);
+
+ return true;
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoredProcedureMapping)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ IEnumerable IStoredProcedureMapping.ResultColumnMappings
+ {
+ [DebuggerStepThrough]
+ get => ColumnMappings;
+ }
+
+ ///
+ IEnumerable IStoredProcedureMapping.ParameterMappings
+ {
+ [DebuggerStepThrough]
+ get => ParameterMappings;
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs
new file mode 100644
index 00000000000..6c69bc8382c
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class StoredProcedureParameterMapping : ColumnMappingBase, IStoredProcedureParameterMapping
+{
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public StoredProcedureParameterMapping(
+ IProperty property,
+ StoreStoredProcedureParameter parameter,
+ StoredProcedureMapping storedProcedureMapping)
+ : base(property, parameter, storedProcedureMapping)
+ {
+ }
+
+ ///
+ public virtual IStoredProcedureMapping StoredProcedureMapping
+ => (IStoredProcedureMapping)TableMapping;
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override RelationalTypeMapping GetTypeMapping()
+ => Property.FindRelationalTypeMapping(StoredProcedureMapping.StoredProcedureIdentifier)!;
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoredProcedureParameterMapping)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ IStoreStoredProcedureParameter IStoredProcedureParameterMapping.Parameter
+ {
+ [DebuggerStepThrough]
+ get => (IStoreStoredProcedureParameter)Column;
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs
new file mode 100644
index 00000000000..c64a612185a
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class StoredProcedureResultColumnMapping : ColumnMappingBase, IStoredProcedureResultColumnMapping
+{
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public StoredProcedureResultColumnMapping(
+ IProperty property,
+ StoreStoredProcedureResultColumn column,
+ StoredProcedureMapping storedProcedureMapping)
+ : base(property, column, storedProcedureMapping)
+ {
+ }
+
+ ///
+ public virtual IStoredProcedureMapping StoredProcedureMapping
+ => (IStoredProcedureMapping)TableMapping;
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override RelationalTypeMapping GetTypeMapping()
+ => Property.FindRelationalTypeMapping(StoredProcedureMapping.StoredProcedureIdentifier)!;
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoredProcedureResultColumnMapping)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ IStoreStoredProcedureResultColumn IStoredProcedureResultColumnMapping.Column
+ {
+ [DebuggerStepThrough]
+ get => (IStoreStoredProcedureResultColumn)Column;
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs b/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
index c060f520d0c..e2e520f71e3 100644
--- a/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
+++ b/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
@@ -92,6 +92,11 @@ public static class RelationalAnnotationNames
///
public const string UpdateStoredProcedure = Prefix + "UpdateStoredProcedure";
+ ///
+ /// The name for mapped function name annotations.
+ ///
+ public const string ParameterDirection = Prefix + "ParameterDirection";
+
///
/// The name for mapped sql query annotations.
///
@@ -234,6 +239,46 @@ public static class RelationalAnnotationNames
///
public const string FunctionColumnMappings = Prefix + "FunctionColumnMappings";
+ ///
+ /// The name for insert stored procedure mappings annotations.
+ ///
+ public const string InsertStoredProcedureMappings = Prefix + "InsertStoredProcedureMappings";
+
+ ///
+ /// The name for insert stored procedure result column mappings annotations.
+ ///
+ public const string InsertStoredProcedureResultColumnMappings = Prefix + "InsertStoredProcedureResultColumnMappings";
+
+ ///
+ /// The name for insert stored procedure parameter mappings annotations.
+ ///
+ public const string InsertStoredProcedureParameterMappings = Prefix + "InsertStoredProcedureParameterMappings";
+
+ ///
+ /// The name for delete stored procedure mappings annotations.
+ ///
+ public const string DeleteStoredProcedureMappings = Prefix + "DeleteStoredProcedureMappings";
+
+ ///
+ /// The name for delete stored procedure parameter mappings annotations.
+ ///
+ public const string DeleteStoredProcedureParameterMappings = Prefix + "DeleteStoredProcedureParameterMappings";
+
+ ///
+ /// The name for update stored procedure mappings annotations.
+ ///
+ public const string UpdateStoredProcedureMappings = Prefix + "UpdateStoredProcedureMappings";
+
+ ///
+ /// The name for update stored procedure result column mappings annotations.
+ ///
+ public const string UpdateStoredProcedureResultColumnMappings = Prefix + "UpdateStoredProcedureResultColumnMappings";
+
+ ///
+ /// The name for update stored procedure parameter mappings annotations.
+ ///
+ public const string UpdateStoredProcedureParameterMappings = Prefix + "UpdateStoredProcedureParameterMappings";
+
///
/// The name for sql query mappings annotations.
///
diff --git a/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs b/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs
index 09c879111ad..fc7e95eab3e 100644
--- a/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs
+++ b/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs
@@ -69,6 +69,18 @@ public virtual IEnumerable For(IStoreFunction function, bool design
///
public virtual IEnumerable For(IFunctionColumn column, bool designTime)
=> Enumerable.Empty();
+
+ ///
+ public virtual IEnumerable For(IStoreStoredProcedure storedProcedure, bool designTime)
+ => Enumerable.Empty();
+
+ ///
+ public virtual IEnumerable For(IStoreStoredProcedureParameter parameter, bool designTime)
+ => Enumerable.Empty();
+
+ ///
+ public virtual IEnumerable For(IStoreStoredProcedureResultColumn column, bool designTime)
+ => Enumerable.Empty();
///
public virtual IEnumerable For(IForeignKeyConstraint foreignKey, bool designTime)
diff --git a/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs b/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs
new file mode 100644
index 00000000000..fcb5e845c37
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/RuntimeStoredProcedure.cs
@@ -0,0 +1,164 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata;
+
+///
+/// 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
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class RuntimeStoredProcedure : AnnotatableBase, IRuntimeStoredProcedure
+{
+ private readonly List _parameters = new();
+ private readonly List _resultColumns = new();
+ private readonly string? _schema;
+ private readonly string _name;
+ private readonly bool _areTransactionsSuppressed;
+ private IStoreStoredProcedure? _storeStoredProcedure;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The mapped entity type.
+ /// The name.
+ /// The schema.
+ /// Whether the automatic transactions are surpressed.
+ public RuntimeStoredProcedure(
+ RuntimeEntityType entityType,
+ string name,
+ string? schema,
+ bool areTransactionsSuppressed)
+ {
+ EntityType = entityType;
+ _name = name;
+ _schema = schema;
+ _areTransactionsSuppressed = areTransactionsSuppressed;
+ }
+
+ ///
+ /// Gets the entity type in which this stored procedure is defined.
+ ///
+ public virtual RuntimeEntityType EntityType { get; set; }
+
+ ///
+ /// Adds a new parameter mapped to the property with the given name.
+ ///
+ /// The name of the corresponding property.
+ public virtual void AddParameter(string propertyName)
+ {
+ _parameters.Add(propertyName);
+ }
+
+ ///
+ /// Adds a new column of the result for this stored procedure mapped to the property with the given name
+ ///
+ /// The name of the corresponding property.
+ public virtual void AddResultColumn(string propertyName)
+ {
+ _resultColumns.Add(propertyName);
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override string ToString()
+ => ((IStoredProcedure)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public virtual DebugView DebugView
+ => new(
+ () => ((IStoredProcedure)this).ToDebugString(),
+ () => ((IStoredProcedure)this).ToDebugString(MetadataDebugStringOptions.LongDefault));
+
+ ///
+ IReadOnlyEntityType IReadOnlyStoredProcedure.EntityType
+ {
+ [DebuggerStepThrough]
+ get => EntityType;
+ }
+
+ ///
+ IEntityType IStoredProcedure.EntityType
+ {
+ [DebuggerStepThrough]
+ get => EntityType;
+ }
+
+ ///
+ string? IReadOnlyStoredProcedure.Name
+ {
+ [DebuggerStepThrough]
+ get => _name;
+ }
+
+ ///
+ string IStoredProcedure.Name
+ {
+ [DebuggerStepThrough]
+ get => _name;
+ }
+
+ ///
+ string? IReadOnlyStoredProcedure.Schema
+ {
+ [DebuggerStepThrough]
+ get => _schema;
+ }
+
+ ///
+ bool IReadOnlyStoredProcedure.AreTransactionsSuppressed
+ {
+ [DebuggerStepThrough]
+ get => _areTransactionsSuppressed;
+ }
+
+ ///
+ IReadOnlyList IReadOnlyStoredProcedure.Parameters
+ {
+ [DebuggerStepThrough]
+ get => _parameters;
+ }
+
+ ///
+ bool IReadOnlyStoredProcedure.ContainsParameter(string propertyName)
+ => _parameters.Contains(propertyName);
+
+ ///
+ IReadOnlyList IReadOnlyStoredProcedure.ResultColumns
+ {
+ [DebuggerStepThrough]
+ get => _resultColumns;
+ }
+
+ ///
+ bool IReadOnlyStoredProcedure.ContainsResultColumn(string propertyName)
+ => _resultColumns.Contains(propertyName);
+
+
+ ///
+ IStoreStoredProcedure IStoredProcedure.StoreStoredProcedure
+ {
+ [DebuggerStepThrough]
+ get => _storeStoredProcedure!;
+ }
+
+ ///
+ IStoreStoredProcedure IRuntimeStoredProcedure.StoreStoredProcedure
+ {
+ get => _storeStoredProcedure!;
+ set => _storeStoredProcedure = value;
+ }
+}
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index eed94e6bbd5..6b5756384b1 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -1230,13 +1230,21 @@ public static string SqlQueryOverrideMismatch(object? propertySpecification, obj
propertySpecification, query);
///
- /// The non-key property '{entityType}.{property}' is mapped to a parameter of the stored procedure '{sproc}', but only key properties are supported for Delete stored procedures.
+ /// The property '{entityType}.{property}' is mapped to a parameter of the stored procedure '{sproc}', but only concurrency token and key properties are supported for Delete stored procedures.
///
public static string StoredProcedureDeleteNonKeyProperty(object? entityType, object? property, object? sproc)
=> string.Format(
GetString("StoredProcedureDeleteNonKeyProperty", nameof(entityType), nameof(property), nameof(sproc)),
entityType, property, sproc);
+ ///
+ /// The entity type '{entityType}' is mapped to the stored procedure '{sproc}', however the store-generated properties {properties} are not mapped to any output parameter or result column.
+ ///
+ public static string StoredProcedureGeneratedPropertiesNotMapped(object? entityType, object? sproc, object? properties)
+ => string.Format(
+ GetString("StoredProcedureGeneratedPropertiesNotMapped", nameof(entityType), nameof(sproc), nameof(properties)),
+ entityType, sproc, properties);
+
///
/// The keyless entity type '{entityType}' was configured to use '{sproc}'. An entity type requires a primary key to be able to be mapped to a stored procedure.
///
@@ -1253,6 +1261,14 @@ public static string StoredProcedureNoName(object? entityType, object? sproc)
GetString("StoredProcedureNoName", nameof(entityType), nameof(sproc)),
entityType, sproc);
+ ///
+ /// The property '{entityType}.{property}' is mapped to an output parameter of the stored procedure '{sproc}', but it is not configured as store-generated. Either configure it as store-generated or don't configure the parameter as output.
+ ///
+ public static string StoredProcedureOutputParameterNotGenerated(object? entityType, object? property, object? sproc)
+ => string.Format(
+ GetString("StoredProcedureOutputParameterNotGenerated", nameof(entityType), nameof(property), nameof(sproc)),
+ entityType, property, sproc);
+
///
/// The property '{propertySpecification}' has specific configuration for the stored procedure '{sproc}', but it isn't mapped to a parameter or a result column on that stored procedure. Remove the specific configuration, or map an entity type that contains this property to '{sproc}'.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index 5a38d8f7d7a..e804bc3e0f0 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -862,7 +862,10 @@
The property '{propertySpecification}' has specific configuration for the SQL query '{query}', but isn't mapped to a column on that query. Remove the specific configuration, or map an entity type that contains this property to '{query}'.
- The non-key property '{entityType}.{property}' is mapped to a parameter of the stored procedure '{sproc}', but only key properties are supported for Delete stored procedures.
+ The property '{entityType}.{property}' is mapped to a parameter of the stored procedure '{sproc}', but only concurrency token and key properties are supported for Delete stored procedures.
+
+
+ The entity type '{entityType}' is mapped to the stored procedure '{sproc}', however the store-generated properties {properties} are not mapped to any output parameter or result column.
The keyless entity type '{entityType}' was configured to use '{sproc}'. An entity type requires a primary key to be able to be mapped to a stored procedure.
@@ -870,6 +873,9 @@
The entity type '{entityType}' was configured to use '{sproc}', but the store name was not specified. Configure the stored procedure name explicitly.
+
+ The property '{entityType}.{property}' is mapped to an output parameter of the stored procedure '{sproc}', but it is not configured as store-generated. Either configure it as store-generated or don't configure the parameter as output.
+
The property '{propertySpecification}' has specific configuration for the stored procedure '{sproc}', but it isn't mapped to a parameter or a result column on that stored procedure. Remove the specific configuration, or map an entity type that contains this property to '{sproc}'.
diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
index ec60455e0e6..d731eb391d6 100644
--- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
+++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
@@ -846,6 +846,11 @@ private static bool IsCompatibleWithValueGeneration(
in StoreObjectIdentifier storeObject,
ITypeMappingSource? typeMappingSource)
{
+ if (storeObject.StoreObjectType != StoreObjectType.Table)
+ {
+ return false;
+ }
+
var valueConverter = property.GetValueConverter()
?? (property.FindRelationalTypeMapping(storeObject)
?? typeMappingSource?.FindMapping((IProperty)property))?.Converter;
diff --git a/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs b/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs
index 93daf7dd161..ec66c77eb03 100644
--- a/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs
+++ b/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs
@@ -72,13 +72,13 @@ public virtual void ProcessEntityTypeBaseTypeChanged(
if (newBaseType.BaseType == null)
{
- discriminator?.HasValue(newBaseType, newBaseType.ShortName());
+ discriminator?.HasValue(newBaseType, newBaseType.GetDefaultDiscriminatorValue());
}
}
if (discriminator != null)
{
- discriminator.HasValue(entityTypeBuilder.Metadata, entityTypeBuilder.Metadata.ShortName());
+ discriminator.HasValue(entityTypeBuilder.Metadata, entityTypeBuilder.Metadata.GetDefaultDiscriminatorValue());
SetDefaultDiscriminatorValues(derivedEntityTypes, discriminator);
}
}
@@ -115,7 +115,7 @@ protected virtual void SetDefaultDiscriminatorValues(
{
foreach (var entityType in entityTypes)
{
- discriminatorBuilder.HasValue(entityType, entityType.ShortName());
+ discriminatorBuilder.HasValue(entityType, entityType.GetDefaultDiscriminatorValue());
}
}
}
diff --git a/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs b/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs
index 58bdf89a38e..98f904eea22 100644
--- a/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs
+++ b/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs
@@ -119,8 +119,10 @@ protected virtual string GenerateJoinTypeName(IConventionSkipNavigation skipNavi
var declaringEntityType = skipNavigation.DeclaringEntityType;
var inverseEntityType = inverseSkipNavigation.DeclaringEntityType;
var model = declaringEntityType.Model;
- var joinEntityTypeName = declaringEntityType.ShortName();
- var inverseName = inverseEntityType.ShortName();
+ var joinEntityTypeName = !declaringEntityType.HasSharedClrType
+ ? declaringEntityType.ClrType.ShortDisplayName() : declaringEntityType.ShortName();
+ var inverseName = !inverseEntityType.HasSharedClrType
+ ? inverseEntityType.ClrType.ShortDisplayName() : inverseEntityType.ShortName();
joinEntityTypeName = StringComparer.Ordinal.Compare(joinEntityTypeName, inverseName) < 0
? joinEntityTypeName + inverseName
: inverseName + joinEntityTypeName;
diff --git a/src/EFCore/Metadata/IReadOnlyEntityType.cs b/src/EFCore/Metadata/IReadOnlyEntityType.cs
index cd07f87b88b..43c89b6e3f2 100644
--- a/src/EFCore/Metadata/IReadOnlyEntityType.cs
+++ b/src/EFCore/Metadata/IReadOnlyEntityType.cs
@@ -77,8 +77,15 @@ bool GetIsDiscriminatorMappingComplete()
: !ClrType.IsInstantiable()
|| (BaseType == null && GetDirectlyDerivedTypes().Count() == 0)
? null
- : (object)ShortName();
+ : (object?)GetDefaultDiscriminatorValue();
}
+
+ ///
+ /// Returns the default discriminator value that would be used for this entity type.
+ ///
+ /// The default discriminator value for this entity type.
+ string GetDefaultDiscriminatorValue()
+ => !HasSharedClrType ? ClrType.ShortDisplayName() : ShortName();
///
/// Gets all types in the model from which a given entity type derives, starting with the root.
diff --git a/src/EFCore/Metadata/IReadOnlyTypeBase.cs b/src/EFCore/Metadata/IReadOnlyTypeBase.cs
index ec411b10016..f60e522cfad 100644
--- a/src/EFCore/Metadata/IReadOnlyTypeBase.cs
+++ b/src/EFCore/Metadata/IReadOnlyTypeBase.cs
@@ -113,7 +113,14 @@ string ShortName()
{
if (!HasSharedClrType)
{
- return ClrType.ShortDisplayName();
+ var name = ClrType.ShortDisplayName();
+ var lessIndex = name.IndexOf("<", StringComparison.Ordinal);
+ if (lessIndex == -1)
+ {
+ return name;
+ }
+
+ return name[..lessIndex];
}
var hashIndex = Name.LastIndexOf("#", StringComparison.Ordinal);
diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
index af84e2eb2f5..f65ae0b71ed 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
@@ -49,10 +49,18 @@ public void Test_new_annotations_handled_for_entity_types()
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.SqlQueryColumnMappings,
RelationalAnnotationNames.FunctionColumnMappings,
+ RelationalAnnotationNames.InsertStoredProcedureParameterMappings,
+ RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings,
+ RelationalAnnotationNames.DeleteStoredProcedureParameterMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureParameterMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings,
RelationalAnnotationNames.DefaultColumnMappings,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.ViewMappings,
RelationalAnnotationNames.FunctionMappings,
+ RelationalAnnotationNames.InsertStoredProcedureMappings,
+ RelationalAnnotationNames.DeleteStoredProcedureMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureMappings,
RelationalAnnotationNames.SqlQueryMappings,
RelationalAnnotationNames.DefaultMappings,
RelationalAnnotationNames.ForeignKeyMappings,
@@ -75,12 +83,12 @@ public void Test_new_annotations_handled_for_entity_types()
RelationalAnnotationNames.IsFixedLength,
RelationalAnnotationNames.Collation,
RelationalAnnotationNames.IsStored,
+ RelationalAnnotationNames.ParameterDirection,
RelationalAnnotationNames.TpcMappingStrategy,
RelationalAnnotationNames.TphMappingStrategy,
RelationalAnnotationNames.TptMappingStrategy,
RelationalAnnotationNames.RelationalModel,
RelationalAnnotationNames.ModelDependencies,
- RelationalAnnotationNames.Triggers, // Appears on entity but requires provider-specific support
RelationalAnnotationNames.FieldValueGetter
};
@@ -199,10 +207,18 @@ public void Test_new_annotations_handled_for_properties()
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.SqlQueryColumnMappings,
RelationalAnnotationNames.FunctionColumnMappings,
+ RelationalAnnotationNames.InsertStoredProcedureParameterMappings,
+ RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings,
+ RelationalAnnotationNames.DeleteStoredProcedureParameterMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureParameterMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings,
RelationalAnnotationNames.DefaultColumnMappings,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.ViewMappings,
RelationalAnnotationNames.FunctionMappings,
+ RelationalAnnotationNames.InsertStoredProcedureMappings,
+ RelationalAnnotationNames.DeleteStoredProcedureMappings,
+ RelationalAnnotationNames.UpdateStoredProcedureMappings,
RelationalAnnotationNames.SqlQueryMappings,
RelationalAnnotationNames.ForeignKeyMappings,
RelationalAnnotationNames.TableIndexMappings,
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
index eb9e5cc0938..3aa528f3320 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
@@ -905,33 +905,33 @@ public partial class BigContextModel
{
partial void Initialize()
{
- var dependentBasebyte = DependentBasebyteEntityType.Create(this);
+ var dependentBase = DependentBaseEntityType.Create(this);
var principalBase = PrincipalBaseEntityType.Create(this);
var ownedType = OwnedTypeEntityType.Create(this);
var ownedType0 = OwnedType0EntityType.Create(this);
var principalBasePrincipalDerivedDependentBasebyte = PrincipalBasePrincipalDerivedDependentBasebyteEntityType.Create(this);
- var dependentDerivedbyte = DependentDerivedbyteEntityType.Create(this, dependentBasebyte);
- var principalDerivedDependentBasebyte = PrincipalDerivedDependentBasebyteEntityType.Create(this, principalBase);
+ var dependentDerived = DependentDerivedEntityType.Create(this, dependentBase);
+ var principalDerived = PrincipalDerivedEntityType.Create(this, principalBase);
- DependentBasebyteEntityType.CreateForeignKey1(dependentBasebyte, principalBase);
- DependentBasebyteEntityType.CreateForeignKey2(dependentBasebyte, principalDerivedDependentBasebyte);
+ DependentBaseEntityType.CreateForeignKey1(dependentBase, principalBase);
+ DependentBaseEntityType.CreateForeignKey2(dependentBase, principalDerived);
OwnedTypeEntityType.CreateForeignKey1(ownedType, principalBase);
OwnedTypeEntityType.CreateForeignKey2(ownedType, ownedType);
- OwnedType0EntityType.CreateForeignKey1(ownedType0, principalDerivedDependentBasebyte);
- PrincipalBasePrincipalDerivedDependentBasebyteEntityType.CreateForeignKey1(principalBasePrincipalDerivedDependentBasebyte, principalDerivedDependentBasebyte);
+ OwnedType0EntityType.CreateForeignKey1(ownedType0, principalDerived);
+ PrincipalBasePrincipalDerivedDependentBasebyteEntityType.CreateForeignKey1(principalBasePrincipalDerivedDependentBasebyte, principalDerived);
PrincipalBasePrincipalDerivedDependentBasebyteEntityType.CreateForeignKey2(principalBasePrincipalDerivedDependentBasebyte, principalBase);
- PrincipalDerivedDependentBasebyteEntityType.CreateForeignKey1(principalDerivedDependentBasebyte, principalBase);
+ PrincipalDerivedEntityType.CreateForeignKey1(principalDerived, principalBase);
- PrincipalBaseEntityType.CreateSkipNavigation1(principalBase, principalDerivedDependentBasebyte, principalBasePrincipalDerivedDependentBasebyte);
- PrincipalDerivedDependentBasebyteEntityType.CreateSkipNavigation1(principalDerivedDependentBasebyte, principalBase, principalBasePrincipalDerivedDependentBasebyte);
+ PrincipalBaseEntityType.CreateSkipNavigation1(principalBase, principalDerived, principalBasePrincipalDerivedDependentBasebyte);
+ PrincipalDerivedEntityType.CreateSkipNavigation1(principalDerived, principalBase, principalBasePrincipalDerivedDependentBasebyte);
- DependentBasebyteEntityType.CreateAnnotations(dependentBasebyte);
+ DependentBaseEntityType.CreateAnnotations(dependentBase);
PrincipalBaseEntityType.CreateAnnotations(principalBase);
OwnedTypeEntityType.CreateAnnotations(ownedType);
OwnedType0EntityType.CreateAnnotations(ownedType0);
PrincipalBasePrincipalDerivedDependentBasebyteEntityType.CreateAnnotations(principalBasePrincipalDerivedDependentBasebyte);
- DependentDerivedbyteEntityType.CreateAnnotations(dependentDerivedbyte);
- PrincipalDerivedDependentBasebyteEntityType.CreateAnnotations(principalDerivedDependentBasebyte);
+ DependentDerivedEntityType.CreateAnnotations(dependentDerived);
+ PrincipalDerivedEntityType.CreateAnnotations(principalDerived);
AddAnnotation(""Relational:MaxIdentifierLength"", 128);
AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);
@@ -940,7 +940,7 @@ partial void Initialize()
}
", c),
c => AssertFileContents(
- "DependentBasebyteEntityType.cs", @"//
+ "DependentBaseEntityType.cs", @"//
using System;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
@@ -955,7 +955,7 @@ partial void Initialize()
namespace TestNamespace
{
- internal partial class DependentBasebyteEntityType
+ internal partial class DependentBaseEntityType
{
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? baseEntityType = null)
{
@@ -975,7 +975,6 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
var principalAlternateId = runtimeEntityType.AddProperty(
""PrincipalAlternateId"",
typeof(Point),
- valueGenerated: ValueGenerated.OnAdd,
afterSaveBehavior: PropertySaveBehavior.Throw);
principalAlternateId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
@@ -1051,7 +1050,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
runtimeEntityType.AddAnnotation(""Relational:MappingStrategy"", ""TPH"");
runtimeEntityType.AddAnnotation(""Relational:Schema"", null);
runtimeEntityType.AddAnnotation(""Relational:SqlQuery"", null);
- runtimeEntityType.AddAnnotation(""Relational:TableName"", ""PrincipalDerived"");
+ runtimeEntityType.AddAnnotation(""Relational:TableName"", ""DependentBase"");
runtimeEntityType.AddAnnotation(""Relational:ViewName"", null);
runtimeEntityType.AddAnnotation(""Relational:ViewSchema"", null);
@@ -1095,6 +1094,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField(""k__BackingField"", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
valueGenerated: ValueGenerated.OnAdd,
afterSaveBehavior: PropertySaveBehavior.Throw);
+
var overrides = new StoreObjectDictionary();
var idPrincipalDerived = new RuntimeRelationalPropertyOverrides(
id,
@@ -1103,6 +1103,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
""DerivedId"");
overrides.Add(StoreObjectIdentifier.Table(""PrincipalDerived"", null), idPrincipalDerived);
id.AddAnnotation(""Relational:RelationalOverrides"", overrides);
+
id.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);
var alternateId = runtimeEntityType.AddProperty(
@@ -1212,6 +1213,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
propertyAccessMode: PropertyAccessMode.Field,
valueGenerated: ValueGenerated.OnAdd,
afterSaveBehavior: PropertySaveBehavior.Throw);
+
var overrides = new StoreObjectDictionary();
var principalBaseIdPrincipalBase = new RuntimeRelationalPropertyOverrides(
principalBaseId,
@@ -1221,6 +1223,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
principalBaseIdPrincipalBase.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);
overrides.Add(StoreObjectIdentifier.Table(""PrincipalBase"", ""mySchema""), principalBaseIdPrincipalBase);
principalBaseId.AddAnnotation(""Relational:RelationalOverrides"", overrides);
+
principalBaseId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);
var principalBaseAlternateId = runtimeEntityType.AddProperty(
@@ -1238,6 +1241,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetField(""k__BackingField"", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
propertyAccessMode: PropertyAccessMode.Field,
nullable: true);
+
var overrides0 = new StoreObjectDictionary();
var detailsDetails = new RuntimeRelationalPropertyOverrides(
details,
@@ -1246,6 +1250,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
null);
overrides0.Add(StoreObjectIdentifier.Table(""Details"", null), detailsDetails);
details.AddAnnotation(""Relational:RelationalOverrides"", overrides0);
+
details.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
var number = runtimeEntityType.AddProperty(
@@ -1351,17 +1356,17 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
baseEntityType,
sharedClrType: true);
- var principalDerivedDependentBasebyteId = runtimeEntityType.AddProperty(
- ""PrincipalDerived>Id"",
+ var principalDerivedId = runtimeEntityType.AddProperty(
+ ""PrincipalDerivedId"",
typeof(long),
afterSaveBehavior: PropertySaveBehavior.Throw);
- principalDerivedDependentBasebyteId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
+ principalDerivedId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
- var principalDerivedDependentBasebyteAlternateId = runtimeEntityType.AddProperty(
- ""PrincipalDerived>AlternateId"",
+ var principalDerivedAlternateId = runtimeEntityType.AddProperty(
+ ""PrincipalDerivedAlternateId"",
typeof(Point),
afterSaveBehavior: PropertySaveBehavior.Throw);
- principalDerivedDependentBasebyteAlternateId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
+ principalDerivedAlternateId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
var id = runtimeEntityType.AddProperty(
""Id"",
@@ -1390,7 +1395,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
propertyInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.OwnedType).GetProperty(""Context"", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
var key = runtimeEntityType.AddKey(
- new[] { principalDerivedDependentBasebyteId, principalDerivedDependentBasebyteAlternateId, id });
+ new[] { principalDerivedId, principalDerivedAlternateId, id });
runtimeEntityType.SetPrimaryKey(key);
return runtimeEntityType;
@@ -1398,7 +1403,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType)
{
- var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty(""PrincipalDerived>Id"")!, declaringEntityType.FindProperty(""PrincipalDerived>AlternateId"")! },
+ var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty(""PrincipalDerivedId"")!, declaringEntityType.FindProperty(""PrincipalDerivedAlternateId"")! },
principalEntityType.FindKey(new[] { principalEntityType.FindProperty(""Id"")!, principalEntityType.FindProperty(""AlternateId"")! })!,
principalEntityType,
deleteBehavior: DeleteBehavior.Cascade,
@@ -1546,7 +1551,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
}
", c),
c => AssertFileContents(
- "DependentDerivedbyteEntityType.cs", @"//
+ "DependentDerivedEntityType.cs", @"//
using System;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -1558,7 +1563,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
namespace TestNamespace
{
- internal partial class DependentDerivedbyteEntityType
+ internal partial class DependentDerivedEntityType
{
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? baseEntityType = null)
{
@@ -1595,7 +1600,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
runtimeEntityType.AddAnnotation(""Relational:FunctionName"", null);
runtimeEntityType.AddAnnotation(""Relational:Schema"", null);
runtimeEntityType.AddAnnotation(""Relational:SqlQuery"", null);
- runtimeEntityType.AddAnnotation(""Relational:TableName"", ""PrincipalDerived"");
+ runtimeEntityType.AddAnnotation(""Relational:TableName"", ""DependentBase"");
runtimeEntityType.AddAnnotation(""Relational:ViewName"", null);
runtimeEntityType.AddAnnotation(""Relational:ViewSchema"", null);
@@ -1607,7 +1612,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
}
", c),
c => AssertFileContents(
- "PrincipalDerivedDependentBasebyteEntityType.cs", @"//
+ "PrincipalDerivedEntityType.cs", @"//
using System;
using System.Collections.Generic;
using System.Reflection;
@@ -1620,7 +1625,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
namespace TestNamespace
{
- internal partial class PrincipalDerivedDependentBasebyteEntityType
+ internal partial class PrincipalDerivedEntityType
{
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? baseEntityType = null)
{
@@ -2279,8 +2284,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.HasForeignKey>("PrincipalId")
.HasPrincipalKey(e => e.Id);
- eb.ToTable("PrincipalDerived");
-
eb.HasDiscriminator("EnumDiscriminator")
.HasValue(Enum1.One)
.HasValue>(Enum1.Two)
@@ -2357,17 +2360,17 @@ public partial class TpcContextModel
{
partial void Initialize()
{
- var dependentBasebyte = DependentBasebyteEntityType.Create(this);
+ var dependentBase = DependentBaseEntityType.Create(this);
var principalBase = PrincipalBaseEntityType.Create(this);
- var principalDerivedDependentBasebyte = PrincipalDerivedDependentBasebyteEntityType.Create(this, principalBase);
+ var principalDerived = PrincipalDerivedEntityType.Create(this, principalBase);
- DependentBasebyteEntityType.CreateForeignKey1(dependentBasebyte, principalDerivedDependentBasebyte);
+ DependentBaseEntityType.CreateForeignKey1(dependentBase, principalDerived);
PrincipalBaseEntityType.CreateForeignKey1(principalBase, principalBase);
- PrincipalBaseEntityType.CreateForeignKey2(principalBase, principalDerivedDependentBasebyte);
+ PrincipalBaseEntityType.CreateForeignKey2(principalBase, principalDerived);
- DependentBasebyteEntityType.CreateAnnotations(dependentBasebyte);
+ DependentBaseEntityType.CreateAnnotations(dependentBase);
PrincipalBaseEntityType.CreateAnnotations(principalBase);
- PrincipalDerivedDependentBasebyteEntityType.CreateAnnotations(principalDerivedDependentBasebyte);
+ PrincipalDerivedEntityType.CreateAnnotations(principalDerived);
AddAnnotation(""Relational:DefaultSchema"", ""TPC"");
AddAnnotation(""Relational:MaxIdentifierLength"", 128);
@@ -2377,7 +2380,7 @@ partial void Initialize()
}
", c),
c => AssertFileContents(
- "DependentBasebyteEntityType.cs", @"//
+ "DependentBaseEntityType.cs", @"//
using System;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
@@ -2389,7 +2392,7 @@ partial void Initialize()
namespace TestNamespace
{
- internal partial class DependentBasebyteEntityType
+ internal partial class DependentBaseEntityType
{
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? baseEntityType = null)
{
@@ -2469,6 +2472,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
"PrincipalBaseEntityType.cs", @"//
using System;
using System.Collections.Generic;
+using System.Data;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
@@ -2495,6 +2499,32 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
fieldInfo: typeof(CSharpRuntimeModelCodeGeneratorTest.PrincipalBase).GetField(""k__BackingField"", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
valueGenerated: ValueGenerated.OnAdd,
afterSaveBehavior: PropertySaveBehavior.Throw);
+
+ var overrides = new StoreObjectDictionary();
+ var idDerivedInsert = new RuntimeRelationalPropertyOverrides(
+ id,
+ StoreObjectIdentifier.InsertStoredProcedure(""Derived_Insert"", ""TPC""),
+ true,
+ ""DerivedId"");
+ idDerivedInsert.AddAnnotation(""foo"", ""bar3"");
+ overrides.Add(StoreObjectIdentifier.InsertStoredProcedure(""Derived_Insert"", ""TPC""), idDerivedInsert);
+ var idPrincipalBaseView = new RuntimeRelationalPropertyOverrides(
+ id,
+ StoreObjectIdentifier.View(""PrincipalBaseView"", ""TPC""),
+ false,
+ null);
+ idPrincipalBaseView.AddAnnotation(""foo"", ""bar2"");
+ overrides.Add(StoreObjectIdentifier.View(""PrincipalBaseView"", ""TPC""), idPrincipalBaseView);
+ var idPrincipalBaseInsert = new RuntimeRelationalPropertyOverrides(
+ id,
+ StoreObjectIdentifier.InsertStoredProcedure(""PrincipalBase_Insert"", ""TPC""),
+ true,
+ ""BaseId"");
+ idPrincipalBaseInsert.AddAnnotation(""foo"", ""bar"");
+ idPrincipalBaseInsert.AddAnnotation(""Relational:ParameterDirection"", ParameterDirection.Output);
+ overrides.Add(StoreObjectIdentifier.InsertStoredProcedure(""PrincipalBase_Insert"", ""TPC""), idPrincipalBaseInsert);
+ id.AddAnnotation(""Relational:RelationalOverrides"", overrides);
+
id.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);
var principalBaseId = runtimeEntityType.AddProperty(
@@ -2503,11 +2533,11 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
nullable: true);
principalBaseId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
- var principalDerivedDependentBasebyteId = runtimeEntityType.AddProperty(
- ""PrincipalDerived>Id"",
+ var principalDerivedId = runtimeEntityType.AddProperty(
+ ""PrincipalDerivedId"",
typeof(long?),
nullable: true);
- principalDerivedDependentBasebyteId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
+ principalDerivedId.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.None);
var key = runtimeEntityType.AddKey(
new[] { id });
@@ -2517,7 +2547,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
new[] { principalBaseId });
var index0 = runtimeEntityType.AddIndex(
- new[] { principalDerivedDependentBasebyteId });
+ new[] { principalDerivedId });
return runtimeEntityType;
}
@@ -2540,7 +2570,7 @@ public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEnt
public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType)
{
- var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty(""PrincipalDerived>Id"")! },
+ var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty(""PrincipalDerivedId"")! },
principalEntityType.FindKey(new[] { principalEntityType.FindProperty(""Id"")! })!,
principalEntityType);
@@ -2556,6 +2586,38 @@ public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEnt
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
{
+ var insertSproc = new RuntimeStoredProcedure(
+ runtimeEntityType,
+ ""PrincipalBase_Insert"",
+ ""TPC"",
+ true);
+
+ insertSproc.AddParameter(""PrincipalBaseId"");
+ insertSproc.AddParameter(""PrincipalDerivedId"");
+ insertSproc.AddParameter(""Id"");
+ insertSproc.AddAnnotation(""foo"", ""bar1"");
+ runtimeEntityType.AddAnnotation(""Relational:InsertStoredProcedure"", insertSproc);
+
+ var deleteSproc = new RuntimeStoredProcedure(
+ runtimeEntityType,
+ ""PrincipalBase_Delete"",
+ ""TPC"",
+ false);
+
+ deleteSproc.AddParameter(""Id"");
+ runtimeEntityType.AddAnnotation(""Relational:DeleteStoredProcedure"", deleteSproc);
+
+ var updateSproc = new RuntimeStoredProcedure(
+ runtimeEntityType,
+ ""PrincipalBase_Update"",
+ ""TPC"",
+ false);
+
+ updateSproc.AddParameter(""PrincipalBaseId"");
+ updateSproc.AddParameter(""PrincipalDerivedId"");
+ updateSproc.AddParameter(""Id"");
+ runtimeEntityType.AddAnnotation(""Relational:UpdateStoredProcedure"", updateSproc);
+
runtimeEntityType.AddAnnotation(""Relational:FunctionName"", null);
runtimeEntityType.AddAnnotation(""Relational:MappingStrategy"", ""TPC"");
runtimeEntityType.AddAnnotation(""Relational:Schema"", ""TPC"");
@@ -2573,7 +2635,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
}
", c),
c => AssertFileContents(
- "PrincipalDerivedDependentBasebyteEntityType.cs", @"//
+ "PrincipalDerivedEntityType.cs", @"//
using System;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -2584,7 +2646,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
namespace TestNamespace
{
- internal partial class PrincipalDerivedDependentBasebyteEntityType
+ internal partial class PrincipalDerivedEntityType
{
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? baseEntityType = null)
{
@@ -2599,6 +2661,37 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType? ba
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
{
+ var insertSproc = new RuntimeStoredProcedure(
+ runtimeEntityType,
+ ""Derived_Insert"",
+ ""TPC"",
+ false);
+
+ insertSproc.AddParameter(""PrincipalBaseId"");
+ insertSproc.AddParameter(""PrincipalDerivedId"");
+ insertSproc.AddResultColumn(""Id"");
+ runtimeEntityType.AddAnnotation(""Relational:InsertStoredProcedure"", insertSproc);
+
+ var deleteSproc = new RuntimeStoredProcedure(
+ runtimeEntityType,
+ ""Derived_Delete"",
+ ""TPC"",
+ false);
+
+ deleteSproc.AddParameter(""Id"");
+ runtimeEntityType.AddAnnotation(""Relational:DeleteStoredProcedure"", deleteSproc);
+
+ var updateSproc = new RuntimeStoredProcedure(
+ runtimeEntityType,
+ ""Derived_Update"",
+ ""Derived"",
+ false);
+
+ updateSproc.AddParameter(""PrincipalBaseId"");
+ updateSproc.AddParameter(""PrincipalDerivedId"");
+ updateSproc.AddParameter(""Id"");
+ runtimeEntityType.AddAnnotation(""Relational:UpdateStoredProcedure"", updateSproc);
+
runtimeEntityType.AddAnnotation(""Relational:FunctionName"", null);
runtimeEntityType.AddAnnotation(""Relational:Schema"", ""TPC"");
runtimeEntityType.AddAnnotation(""Relational:SqlQuery"", null);
@@ -2619,10 +2712,51 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
Assert.Equal("TPC", model.GetDefaultSchema());
var principalBase = model.FindEntityType(typeof(PrincipalBase));
+ var id = principalBase.FindProperty("Id");
+
+ Assert.Equal("Id", id.GetColumnName());
Assert.Equal("PrincipalBase", principalBase.GetTableName());
Assert.Equal("TPC", principalBase.GetSchema());
+ Assert.Equal("Id", id.GetColumnName(StoreObjectIdentifier.Create(principalBase, StoreObjectType.Table).Value));
+ Assert.Null(id.FindOverrides(StoreObjectIdentifier.Create(principalBase, StoreObjectType.Table).Value));
+
Assert.Equal("PrincipalBaseView", principalBase.GetViewName());
Assert.Equal("TPC", principalBase.GetViewSchema());
+ Assert.Equal("Id", id.GetColumnName(StoreObjectIdentifier.Create(principalBase, StoreObjectType.View).Value));
+ Assert.Equal("bar2",
+ id.FindOverrides(StoreObjectIdentifier.Create(principalBase, StoreObjectType.View).Value)["foo"]);
+
+ var insertSproc = principalBase.GetInsertStoredProcedure()!;
+ Assert.Equal("PrincipalBase_Insert", insertSproc.Name);
+ Assert.Equal("TPC", insertSproc.Schema);
+ Assert.Equal(new[] { "PrincipalBaseId", "PrincipalDerivedId", "Id" }, insertSproc.Parameters);
+ Assert.Empty(insertSproc.ResultColumns);
+ Assert.True(insertSproc.AreTransactionsSuppressed);
+ Assert.Equal("bar1", insertSproc["foo"]);
+ Assert.Same(principalBase, insertSproc.EntityType);
+ Assert.Equal("BaseId", id.GetColumnName(StoreObjectIdentifier.Create(principalBase, StoreObjectType.InsertStoredProcedure).Value));
+ Assert.Equal("bar",
+ id.FindOverrides(StoreObjectIdentifier.Create(principalBase, StoreObjectType.InsertStoredProcedure).Value)["foo"]);
+
+ var updateSproc = principalBase.GetUpdateStoredProcedure()!;
+ Assert.Equal("PrincipalBase_Update", updateSproc.Name);
+ Assert.Equal("TPC", updateSproc.Schema);
+ Assert.Equal(new[] { "PrincipalBaseId", "PrincipalDerivedId", "Id" }, updateSproc.Parameters);
+ Assert.Empty(updateSproc.ResultColumns);
+ Assert.False(updateSproc.AreTransactionsSuppressed);
+ Assert.Empty(updateSproc.GetAnnotations());
+ Assert.Same(principalBase, updateSproc.EntityType);
+ Assert.Equal("Id", id.GetColumnName(StoreObjectIdentifier.Create(principalBase, StoreObjectType.UpdateStoredProcedure).Value));
+ Assert.Null(id.FindOverrides(StoreObjectIdentifier.Create(principalBase, StoreObjectType.UpdateStoredProcedure).Value));
+
+ var deleteSproc = principalBase.GetDeleteStoredProcedure()!;
+ Assert.Equal("PrincipalBase_Delete", deleteSproc.Name);
+ Assert.Equal("TPC", deleteSproc.Schema);
+ Assert.Equal(new[] { "Id" }, deleteSproc.Parameters);
+ Assert.Empty(deleteSproc.ResultColumns);
+ Assert.Same(principalBase, deleteSproc.EntityType);
+ Assert.Equal("Id", id.GetColumnName(StoreObjectIdentifier.Create(principalBase, StoreObjectType.DeleteStoredProcedure).Value));
+ Assert.Null(id.FindOverrides(StoreObjectIdentifier.Create(principalBase, StoreObjectType.DeleteStoredProcedure).Value));
Assert.Equal("PrincipalBase", principalBase.GetDiscriminatorValue());
Assert.Null(principalBase.FindDiscriminatorProperty());
@@ -2643,6 +2777,39 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
Assert.Equal("PrincipalDerivedView", principalDerived.GetViewName());
Assert.Equal("TPC", principalBase.GetViewSchema());
+ insertSproc = principalDerived.GetInsertStoredProcedure()!;
+ Assert.Equal("Derived_Insert", insertSproc.Name);
+ Assert.Equal("TPC", insertSproc.Schema);
+ Assert.Equal(new[] { "PrincipalBaseId", "PrincipalDerivedId" }, insertSproc.Parameters);
+ Assert.Equal(new[] { "Id" }, insertSproc.ResultColumns);
+ Assert.False(insertSproc.AreTransactionsSuppressed);
+ Assert.Null(insertSproc["foo"]);
+ Assert.Same(principalDerived, insertSproc.EntityType);
+ Assert.Equal("DerivedId", id.GetColumnName(StoreObjectIdentifier.Create(principalDerived, StoreObjectType.InsertStoredProcedure).Value));
+ Assert.Equal("bar3",
+ id.FindOverrides(StoreObjectIdentifier.Create(principalDerived, StoreObjectType.InsertStoredProcedure).Value)["foo"]);
+
+ updateSproc = principalDerived.GetUpdateStoredProcedure()!;
+ Assert.Equal("Derived_Update", updateSproc.Name);
+ Assert.Equal("Derived", updateSproc.Schema);
+ Assert.Equal(new[] { "PrincipalBaseId", "PrincipalDerivedId", "Id" }, updateSproc.Parameters);
+ Assert.Empty(updateSproc.ResultColumns);
+ Assert.False(updateSproc.AreTransactionsSuppressed);
+ Assert.Empty(updateSproc.GetAnnotations());
+ Assert.Same(principalDerived, updateSproc.EntityType);
+ Assert.Equal("Id", id.GetColumnName(StoreObjectIdentifier.Create(principalDerived, StoreObjectType.UpdateStoredProcedure).Value));
+ Assert.Null(id.FindOverrides(StoreObjectIdentifier.Create(principalDerived, StoreObjectType.UpdateStoredProcedure).Value));
+
+ deleteSproc = principalDerived.GetDeleteStoredProcedure()!;
+ Assert.Equal("Derived_Delete", deleteSproc.Name);
+ Assert.Equal("TPC", deleteSproc.Schema);
+ Assert.Equal(new[] { "Id" }, deleteSproc.Parameters);
+ Assert.Empty(deleteSproc.ResultColumns);
+ Assert.False(deleteSproc.AreTransactionsSuppressed);
+ Assert.Same(principalDerived, deleteSproc.EntityType);
+ Assert.Equal("Id", id.GetColumnName(StoreObjectIdentifier.Create(principalDerived, StoreObjectType.DeleteStoredProcedure).Value));
+ Assert.Null(id.FindOverrides(StoreObjectIdentifier.Create(principalDerived, StoreObjectType.DeleteStoredProcedure).Value));
+
Assert.Equal("PrincipalDerived>", principalDerived.GetDiscriminatorValue());
Assert.Null(principalDerived.FindDiscriminatorProperty());
Assert.Equal("TPC", principalDerived.GetMappingStrategy());
@@ -2708,8 +2875,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
eb.Ignore(e => e.Owned);
eb.UseTpcMappingStrategy();
+
eb.ToTable("PrincipalBase");
- eb.ToView("PrincipalBaseView");
+ eb.ToView("PrincipalBaseView", tb => tb.Property(e => e.Id).HasAnnotation("foo", "bar2"));
+
+ eb.InsertUsingStoredProcedure(s => s.SuppressTransactions()
+ .HasParameter("PrincipalBaseId")
+ .HasParameter("PrincipalDerivedId")
+ .HasParameter(p => p.Id, pb => pb.HasName("BaseId").IsOutput().HasAnnotation("foo", "bar"))
+ .HasAnnotation("foo", "bar1"));
+ eb.UpdateUsingStoredProcedure(s => s
+ .HasParameter("PrincipalBaseId")
+ .HasParameter("PrincipalDerivedId")
+ .HasParameter(p => p.Id));
+ eb.DeleteUsingStoredProcedure(s => s
+ .HasParameter(p => p.Id));
});
modelBuilder.Entity>>(
@@ -2723,6 +2903,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
eb.ToTable("PrincipalDerived");
eb.ToView("PrincipalDerivedView");
+
+ eb.InsertUsingStoredProcedure("Derived_Insert", s => s
+ .HasParameter("PrincipalBaseId")
+ .HasParameter("PrincipalDerivedId")
+ .HasResultColumn(p => p.Id, pb => pb.HasName("DerivedId").HasAnnotation("foo", "bar3")));
+ eb.UpdateUsingStoredProcedure("Derived_Update", "Derived", s => s
+ .HasParameter("PrincipalBaseId")
+ .HasParameter("PrincipalDerivedId")
+ .HasParameter(p => p.Id));
+ eb.DeleteUsingStoredProcedure("Derived_Delete", s => s
+ .HasParameter(p => p.Id));
});
modelBuilder.Entity>(
@@ -4119,23 +4310,23 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
nullable: true);
blob.AddAnnotation(""Cosmos:PropertyName"", ""JsonBlob"");
- var __id = runtimeEntityType.AddProperty(
+ var id0 = runtimeEntityType.AddProperty(
""__id"",
typeof(string),
afterSaveBehavior: PropertySaveBehavior.Throw,
valueGeneratorFactory: new IdValueGeneratorFactory().Create);
- __id.AddAnnotation(""Cosmos:PropertyName"", ""id"");
+ id0.AddAnnotation(""Cosmos:PropertyName"", ""id"");
- var __jObject = runtimeEntityType.AddProperty(
+ var jObject = runtimeEntityType.AddProperty(
""__jObject"",
typeof(JObject),
nullable: true,
valueGenerated: ValueGenerated.OnAddOrUpdate,
beforeSaveBehavior: PropertySaveBehavior.Ignore,
afterSaveBehavior: PropertySaveBehavior.Ignore);
- __jObject.AddAnnotation(""Cosmos:PropertyName"", """");
+ jObject.AddAnnotation(""Cosmos:PropertyName"", """");
- var _etag = runtimeEntityType.AddProperty(
+ var etag = runtimeEntityType.AddProperty(
""_etag"",
typeof(string),
nullable: true,
@@ -4149,7 +4340,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
runtimeEntityType.SetPrimaryKey(key);
var key0 = runtimeEntityType.AddKey(
- new[] { __id, partitionId });
+ new[] { id0, partitionId });
return runtimeEntityType;
}
diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
index 574fb3bdb5c..42320146305 100644
--- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
+++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
@@ -2735,6 +2735,33 @@ public virtual void Detects_non_generated_update_stored_procedure_result_columns
modelBuilder);
}
+ [ConditionalFact]
+ public virtual void Detects_non_generated_insert_stored_procedure_output_parameter_in_TPC()
+ {
+ var modelBuilder = CreateConventionModelBuilder();
+ modelBuilder.Entity()
+ .InsertUsingStoredProcedure(s => s.HasParameter(a => a.Name, p => p.IsOutput()))
+ .UseTpcMappingStrategy();
+ modelBuilder.Entity();
+
+ VerifyError(
+ RelationalStrings.StoredProcedureOutputParameterNotGenerated(nameof(Animal), nameof(Animal.Name), "Animal_Insert"),
+ modelBuilder);
+ }
+
+ [ConditionalFact]
+ public virtual void Detects_non_generated_update_stored_procedure_input_output_parameter()
+ {
+ var modelBuilder = CreateConventionModelBuilder();
+ modelBuilder.Entity()
+ .Ignore(a => a.FavoritePerson)
+ .UpdateUsingStoredProcedure(s => s.HasParameter(a => a.Id).HasParameter(a => a.Name, p => p.IsInputOutput()));
+
+ VerifyError(
+ RelationalStrings.StoredProcedureOutputParameterNotGenerated(nameof(Animal), nameof(Animal.Name), "Animal_Update"),
+ modelBuilder);
+ }
+
[ConditionalFact]
public virtual void Detects_delete_stored_procedure_result_columns_in_TPH()
{
@@ -2852,6 +2879,24 @@ public virtual void Detects_InsertUsingStoredProcedure_without_a_name()
modelBuilder);
}
+ [ConditionalFact]
+ public virtual void Detects_missing_generated_stored_procedure_parameters()
+ {
+ var modelBuilder = CreateConventionModelBuilder();
+ modelBuilder.Entity()
+ .UpdateUsingStoredProcedure("Update", s => s
+ .HasParameter(a => a.Id, p => p.HasName("MyId"))
+ .HasParameter(a => a.Name)
+ .HasParameter("FavoritePersonId")
+ .HasParameter(a => a.Name))
+ .Property(a => a.Name).ValueGeneratedOnUpdate();
+
+ VerifyError(
+ RelationalStrings.StoredProcedureGeneratedPropertiesNotMapped(nameof(Animal),
+ "Update", "{'Name'}"),
+ modelBuilder);
+ }
+
[ConditionalFact]
public virtual void Detects_missing_stored_procedure_parameters_in_TPC()
{
@@ -2865,7 +2910,10 @@ public virtual void Detects_missing_stored_procedure_parameters_in_TPC()
.HasResultColumn(a => a.Name))
.Property(a => a.Name).ValueGeneratedOnUpdate();
modelBuilder.Entity()
- .UpdateUsingStoredProcedure(s => s.HasParameter(c => c.Breed).HasParameter(a => a.Name));
+ .UpdateUsingStoredProcedure(s => s
+ .HasResultColumn(a => a.Name)
+ .HasParameter(c => c.Breed)
+ .HasParameter(a => a.Name));
VerifyError(
RelationalStrings.StoredProcedurePropertiesNotMapped(nameof(Cat), "Cat_Update", "{'Identity', 'Id', 'FavoritePersonId'}"),
diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs
index 5afc5bb785c..827b0e86125 100644
--- a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs
+++ b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using NameSpace1;
@@ -51,7 +52,7 @@ public void Can_use_relational_model_with_tables(bool useExplicitMapping, Mappin
[InlineData(Mapping.TPC)]
public void Can_use_relational_model_with_views(Mapping mapping)
{
- var model = CreateTestModel(mapToTables: false, mapToViews: true, mapping);
+ var model = CreateTestModel(mapToTables: false, mapToViews: true, mapping: mapping);
Assert.Equal(11, model.Model.GetEntityTypes().Count());
Assert.Equal(mapping switch
@@ -67,13 +68,47 @@ public void Can_use_relational_model_with_views(Mapping mapping)
AssertViews(model, mapping);
}
+ [ConditionalTheory]
+ [InlineData(true, Mapping.TPH)]
+ [InlineData(true, Mapping.TPT)]
+ [InlineData(true, Mapping.TPC)]
+ [InlineData(false, Mapping.TPH)]
+ [InlineData(false, Mapping.TPT)]
+ [InlineData(false, Mapping.TPC)]
+ public void Can_use_relational_model_with_sprocs(bool mapToTables, Mapping mapping)
+ {
+ var model = CreateTestModel(mapToTables: mapToTables, mapToSprocs:true, mapping: mapping);
+
+ Assert.Equal(11, model.Model.GetEntityTypes().Count());
+ Assert.Equal(
+ mapping switch
+ {
+ Mapping.TPC => 5,
+ Mapping.TPH => 3,
+ _ => 6
+ }, model.Tables.Count());
+
+ Assert.Equal(mapping switch
+ {
+ Mapping.TPC => 24,
+ Mapping.TPH => 18,
+ _ => 27
+ }, model.StoredProcedures.Count());
+
+ Assert.Empty(model.Views);
+ Assert.True(model.Model.GetEntityTypes().All(et => !et.GetViewMappings().Any()));
+
+ AssertDefaultMappings(model, mapping);
+ AssertSprocs(model, mapping);
+ }
+
[ConditionalTheory]
[InlineData(Mapping.TPH)]
[InlineData(Mapping.TPT)]
[InlineData(Mapping.TPC)]
- public void Can_use_relational_model_with_views_and_tables(Mapping mapping)
+ public void Can_use_relational_model_with_tables_and_views(Mapping mapping)
{
- var model = CreateTestModel(mapToTables: true, mapToViews: true, mapping);
+ var model = CreateTestModel(mapToTables: true, mapToViews: true, mapping: mapping);
Assert.Equal(11, model.Model.GetEntityTypes().Count());
Assert.Equal(mapping switch
@@ -82,6 +117,7 @@ public void Can_use_relational_model_with_views_and_tables(Mapping mapping)
Mapping.TPH => 3,
_ => 6
}, model.Tables.Count());
+
Assert.Equal(mapping switch
{
Mapping.TPC => 5,
@@ -641,12 +677,12 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
var specialCustomerTable =
specialCustomerType.GetTableMappings().Select(t => t.Table).Last();
- var SpecialtyCK = specialCustomerType.GetCheckConstraints().Single();
- Assert.Equal("Specialty", SpecialtyCK.Name);
- Assert.Equal("Specialty", SpecialtyCK.GetName(
+ var specialtyCk = specialCustomerType.GetCheckConstraints().Single();
+ Assert.Equal("Specialty", specialtyCk.Name);
+ Assert.Equal("Specialty", specialtyCk.GetName(
StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema)));
- Assert.Equal("Specialty", SpecialtyCK.GetDefaultName());
- Assert.Equal("Specialty", SpecialtyCK.GetDefaultName(
+ Assert.Equal("Specialty", specialtyCk.GetDefaultName());
+ Assert.Equal("Specialty", specialtyCk.GetDefaultName(
StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema)));
var customerTable = customerType.GetTableMappings().Last().Table;
@@ -722,7 +758,7 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
extraSpecialCustomerType.GetTableMappings().Select(t => t.Table).First(t => t.Name == "ExtraSpecialCustomer");
Assert.Empty(customerTable.CheckConstraints);
- Assert.Same(SpecialtyCK, specialCustomerTable.CheckConstraints.Single());
+ Assert.Same(specialtyCk, specialCustomerTable.CheckConstraints.Single());
Assert.Empty(extraSpecialCustomerTable.CheckConstraints);
Assert.Equal(4, customerPk.GetMappedConstraints().Count());
@@ -845,8 +881,8 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
Assert.True(addressColumn.IsNullable);
var abstractStringColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(AbstractCustomer.AbstractString));
- Assert.True(specialtyColumn.IsNullable);
- Assert.Equal(2, specialtyColumn.PropertyMappings.Count);
+ Assert.True(abstractStringColumn.IsNullable);
+ Assert.Equal(3, abstractStringColumn.PropertyMappings.Count);
var abstractStringProperty = abstractStringColumn.PropertyMappings.First().Property;
Assert.Equal(3, abstractStringProperty.GetTableColumnMappings().Count());
@@ -902,8 +938,6 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
Assert.Equal("IX_AbstractBase_RelatedCustomerSpecialty", specialCustomerDbIndex.Name);
Assert.Equal("IX_AbstractBase_AnotherRelatedCustomerId", anotherSpecialCustomerDbIndex.Name);
-
- Assert.Equal(5, idProperty.GetTableColumnMappings().Count());
}
else
{
@@ -974,8 +1008,6 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
Assert.Equal("IX_SpecialCustomer_RelatedCustomerSpecialty", specialCustomerDbIndex.Name);
Assert.Equal("IX_SpecialCustomer_AnotherRelatedCustomerId", anotherSpecialCustomerDbIndex.Name);
-
- Assert.Equal(3, idProperty.GetTableColumnMappings().Count());
}
Assert.Same(specialCustomerPkConstraint.MappedKeys.First(), customerPk);
@@ -987,7 +1019,833 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
}
}
- private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToViews = false, Mapping mapping = Mapping.TPH)
+ private static void AssertSprocs(IRelationalModel model, Mapping mapping)
+ {
+ var orderType = model.Model.FindEntityType(typeof(Order));
+ var orderInsertMapping = orderType.GetInsertStoredProcedureMappings().Single();
+ Assert.True(orderInsertMapping.IncludesDerivedTypes);
+ Assert.Same(orderType.GetInsertStoredProcedure(), orderInsertMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { nameof(Order.AlternateId), nameof(Order.CustomerId), nameof(Order.OrderDate) },
+ orderInsertMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Equal(
+ new[] { nameof(Order.Id) },
+ orderInsertMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(orderInsertMapping.ResultColumnMappings, orderInsertMapping.ColumnMappings);
+
+ var ordersInsertSproc = orderInsertMapping.StoreStoredProcedure;
+ Assert.Same(ordersInsertSproc, orderInsertMapping.Table);
+ Assert.Equal("Order_Insert", ordersInsertSproc.Name);
+ Assert.Null(ordersInsertSproc.Schema);
+ Assert.False(ordersInsertSproc.IsShared);
+ Assert.Same(ordersInsertSproc, model.FindStoredProcedure(ordersInsertSproc.Name, ordersInsertSproc.Schema));
+ Assert.Equal(
+ new[] { nameof(Order) },
+ ordersInsertSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { nameof(Order.AlternateId), nameof(Order.CustomerId), nameof(Order.OrderDate) },
+ ordersInsertSproc.Parameters.Select(m => m.Name));
+
+ Assert.Equal(
+ new[] { nameof(Order.Id) },
+ ordersInsertSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(ordersInsertSproc.ResultColumns, ordersInsertSproc.Columns);
+
+ var orderDate = orderType.FindProperty(nameof(Order.OrderDate));
+
+ var orderDateInsertMapping = orderDate.GetInsertStoredProcedureParameterMappings().Single();
+ Assert.NotNull(orderDateInsertMapping.TypeMapping);
+ Assert.Equal("default_datetime_mapping", orderDateInsertMapping.TypeMapping.StoreType);
+ Assert.Same(orderInsertMapping, orderDateInsertMapping.TableMapping);
+
+ var orderDateColumn = orderDateInsertMapping.Parameter;
+ Assert.Same(orderDateInsertMapping.Parameter, orderDateInsertMapping.Column);
+ Assert.Same(orderDateColumn, ordersInsertSproc.FindParameter("OrderDate"));
+ Assert.Same(orderDateColumn, ordersInsertSproc.FindParameter(orderDate));
+ Assert.Equal("OrderDate", orderDateColumn.Name);
+ Assert.Equal("default_datetime_mapping", orderDateColumn.StoreType);
+ Assert.False(orderDateColumn.IsNullable);
+ Assert.Equal(ParameterDirection.Input, orderDateColumn.Direction);
+ Assert.Same(ordersInsertSproc, orderDateColumn.StoredProcedure);
+ Assert.Same(orderDateColumn.StoredProcedure, orderDateColumn.Table);
+ Assert.Same(orderDateInsertMapping, orderDateColumn.FindParameterMapping(orderType));
+
+ var abstractBaseType = model.Model.FindEntityType(typeof(AbstractBase));
+ var abstractCustomerType = model.Model.FindEntityType(typeof(AbstractCustomer));
+ var customerType = model.Model.FindEntityType(typeof(Customer));
+ var specialCustomerType = model.Model.FindEntityType(typeof(SpecialCustomer));
+ var extraSpecialCustomerType = model.Model.FindEntityType(typeof(ExtraSpecialCustomer));
+ var orderDetailsOwnership = orderType.FindNavigation(nameof(Order.Details)).ForeignKey;
+ var orderDetailsType = orderDetailsOwnership.DeclaringEntityType;
+
+ Assert.Empty(ordersInsertSproc.GetReferencingRowInternalForeignKeys(orderType));
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(SpecialCustomer), ordersInsertSproc.Name),
+ Assert.Throws(
+ () => ordersInsertSproc.GetReferencingRowInternalForeignKeys(specialCustomerType)).Message);
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(SpecialCustomer), ordersInsertSproc.Name),
+ Assert.Throws(
+ () => ordersInsertSproc.GetRowInternalForeignKeys(specialCustomerType)).Message);
+ Assert.False(ordersInsertSproc.IsOptional(orderType));
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(OrderDetails), ordersInsertSproc.Name),
+ Assert.Throws(
+ () => ordersInsertSproc.IsOptional(orderDetailsType)).Message);
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(SpecialCustomer), ordersInsertSproc.Name),
+ Assert.Throws(
+ () => ordersInsertSproc.IsOptional(specialCustomerType)).Message);
+
+ var billingAddressOwnership = orderDetailsType.FindNavigation(nameof(OrderDetails.BillingAddress)).ForeignKey;
+ Assert.True(billingAddressOwnership.IsRequiredDependent);
+
+ var billingAddressType = billingAddressOwnership.DeclaringEntityType;
+
+ var shippingAddressOwnership = orderDetailsType.FindNavigation(nameof(OrderDetails.ShippingAddress)).ForeignKey;
+ Assert.True(shippingAddressOwnership.IsRequiredDependent);
+
+ var billingAddressInsertMapping = billingAddressType.GetInsertStoredProcedureMappings().Single();
+ Assert.Same(billingAddressType.GetInsertStoredProcedure(), billingAddressInsertMapping.StoredProcedure);
+ Assert.Same(billingAddressType, billingAddressInsertMapping.StoredProcedure.EntityType);
+
+ var billingAddressInsertSproc = billingAddressInsertMapping.StoreStoredProcedure;
+ Assert.Equal("BillingAddress_Insert", billingAddressInsertSproc.Name);
+ Assert.Null(billingAddressInsertSproc.Schema);
+ Assert.Equal(
+ new[] { "OrderDetails.BillingAddress#Address" },
+ billingAddressInsertSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { nameof(Address.City), nameof(Address.Street), "OrderDetailsOrderId" },
+ billingAddressInsertSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(billingAddressInsertSproc.ResultColumns.Select(m => m.Name));
+
+ var billingAddressUpdateMapping = billingAddressType.GetUpdateStoredProcedureMappings().Single();
+ Assert.Same(billingAddressType.GetUpdateStoredProcedure(), billingAddressUpdateMapping.StoredProcedure);
+ Assert.Same(billingAddressType, billingAddressUpdateMapping.StoredProcedure.EntityType);
+
+ var billingAddressUpdateSproc = billingAddressUpdateMapping.StoreStoredProcedure;
+ Assert.Equal("BillingAddress_Update", billingAddressUpdateSproc.Name);
+ Assert.Null(billingAddressUpdateSproc.Schema);
+ Assert.Equal(
+ new[] { "OrderDetails.BillingAddress#Address" },
+ billingAddressUpdateSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { nameof(Address.City), nameof(Address.Street), "OrderDetailsOrderId" },
+ billingAddressUpdateSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(billingAddressUpdateSproc.ResultColumns.Select(m => m.Name));
+
+ var billingAddressDeleteMapping = billingAddressType.GetDeleteStoredProcedureMappings().Single();
+ Assert.Same(billingAddressType.GetDeleteStoredProcedure(), billingAddressDeleteMapping.StoredProcedure);
+ Assert.Same(billingAddressType, billingAddressDeleteMapping.StoredProcedure.EntityType);
+
+ var billingAddressDeleteSproc = billingAddressDeleteMapping.StoreStoredProcedure;
+ Assert.Equal("BillingAddress_Delete", billingAddressDeleteSproc.Name);
+ Assert.Null(billingAddressDeleteSproc.Schema);
+ Assert.Equal(
+ new[] { "OrderDetails.BillingAddress#Address" },
+ billingAddressDeleteSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { "OrderDetailsOrderId" },
+ billingAddressDeleteSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(billingAddressDeleteSproc.ResultColumns.Select(m => m.Name));
+
+ Assert.Equal(new[] { orderDate }, orderDateColumn.PropertyMappings.Select(m => m.Property));
+
+ var specialCustomerInsertSproc =
+ specialCustomerType.GetInsertStoredProcedureMappings().Last().StoreStoredProcedure;
+ var specialCustomerUpdateSproc =
+ specialCustomerType.GetUpdateStoredProcedureMappings().Last().StoreStoredProcedure;
+ var specialCustomerDeleteSproc =
+ specialCustomerType.GetDeleteStoredProcedureMappings().Last().StoreStoredProcedure;
+
+ var customerInsertSproc = customerType.GetInsertStoredProcedureMappings().Last().StoreStoredProcedure;
+ Assert.False(customerInsertSproc.IsOptional(customerType));
+ if (mapping == Mapping.TPC)
+ {
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(SpecialCustomer), customerInsertSproc.Name),
+ Assert.Throws(
+ () => customerInsertSproc.IsOptional(specialCustomerType)).Message);
+ }
+ else
+ {
+ Assert.False(customerInsertSproc.IsOptional(specialCustomerType));
+ Assert.False(customerInsertSproc.IsOptional(extraSpecialCustomerType));
+ }
+
+ var customerUpdateSproc = customerType.GetUpdateStoredProcedureMappings().Last().StoreStoredProcedure;
+ Assert.False(customerUpdateSproc.IsOptional(customerType));
+ if (mapping == Mapping.TPC)
+ {
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(SpecialCustomer), customerUpdateSproc.Name),
+ Assert.Throws(
+ () => customerUpdateSproc.IsOptional(specialCustomerType)).Message);
+ }
+ else
+ {
+ Assert.False(customerUpdateSproc.IsOptional(specialCustomerType));
+ Assert.False(customerUpdateSproc.IsOptional(extraSpecialCustomerType));
+ }
+
+ var customerDeleteSproc = customerType.GetDeleteStoredProcedureMappings().Last().StoreStoredProcedure;
+ Assert.False(customerDeleteSproc.IsOptional(customerType));
+ if (mapping == Mapping.TPC)
+ {
+ Assert.Equal(
+ RelationalStrings.TableNotMappedEntityType(nameof(SpecialCustomer), customerDeleteSproc.Name),
+ Assert.Throws(
+ () => customerDeleteSproc.IsOptional(specialCustomerType)).Message);
+ }
+ else
+ {
+ Assert.False(customerDeleteSproc.IsOptional(specialCustomerType));
+ Assert.False(customerDeleteSproc.IsOptional(extraSpecialCustomerType));
+ }
+
+ var customerPk = specialCustomerType.FindPrimaryKey();
+ var idProperty = customerPk.Properties.Single();
+
+ if (mapping == Mapping.TPT)
+ {
+ var baseInsertMapping = abstractBaseType.GetInsertStoredProcedureMappings().Single();
+ Assert.True(baseInsertMapping.IncludesDerivedTypes);
+ Assert.Same(abstractBaseType.GetInsertStoredProcedure(), baseInsertMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { nameof(AbstractBase.Id), "SpecialtyAk" },
+ baseInsertMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Empty(baseInsertMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(baseInsertMapping.ResultColumnMappings, baseInsertMapping.ColumnMappings);
+
+ var baseInsertSproc = baseInsertMapping.StoreStoredProcedure;
+ Assert.Equal("AbstractBase_Insert", baseInsertSproc.Name);
+ Assert.Equal("Customer_Insert", customerInsertSproc.Name);
+ Assert.Empty(abstractCustomerType.GetInsertStoredProcedureMappings().Where(m => m.IncludesDerivedTypes));
+ Assert.Equal(
+ "SpecialCustomer_Insert",
+ specialCustomerType.GetInsertStoredProcedureMappings().Single(m => m.IncludesDerivedTypes).StoreStoredProcedure.Name);
+ Assert.Null(baseInsertSproc.Schema);
+ Assert.Equal(
+ new[] { nameof(AbstractBase), nameof(Customer), nameof(ExtraSpecialCustomer), nameof(SpecialCustomer) },
+ baseInsertSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { "InsertId", "SpecialtyAk" },
+ baseInsertSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(baseInsertSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(baseInsertSproc.ResultColumns, baseInsertSproc.Columns);
+
+ var baseUpdateMapping = abstractBaseType.GetUpdateStoredProcedureMappings().Single();
+ Assert.True(baseUpdateMapping.IncludesDerivedTypes);
+ Assert.Same(abstractBaseType.GetUpdateStoredProcedure(), baseUpdateMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { nameof(AbstractBase.Id), "SpecialtyAk" },
+ baseUpdateMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Empty(
+ baseUpdateMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(baseUpdateMapping.ResultColumnMappings, baseUpdateMapping.ColumnMappings);
+
+ var baseUpdateSproc = baseUpdateMapping.StoreStoredProcedure;
+ Assert.Equal("AbstractBase_Update", baseUpdateSproc.Name);
+ Assert.Equal("Customer_Update", customerUpdateSproc.Name);
+ Assert.Empty(abstractCustomerType.GetUpdateStoredProcedureMappings().Where(m => m.IncludesDerivedTypes));
+ Assert.Equal(
+ "SpecialCustomer_Update",
+ specialCustomerType.GetUpdateStoredProcedureMappings().Single(m => m.IncludesDerivedTypes).StoreStoredProcedure.Name);
+
+ Assert.Null(baseUpdateSproc.Schema);
+ Assert.Equal(
+ new[] { nameof(AbstractBase), nameof(Customer), nameof(ExtraSpecialCustomer), nameof(SpecialCustomer) },
+ baseUpdateSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { "UpdateId", "SpecialtyAk" },
+ baseUpdateSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(baseUpdateSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(baseUpdateSproc.ResultColumns, baseUpdateSproc.Columns);
+
+ var baseDeleteMapping = abstractBaseType.GetDeleteStoredProcedureMappings().Single();
+ Assert.True(baseDeleteMapping.IncludesDerivedTypes);
+ Assert.Same(abstractBaseType.GetDeleteStoredProcedure(), baseDeleteMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { nameof(AbstractBase.Id) },
+ baseDeleteMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Empty(
+ baseDeleteMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(baseDeleteMapping.ResultColumnMappings, baseDeleteMapping.ColumnMappings);
+
+ var baseDeleteSproc = baseDeleteMapping.StoreStoredProcedure;
+ Assert.Equal("AbstractBase_Delete", baseDeleteSproc.Name);
+ Assert.Equal("Customer_Delete", customerDeleteSproc.Name);
+ Assert.Empty(abstractCustomerType.GetDeleteStoredProcedureMappings().Where(m => m.IncludesDerivedTypes));
+ Assert.Equal(
+ "SpecialCustomer_Delete",
+ specialCustomerType.GetDeleteStoredProcedureMappings().Single(m => m.IncludesDerivedTypes).StoreStoredProcedure.Name);
+
+ Assert.Null(baseDeleteSproc.Schema);
+ Assert.Equal(
+ new[] { nameof(AbstractBase), nameof(Customer), nameof(ExtraSpecialCustomer), nameof(SpecialCustomer) },
+ baseDeleteSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { "DeleteId" },
+ baseDeleteSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(baseDeleteSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(baseDeleteSproc.ResultColumns, baseDeleteSproc.Columns);
+
+ Assert.Equal(3, specialCustomerType.GetInsertStoredProcedureMappings().Count());
+ Assert.Null(specialCustomerType.GetInsertStoredProcedureMappings().First().IsSplitEntityTypePrincipal);
+ Assert.False(specialCustomerType.GetInsertStoredProcedureMappings().First().IncludesDerivedTypes);
+ Assert.Null(specialCustomerType.GetInsertStoredProcedureMappings().Last().IsSplitEntityTypePrincipal);
+ Assert.True(specialCustomerType.GetTableMappings().Last().IncludesDerivedTypes);
+
+ Assert.Equal("SpecialCustomer_Insert", specialCustomerInsertSproc.Name);
+ Assert.Single(specialCustomerInsertSproc.ResultColumns);
+ Assert.Equal(4, specialCustomerInsertSproc.Parameters.Count());
+
+ Assert.Null(
+ specialCustomerInsertSproc.EntityTypeMappings.Single(m => m.EntityType == specialCustomerType).IsSharedTablePrincipal);
+
+ var specialtyInsertParameter =
+ specialCustomerInsertSproc.Parameters.Single(c => c.Name == nameof(SpecialCustomer.Specialty));
+
+ Assert.False(specialtyInsertParameter.IsNullable);
+
+ var specialtyProperty = specialtyInsertParameter.PropertyMappings.First().Property;
+
+ Assert.Equal(
+ RelationalStrings.PropertyNotMappedToTable(
+ nameof(SpecialCustomer.Specialty), nameof(SpecialCustomer), "Customer_Insert"),
+ Assert.Throws(
+ () =>
+ specialtyProperty.IsColumnNullable(
+ StoreObjectIdentifier.InsertStoredProcedure(customerInsertSproc.Name, customerInsertSproc.Schema)))
+ .Message);
+
+ var abstractStringParameter =
+ specialCustomerInsertSproc.Parameters.Single(c => c.Name == nameof(AbstractCustomer.AbstractString));
+ Assert.False(abstractStringParameter.IsNullable);
+ Assert.Equal(2, abstractStringParameter.PropertyMappings.Count);
+
+ var abstractStringProperty = abstractStringParameter.PropertyMappings.First().Property;
+ Assert.Equal(2, abstractStringProperty.GetInsertStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.InsertStoredProcedure(specialCustomerInsertSproc.Name, specialCustomerInsertSproc.Schema)
+ },
+ abstractStringProperty.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure));
+
+ var extraSpecialCustomerInsertSproc =
+ extraSpecialCustomerType.GetInsertStoredProcedureMappings().Select(t => t.StoreStoredProcedure)
+ .First(t => t.Name == "ExtraSpecialCustomer_Insert");
+
+ var idPropertyInsertParameter = baseInsertSproc.FindParameter(idProperty)!;
+ var idPropertyInsertParameterMapping = idProperty.GetInsertStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyInsertParameter, baseInsertSproc.FindParameter("InsertId"));
+ Assert.Same(idPropertyInsertParameter, idPropertyInsertParameterMapping.Column);
+ Assert.Equal("InsertId", idPropertyInsertParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyInsertParameter.StoreType);
+ Assert.False(idPropertyInsertParameter.IsNullable);
+ Assert.Same(baseInsertSproc, idPropertyInsertParameter.StoredProcedure);
+ Assert.Same(idPropertyInsertParameter.StoredProcedure, idPropertyInsertParameter.Table);
+ Assert.Same(idPropertyInsertParameterMapping, idPropertyInsertParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Equal(3, idProperty.GetInsertStoredProcedureResultColumnMappings().Count());
+ Assert.Equal(7, idProperty.GetInsertStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.InsertStoredProcedure(baseInsertSproc.Name, baseInsertSproc.Schema),
+ StoreObjectIdentifier.InsertStoredProcedure(customerInsertSproc.Name, customerInsertSproc.Schema),
+ StoreObjectIdentifier.InsertStoredProcedure(specialCustomerInsertSproc.Name, specialCustomerInsertSproc.Schema),
+ StoreObjectIdentifier.InsertStoredProcedure(
+ extraSpecialCustomerInsertSproc.Name, extraSpecialCustomerInsertSproc.Schema)
+ },
+ idProperty.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure));
+
+ var extraSpecialCustomerUpdateSproc =
+ extraSpecialCustomerType.GetUpdateStoredProcedureMappings().Select(t => t.StoreStoredProcedure)
+ .First(t => t.Name == "ExtraSpecialCustomer_Update");
+
+ var idPropertyUpdateParameter = baseUpdateSproc.FindParameter(idProperty)!;
+ var idPropertyUpdateParameterMapping = idProperty.GetUpdateStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyUpdateParameter, baseUpdateSproc.FindParameter("UpdateId"));
+ Assert.Same(idPropertyUpdateParameter, idPropertyUpdateParameterMapping.Parameter);
+ Assert.Equal("UpdateId", idPropertyUpdateParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyUpdateParameter.StoreType);
+ Assert.Equal(ParameterDirection.Input, idPropertyUpdateParameter.Direction);
+ Assert.False(idPropertyUpdateParameter.IsNullable);
+ Assert.Same(baseUpdateSproc, idPropertyUpdateParameter.StoredProcedure);
+ Assert.Same(idPropertyUpdateParameter.StoredProcedure, idPropertyUpdateParameter.Table);
+ Assert.Same(idPropertyUpdateParameterMapping, idPropertyUpdateParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Empty(idProperty.GetUpdateStoredProcedureResultColumnMappings());
+ Assert.Equal(10, idProperty.GetUpdateStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.UpdateStoredProcedure(baseUpdateSproc.Name, baseUpdateSproc.Schema),
+ StoreObjectIdentifier.UpdateStoredProcedure(customerUpdateSproc.Name, customerUpdateSproc.Schema),
+ StoreObjectIdentifier.UpdateStoredProcedure(specialCustomerUpdateSproc.Name, specialCustomerUpdateSproc.Schema),
+ StoreObjectIdentifier.UpdateStoredProcedure(
+ extraSpecialCustomerUpdateSproc.Name, extraSpecialCustomerUpdateSproc.Schema)
+ },
+ idProperty.GetMappedStoreObjects(StoreObjectType.UpdateStoredProcedure));
+
+ var extraSpecialCustomerDeleteSproc =
+ extraSpecialCustomerType.GetDeleteStoredProcedureMappings().Select(t => t.StoreStoredProcedure)
+ .First(t => t.Name == "ExtraSpecialCustomer_Delete");
+
+ var idPropertyDeleteParameter = baseDeleteSproc.FindParameter(idProperty)!;
+ var idPropertyDeleteParameterMapping = idProperty.GetDeleteStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyDeleteParameter, baseDeleteSproc.FindParameter("DeleteId"));
+ Assert.Same(idPropertyDeleteParameter, idPropertyDeleteParameterMapping.Parameter);
+ Assert.Equal("DeleteId", idPropertyDeleteParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyDeleteParameter.StoreType);
+ Assert.Equal(ParameterDirection.Input, idPropertyDeleteParameter.Direction);
+ Assert.False(idPropertyDeleteParameter.IsNullable);
+ Assert.Same(baseDeleteSproc, idPropertyDeleteParameter.StoredProcedure);
+ Assert.Same(idPropertyDeleteParameter.StoredProcedure, idPropertyDeleteParameter.Table);
+ Assert.Same(idPropertyDeleteParameterMapping, idPropertyDeleteParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Equal(10, idProperty.GetDeleteStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.DeleteStoredProcedure(baseDeleteSproc.Name, baseDeleteSproc.Schema),
+ StoreObjectIdentifier.DeleteStoredProcedure(customerDeleteSproc.Name, customerDeleteSproc.Schema),
+ StoreObjectIdentifier.DeleteStoredProcedure(specialCustomerDeleteSproc.Name, specialCustomerDeleteSproc.Schema),
+ StoreObjectIdentifier.DeleteStoredProcedure(
+ extraSpecialCustomerDeleteSproc.Name, extraSpecialCustomerDeleteSproc.Schema)
+ },
+ idProperty.GetMappedStoreObjects(StoreObjectType.DeleteStoredProcedure));
+ }
+ else
+ {
+ var specialCustomerInsertMapping = specialCustomerType.GetInsertStoredProcedureMappings().Single();
+ Assert.Null(specialCustomerInsertMapping.IsSplitEntityTypePrincipal);
+
+ var specialtyParameter = specialCustomerInsertSproc.Parameters.Single(c => c.Name == nameof(SpecialCustomer.Specialty));
+
+ if (mapping == Mapping.TPH)
+ {
+ var baseInsertMapping = abstractBaseType.GetInsertStoredProcedureMappings().Single();
+ Assert.True(baseInsertMapping.IncludesDerivedTypes);
+ Assert.Same(abstractBaseType.GetInsertStoredProcedure(), baseInsertMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { "Discriminator", "SpecialtyAk" },
+ baseInsertMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Equal(
+ new[] { nameof(AbstractBase.Id) },
+ baseInsertMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(baseInsertMapping.ResultColumnMappings, baseInsertMapping.ColumnMappings);
+
+ var baseInsertSproc = baseInsertMapping.StoreStoredProcedure;
+ Assert.Equal("AbstractBase_Insert", baseInsertSproc.Name);
+ Assert.Same(baseInsertSproc, customerInsertSproc);
+ Assert.Same(baseInsertSproc, abstractBaseType.GetInsertStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseInsertSproc, abstractCustomerType.GetInsertStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseInsertSproc, specialCustomerType.GetInsertStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseInsertSproc, baseInsertMapping.Table);
+ Assert.Null(baseInsertSproc.Schema);
+ Assert.Equal(
+ new[]
+ {
+ nameof(AbstractBase),
+ nameof(AbstractCustomer),
+ nameof(Customer),
+ nameof(ExtraSpecialCustomer),
+ nameof(SpecialCustomer)
+ },
+ baseInsertSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[]
+ {
+ "Discriminator",
+ nameof(SpecialCustomer.Specialty),
+ nameof(SpecialCustomer.RelatedCustomerSpecialty),
+ "SpecialtyAk",
+ "AnotherRelatedCustomerId",
+ nameof(Customer.EnumValue),
+ nameof(Customer.Name),
+ nameof(Customer.SomeShort),
+ nameof(AbstractCustomer.AbstractString),
+ },
+ baseInsertSproc.Parameters.Select(m => m.Name));
+
+ Assert.Equal(new[] { "InsertId" }, baseInsertSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(baseInsertSproc.ResultColumns, baseInsertSproc.Columns);
+
+ Assert.True(specialCustomerInsertMapping.IncludesDerivedTypes);
+ Assert.Same(customerUpdateSproc, specialCustomerUpdateSproc);
+
+ Assert.Equal(5, specialCustomerInsertSproc.EntityTypeMappings.Count());
+ Assert.Null(specialCustomerInsertSproc.EntityTypeMappings.First().IsSharedTablePrincipal);
+ Assert.Null(specialCustomerInsertSproc.EntityTypeMappings.Last().IsSharedTablePrincipal);
+
+ Assert.Single(specialCustomerInsertSproc.Columns);
+ Assert.Equal(9, specialCustomerInsertSproc.Parameters.Count());
+
+ var baseUpdateMapping = abstractBaseType.GetUpdateStoredProcedureMappings().Single();
+ Assert.True(baseUpdateMapping.IncludesDerivedTypes);
+ Assert.Same(abstractBaseType.GetUpdateStoredProcedure(), baseUpdateMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { nameof(AbstractBase.Id) },
+ baseUpdateMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Empty(
+ baseUpdateMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(baseUpdateMapping.ResultColumnMappings, baseUpdateMapping.ColumnMappings);
+
+ var baseUpdateSproc = baseUpdateMapping.StoreStoredProcedure;
+ Assert.Equal("AbstractBase_Update", baseUpdateSproc.Name);
+ Assert.Same(baseUpdateSproc, customerUpdateSproc);
+ Assert.Same(baseUpdateSproc, abstractBaseType.GetUpdateStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseUpdateSproc, abstractCustomerType.GetUpdateStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseUpdateSproc, specialCustomerType.GetUpdateStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseUpdateSproc, baseUpdateMapping.Table);
+ Assert.Null(baseUpdateSproc.Schema);
+ Assert.Equal(
+ new[]
+ {
+ nameof(AbstractBase),
+ nameof(AbstractCustomer),
+ nameof(Customer),
+ nameof(ExtraSpecialCustomer),
+ nameof(SpecialCustomer)
+ },
+ baseUpdateSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[]
+ {
+ "UpdateId",
+ nameof(SpecialCustomer.Specialty),
+ nameof(SpecialCustomer.RelatedCustomerSpecialty),
+ "AnotherRelatedCustomerId",
+ nameof(Customer.EnumValue),
+ nameof(Customer.Name),
+ nameof(Customer.SomeShort),
+ nameof(AbstractCustomer.AbstractString),
+ },
+ baseUpdateSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(baseUpdateSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(baseUpdateSproc.ResultColumns, baseUpdateSproc.Columns);
+
+ var baseDeleteMapping = abstractBaseType.GetDeleteStoredProcedureMappings().Single();
+ Assert.True(baseDeleteMapping.IncludesDerivedTypes);
+ Assert.Same(abstractBaseType.GetDeleteStoredProcedure(), baseDeleteMapping.StoredProcedure);
+
+ Assert.Equal(
+ new[] { nameof(AbstractBase.Id) },
+ baseDeleteMapping.ParameterMappings.Select(m => m.Property.Name));
+
+ Assert.Empty(
+ baseDeleteMapping.ResultColumnMappings.Select(m => m.Property.Name));
+ Assert.Equal(baseDeleteMapping.ResultColumnMappings, baseDeleteMapping.ColumnMappings);
+
+ var baseDeleteSproc = baseDeleteMapping.StoreStoredProcedure;
+ Assert.Equal("AbstractBase_Delete", baseDeleteSproc.Name);
+ Assert.Same(baseDeleteSproc, customerDeleteSproc);
+ Assert.Same(baseDeleteSproc, abstractBaseType.GetDeleteStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseDeleteSproc, abstractCustomerType.GetDeleteStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseDeleteSproc, specialCustomerType.GetDeleteStoredProcedureMappings().Single().StoreStoredProcedure);
+ Assert.Same(baseDeleteSproc, baseDeleteMapping.Table);
+ Assert.Null(baseDeleteSproc.Schema);
+ Assert.Equal(
+ new[]
+ {
+ nameof(AbstractBase),
+ nameof(AbstractCustomer),
+ nameof(Customer),
+ nameof(ExtraSpecialCustomer),
+ nameof(SpecialCustomer)
+ },
+ baseDeleteSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+
+ Assert.Equal(
+ new[] { "DeleteId" },
+ baseDeleteSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(baseDeleteSproc.ResultColumns.Select(m => m.Name));
+ Assert.Equal(baseDeleteSproc.ResultColumns, baseDeleteSproc.Columns);
+
+ Assert.True(specialCustomerInsertMapping.IncludesDerivedTypes);
+ Assert.Same(customerInsertSproc, specialCustomerInsertSproc);
+
+ Assert.Equal(5, specialCustomerInsertSproc.EntityTypeMappings.Count());
+ Assert.Null(specialCustomerInsertSproc.EntityTypeMappings.First().IsSharedTablePrincipal);
+ Assert.Null(specialCustomerInsertSproc.EntityTypeMappings.Last().IsSharedTablePrincipal);
+
+ Assert.Single(specialCustomerInsertSproc.Columns);
+ Assert.Equal(9, specialCustomerInsertSproc.Parameters.Count());
+
+ Assert.True(specialtyParameter.IsNullable);
+
+ var abstractStringColumn =
+ specialCustomerInsertSproc.Parameters.Single(c => c.Name == nameof(AbstractCustomer.AbstractString));
+ Assert.True(specialtyParameter.IsNullable);
+ Assert.Equal(2, specialtyParameter.PropertyMappings.Count);
+
+ var abstractStringProperty = abstractStringColumn.PropertyMappings.First().Property;
+ Assert.Equal(3, abstractStringProperty.GetInsertStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[] { StoreObjectIdentifier.InsertStoredProcedure(customerInsertSproc.Name, customerInsertSproc.Schema) },
+ abstractStringProperty.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure));
+
+ var idPropertyInsertColumn = baseInsertSproc.FindResultColumn(idProperty)!;
+ var idPropertyInsertColumnMapping = idProperty.GetInsertStoredProcedureResultColumnMappings().First();
+ Assert.Same(idPropertyInsertColumn, baseInsertSproc.FindResultColumn("InsertId"));
+ Assert.Same(idPropertyInsertColumn, idPropertyInsertColumnMapping.Column);
+ Assert.Equal("InsertId", idPropertyInsertColumn.Name);
+ Assert.Equal("default_int_mapping", idPropertyInsertColumn.StoreType);
+ Assert.False(idPropertyInsertColumn.IsNullable);
+ Assert.Same(baseInsertSproc, idPropertyInsertColumn.StoredProcedure);
+ Assert.Same(idPropertyInsertColumn.StoredProcedure, idPropertyInsertColumn.Table);
+ Assert.Same(idPropertyInsertColumnMapping, idPropertyInsertColumn.FindColumnMapping(abstractBaseType));
+
+ Assert.Empty(idProperty.GetInsertStoredProcedureParameterMappings());
+ Assert.Equal(5, idProperty.GetInsertStoredProcedureResultColumnMappings().Count());
+ Assert.Equal(
+ new[] { StoreObjectIdentifier.InsertStoredProcedure(customerInsertSproc.Name, customerInsertSproc.Schema) },
+ idProperty.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure));
+
+ var idPropertyUpdateParameter = baseUpdateSproc.FindParameter(idProperty)!;
+ var idPropertyUpdateParameterMapping = idProperty.GetUpdateStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyUpdateParameter, baseUpdateSproc.FindParameter("UpdateId"));
+ Assert.Same(idPropertyUpdateParameter, idPropertyUpdateParameterMapping.Parameter);
+ Assert.Equal("UpdateId", idPropertyUpdateParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyUpdateParameter.StoreType);
+ Assert.Equal(ParameterDirection.Input, idPropertyUpdateParameter.Direction);
+ Assert.False(idPropertyUpdateParameter.IsNullable);
+ Assert.Same(baseUpdateSproc, idPropertyUpdateParameter.StoredProcedure);
+ Assert.Same(idPropertyUpdateParameter.StoredProcedure, idPropertyUpdateParameter.Table);
+ Assert.Same(idPropertyUpdateParameterMapping, idPropertyUpdateParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Empty(idProperty.GetUpdateStoredProcedureResultColumnMappings());
+ Assert.Equal(5, idProperty.GetUpdateStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[] { StoreObjectIdentifier.UpdateStoredProcedure(customerUpdateSproc.Name, customerUpdateSproc.Schema) },
+ idProperty.GetMappedStoreObjects(StoreObjectType.UpdateStoredProcedure));
+
+ var idPropertyDeleteParameter = baseDeleteSproc.FindParameter(idProperty)!;
+ var idPropertyDeleteParameterMapping = idProperty.GetDeleteStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyDeleteParameter, baseDeleteSproc.FindParameter("DeleteId"));
+ Assert.Same(idPropertyDeleteParameter, idPropertyDeleteParameterMapping.Parameter);
+ Assert.Equal("DeleteId", idPropertyDeleteParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyDeleteParameter.StoreType);
+ Assert.Equal(ParameterDirection.Input, idPropertyDeleteParameter.Direction);
+ Assert.False(idPropertyDeleteParameter.IsNullable);
+ Assert.Same(baseDeleteSproc, idPropertyDeleteParameter.StoredProcedure);
+ Assert.Same(idPropertyDeleteParameter.StoredProcedure, idPropertyDeleteParameter.Table);
+ Assert.Same(idPropertyDeleteParameterMapping, idPropertyDeleteParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Equal(5, idProperty.GetDeleteStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[] { StoreObjectIdentifier.DeleteStoredProcedure(customerDeleteSproc.Name, customerDeleteSproc.Schema) },
+ idProperty.GetMappedStoreObjects(StoreObjectType.DeleteStoredProcedure));
+ }
+ else
+ {
+ Assert.Null(abstractBaseType.GetInsertStoredProcedure());
+ Assert.Null(abstractBaseType.GetUpdateStoredProcedure());
+ Assert.Null(abstractBaseType.GetDeleteStoredProcedure());
+
+ Assert.Equal("Customer_Insert", customerInsertSproc.Name);
+ Assert.Null(abstractCustomerType.GetInsertStoredProcedure());
+ Assert.Equal("SpecialCustomer_Insert", specialCustomerType.GetInsertStoredProcedure().Name);
+
+ Assert.False(specialCustomerInsertMapping.IncludesDerivedTypes);
+ Assert.NotSame(customerInsertSproc, specialCustomerInsertSproc);
+
+ Assert.Equal("Customer_Insert", customerInsertSproc.Name);
+ Assert.Empty(abstractCustomerType.GetInsertStoredProcedureMappings());
+ Assert.Equal(
+ "SpecialCustomer_Insert",
+ specialCustomerType.GetInsertStoredProcedureMappings().Single().StoreStoredProcedure
+ .Name);
+
+ Assert.Null(customerInsertSproc.Schema);
+ Assert.Equal(
+ new[] { nameof(Customer) },
+ customerInsertSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+ Assert.Null(customerInsertSproc.EntityTypeMappings.Single().IsSharedTablePrincipal);
+ Assert.Null(customerInsertSproc.EntityTypeMappings.Single().IsSplitEntityTypePrincipal);
+
+ Assert.Equal(
+ new[] { "InsertId", nameof(Customer.EnumValue), nameof(Customer.Name), nameof(Customer.SomeShort), "SpecialtyAk" },
+ customerInsertSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(customerInsertSproc.ResultColumns.Select(m => m.Name));
+
+ Assert.Equal("Customer_Update", customerUpdateSproc.Name);
+ Assert.Empty(abstractCustomerType.GetUpdateStoredProcedureMappings());
+ Assert.Equal(
+ "SpecialCustomer_Update",
+ specialCustomerType.GetUpdateStoredProcedureMappings().Single().StoreStoredProcedure
+ .Name);
+ Assert.Null(customerUpdateSproc.Schema);
+ Assert.Equal(
+ new[] { nameof(Customer) },
+ customerUpdateSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+ Assert.Null(customerUpdateSproc.EntityTypeMappings.Single().IsSharedTablePrincipal);
+ Assert.Null(customerUpdateSproc.EntityTypeMappings.Single().IsSplitEntityTypePrincipal);
+
+ Assert.Equal(
+ new[] { "UpdateId", nameof(Customer.EnumValue), nameof(Customer.Name), nameof(Customer.SomeShort) },
+ customerUpdateSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(customerUpdateSproc.ResultColumns.Select(m => m.Name));
+
+ Assert.Equal("Customer_Delete", customerDeleteSproc.Name);
+ Assert.Empty(abstractCustomerType.GetDeleteStoredProcedureMappings());
+ Assert.Equal(
+ "SpecialCustomer_Delete",
+ specialCustomerType.GetDeleteStoredProcedureMappings().Single().StoreStoredProcedure
+ .Name);
+ Assert.Null(customerDeleteSproc.Schema);
+ Assert.Equal(
+ new[] { nameof(Customer) },
+ customerDeleteSproc.EntityTypeMappings.Select(m => m.EntityType.DisplayName()));
+ Assert.Null(customerDeleteSproc.EntityTypeMappings.Single().IsSharedTablePrincipal);
+ Assert.Null(customerDeleteSproc.EntityTypeMappings.Single().IsSplitEntityTypePrincipal);
+
+ Assert.Equal(
+ new[] { "DeleteId" },
+ customerDeleteSproc.Parameters.Select(m => m.Name));
+
+ Assert.Empty(customerDeleteSproc.ResultColumns.Select(m => m.Name));
+
+ Assert.Single(specialCustomerInsertSproc.EntityTypeMappings);
+
+ var abstractStringInsertParameter = specialCustomerInsertSproc.Parameters
+ .Single(c => c.Name == nameof(AbstractCustomer.AbstractString));
+ Assert.False(specialtyParameter.IsNullable);
+
+ var extraSpecialCustomerInsertSproc =
+ extraSpecialCustomerType.GetInsertStoredProcedureMappings().Select(t => t.StoreStoredProcedure)
+ .First(t => t.Name == "ExtraSpecialCustomer_Insert");
+
+ Assert.Single(extraSpecialCustomerInsertSproc.EntityTypeMappings);
+
+ var idPropertyInsertColumn = customerInsertSproc.FindParameter(idProperty)!;
+ var idPropertyInsertColumnMapping = idProperty.GetInsertStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyInsertColumn, customerInsertSproc.FindParameter("InsertId"));
+ Assert.Same(idPropertyInsertColumn, idPropertyInsertColumnMapping.Column);
+ Assert.Equal("InsertId", idPropertyInsertColumn.Name);
+ Assert.Equal("default_int_mapping", idPropertyInsertColumn.StoreType);
+ Assert.False(idPropertyInsertColumn.IsNullable);
+ Assert.Same(customerInsertSproc, idPropertyInsertColumn.StoredProcedure);
+ Assert.Same(idPropertyInsertColumn.StoredProcedure, idPropertyInsertColumn.Table);
+ Assert.Same(idPropertyInsertColumnMapping, idPropertyInsertColumn.FindColumnMapping(abstractBaseType));
+
+ Assert.Empty(idProperty.GetInsertStoredProcedureResultColumnMappings());
+ Assert.Equal(3, idProperty.GetInsertStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.InsertStoredProcedure(customerInsertSproc.Name, customerInsertSproc.Schema),
+ StoreObjectIdentifier.InsertStoredProcedure(
+ specialCustomerInsertSproc.Name, specialCustomerInsertSproc.Schema),
+ StoreObjectIdentifier.InsertStoredProcedure(
+ extraSpecialCustomerInsertSproc.Name, extraSpecialCustomerInsertSproc.Schema)
+ },
+ idProperty.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure));
+
+ var extraSpecialCustomerUpdateSproc =
+ extraSpecialCustomerType.GetUpdateStoredProcedureMappings().Select(t => t.StoreStoredProcedure)
+ .First(t => t.Name == "ExtraSpecialCustomer_Update");
+
+ Assert.Single(extraSpecialCustomerUpdateSproc.EntityTypeMappings);
+
+ var idPropertyUpdateParameter = customerUpdateSproc.FindParameter(idProperty)!;
+ var idPropertyUpdateParameterMapping = idProperty.GetUpdateStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyUpdateParameter, customerUpdateSproc.FindParameter("UpdateId"));
+ Assert.Same(idPropertyUpdateParameter, idPropertyUpdateParameterMapping.Parameter);
+ Assert.Equal("UpdateId", idPropertyUpdateParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyUpdateParameter.StoreType);
+ Assert.Equal(ParameterDirection.Input, idPropertyUpdateParameter.Direction);
+ Assert.False(idPropertyUpdateParameter.IsNullable);
+ Assert.Same(customerUpdateSproc, idPropertyUpdateParameter.StoredProcedure);
+ Assert.Same(idPropertyUpdateParameter.StoredProcedure, idPropertyUpdateParameter.Table);
+ Assert.Same(idPropertyUpdateParameterMapping, idPropertyUpdateParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Empty(idProperty.GetUpdateStoredProcedureResultColumnMappings());
+ Assert.Equal(3, idProperty.GetUpdateStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.UpdateStoredProcedure(customerUpdateSproc.Name, customerUpdateSproc.Schema),
+ StoreObjectIdentifier.UpdateStoredProcedure(
+ specialCustomerUpdateSproc.Name, specialCustomerUpdateSproc.Schema),
+ StoreObjectIdentifier.UpdateStoredProcedure(
+ extraSpecialCustomerUpdateSproc.Name, extraSpecialCustomerUpdateSproc.Schema)
+ },
+ idProperty.GetMappedStoreObjects(StoreObjectType.UpdateStoredProcedure));
+
+ var extraSpecialCustomerDeleteSproc =
+ extraSpecialCustomerType.GetDeleteStoredProcedureMappings().Select(t => t.StoreStoredProcedure)
+ .First(t => t.Name == "ExtraSpecialCustomer_Delete");
+
+ Assert.Single(extraSpecialCustomerDeleteSproc.EntityTypeMappings);
+
+ var idPropertyDeleteParameter = customerDeleteSproc.FindParameter(idProperty)!;
+ var idPropertyDeleteParameterMapping = idProperty.GetDeleteStoredProcedureParameterMappings().First();
+ Assert.Same(idPropertyDeleteParameter, customerDeleteSproc.FindParameter("DeleteId"));
+ Assert.Same(idPropertyDeleteParameter, idPropertyDeleteParameterMapping.Parameter);
+ Assert.Equal("DeleteId", idPropertyDeleteParameter.Name);
+ Assert.Equal("default_int_mapping", idPropertyDeleteParameter.StoreType);
+ Assert.Equal(ParameterDirection.Input, idPropertyDeleteParameter.Direction);
+ Assert.False(idPropertyDeleteParameter.IsNullable);
+ Assert.Same(customerDeleteSproc, idPropertyDeleteParameter.StoredProcedure);
+ Assert.Same(idPropertyDeleteParameter.StoredProcedure, idPropertyDeleteParameter.Table);
+ Assert.Same(idPropertyDeleteParameterMapping, idPropertyDeleteParameter.FindParameterMapping(abstractBaseType));
+
+ Assert.Equal(3, idProperty.GetDeleteStoredProcedureParameterMappings().Count());
+ Assert.Equal(
+ new[]
+ {
+ StoreObjectIdentifier.DeleteStoredProcedure(customerDeleteSproc.Name, customerDeleteSproc.Schema),
+ StoreObjectIdentifier.DeleteStoredProcedure(
+ specialCustomerDeleteSproc.Name, specialCustomerDeleteSproc.Schema),
+ StoreObjectIdentifier.DeleteStoredProcedure(
+ extraSpecialCustomerDeleteSproc.Name, extraSpecialCustomerDeleteSproc.Schema)
+ },
+ idProperty.GetMappedStoreObjects(StoreObjectType.DeleteStoredProcedure));
+ }
+ }
+ }
+
+ private IRelationalModel CreateTestModel(
+ bool mapToTables = false,
+ bool mapToViews = false,
+ bool mapToSprocs = false,
+ Mapping mapping = Mapping.TPH)
{
var modelBuilder = CreateConventionModelBuilder();
@@ -1005,6 +1863,50 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
{
cb.ToTable(_ => { });
}
+
+ if (mapToSprocs)
+ {
+ if (mapping == Mapping.TPH)
+ {
+ cb
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasResultColumn(b => b.Id, p => p.HasName("InsertId"))
+ .HasParameter("Discriminator")
+ .HasParameter((SpecialCustomer c) => c.Specialty)
+ .HasParameter((SpecialCustomer c) => c.RelatedCustomerSpecialty)
+ .HasParameter("SpecialtyAk")
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter((Customer c) => c.EnumValue)
+ .HasParameter((Customer c) => c.Name)
+ .HasParameter((Customer c) => c.SomeShort)
+ .HasParameter((AbstractCustomer c) => c.AbstractString))
+ .UpdateUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("UpdateId"))
+ .HasParameter((SpecialCustomer c) => c.Specialty)
+ .HasParameter((SpecialCustomer c) => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter((Customer c) => c.EnumValue)
+ .HasParameter((Customer c) => c.Name)
+ .HasParameter((Customer c) => c.SomeShort)
+ .HasParameter((AbstractCustomer c) => c.AbstractString))
+ .DeleteUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("DeleteId")));
+ }
+ else
+ {
+ cb
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasParameter(b => b.Id, p => p.IsOutput().HasName("InsertId"))
+ .HasParameter("SpecialtyAk"))
+ .UpdateUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("UpdateId"))
+ .HasParameter("SpecialtyAk"))
+ .DeleteUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("DeleteId")));
+ }
+ }
}
if (mapping == Mapping.TPC)
@@ -1036,6 +1938,35 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
{
cb.ToTable("Customer");
}
+
+ if (mapToSprocs)
+ {
+ cb
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasParameter(c => c.Id, p => p.HasName("InsertId"))
+ .HasParameter(c => c.EnumValue)
+ .HasParameter(c => c.Name)
+ .HasParameter(c => c.SomeShort))
+ .UpdateUsingStoredProcedure(
+ s => s
+ .HasParameter(b => b.Id, p => p.HasName("UpdateId"))
+ .HasParameter(c => c.EnumValue)
+ .HasParameter(c => c.Name)
+ .HasParameter(c => c.SomeShort))
+ .DeleteUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("DeleteId")));
+
+ if (mapping == Mapping.TPC)
+ {
+ cb.InsertUsingStoredProcedure(s => s.HasParameter("SpecialtyAk"));
+ }
+ else
+ {
+ cb.InsertUsingStoredProcedure(
+ s => s.HasParameter(c => c.Id, p => p.IsOutput()));
+ }
+ }
}
});
@@ -1064,6 +1995,56 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
cb.ToTable("SpecialCustomer", "SpecialSchema");
}
}
+
+ if (mapToSprocs)
+ {
+ if (mapping == Mapping.TPC)
+ {
+ cb
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasParameter(b => b.Id, p => p.HasName("InsertId"))
+ .HasParameter(c => c.Specialty)
+ .HasParameter(c => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter("SpecialtyAk")
+ .HasParameter(c => c.EnumValue)
+ .HasParameter(c => c.Name)
+ .HasParameter(c => c.SomeShort)
+ .HasParameter(c => c.AbstractString))
+ .UpdateUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("UpdateId"))
+ .HasParameter(c => c.Specialty)
+ .HasParameter(c => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter(c => c.EnumValue)
+ .HasParameter(c => c.Name)
+ .HasParameter(c => c.SomeShort)
+ .HasParameter(c => c.AbstractString))
+ .DeleteUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("DeleteId")));
+ }
+ else if (mapping == Mapping.TPT)
+ {
+ cb
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasResultColumn(b => b.Id, p => p.HasName("InsertId"))
+ .HasParameter(c => c.Specialty)
+ .HasParameter(c => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter(c => c.AbstractString))
+ .UpdateUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("UpdateId"))
+ .HasParameter(c => c.Specialty)
+ .HasParameter(c => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter(c => c.AbstractString))
+ .DeleteUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("DeleteId")));
+ }
+ }
+
cb.HasCheckConstraint("Specialty", "[Specialty] IN ('Specialist', 'Generalist')");
cb.Property(s => s.Specialty).IsRequired();
@@ -1096,6 +2077,22 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
cb.OwnsOne(c => c.Details, cdb => cdb.ToTable("SpecialCustomer", "SpecialSchema"));
}
}
+
+ if (mapToSprocs)
+ {
+ cb.OwnsOne(
+ c => c.Details, cdb => cdb
+ .InsertUsingStoredProcedure("CustomerDetailsInsert", s => s
+ .HasParameter("SpecialCustomerId")
+ .HasParameter(b => b.BirthDay)
+ .HasParameter(b => b.Address))
+ .UpdateUsingStoredProcedure("CustomerDetailsUpdate", s => s
+ .HasParameter("SpecialCustomerId")
+ .HasParameter(b => b.BirthDay)
+ .HasParameter(b => b.Address))
+ .DeleteUsingStoredProcedure("CustomerDetailsDelete", s => s
+ .HasParameter("SpecialCustomerId")));
+ }
}
});
@@ -1113,6 +2110,43 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
{
cb.ToTable("ExtraSpecialCustomer", "ExtraSpecialSchema");
}
+
+ if (mapToSprocs)
+ {
+ if (mapping == Mapping.TPC)
+ {
+ cb
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasParameter(b => b.Id, p => p.HasName("InsertId"))
+ .HasParameter(c => c.Specialty)
+ .HasParameter(c => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter("SpecialtyAk")
+ .HasParameter(c => c.EnumValue)
+ .HasParameter(c => c.Name)
+ .HasParameter(c => c.SomeShort)
+ .HasParameter(c => c.AbstractString))
+ .UpdateUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("UpdateId"))
+ .HasParameter(c => c.Specialty)
+ .HasParameter(c => c.RelatedCustomerSpecialty)
+ .HasParameter("AnotherRelatedCustomerId")
+ .HasParameter(c => c.EnumValue)
+ .HasParameter(c => c.Name)
+ .HasParameter(c => c.SomeShort)
+ .HasParameter(c => c.AbstractString))
+ .DeleteUsingStoredProcedure(
+ s => s.HasParameter(b => b.Id, p => p.HasName("DeleteId")));
+ }
+ else if (mapping == Mapping.TPT)
+ {
+ cb
+ .InsertUsingStoredProcedure(s => s.HasResultColumn(b => b.Id))
+ .UpdateUsingStoredProcedure(s => s.HasParameter(b => b.Id))
+ .DeleteUsingStoredProcedure(s => s.HasParameter(b => b.Id));
+ }
+ }
}
if (mapping == Mapping.TPC)
@@ -1129,6 +2163,25 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
{
cb.OwnsOne(c => c.Details, cdb => cdb.ToTable("ExtraSpecialCustomer", "ExtraSpecialSchema"));
}
+
+ if (mapToSprocs)
+ {
+ cb.OwnsOne(
+ c => c.Details, cdb => cdb
+ .InsertUsingStoredProcedure(
+ "CustomerDetailsInsert", s => s
+ .HasParameter("ExtraSpecialCustomerId")
+ .HasParameter(b => b.BirthDay)
+ .HasParameter(b => b.Address))
+ .UpdateUsingStoredProcedure(
+ "CustomerDetailsUpdate", s => s
+ .HasParameter("ExtraSpecialCustomerId")
+ .HasParameter(b => b.BirthDay)
+ .HasParameter(b => b.Address))
+ .DeleteUsingStoredProcedure(
+ "CustomerDetailsDelete", s => s
+ .HasParameter("ExtraSpecialCustomerId")));
+ }
}
});
@@ -1143,10 +2196,25 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
.HasForeignKey(o => o.OrderDate).HasPrincipalKey(o => o.Date)
.HasConstraintName("FK_DateDetails");
- // Note: the below is resetting the name of the anonymous index
- // created in HasForeignKey() above, not creating a new index.
ob.HasIndex(o => o.OrderDate).HasDatabaseName("IX_OrderDate");
+ if (mapToSprocs)
+ {
+ ob
+ .InsertUsingStoredProcedure(
+ s => s
+ .HasResultColumn(c => c.Id)
+ .HasParameter(c => c.AlternateId)
+ .HasParameter(c => c.CustomerId)
+ .HasParameter(c => c.OrderDate))
+ .UpdateUsingStoredProcedure(
+ s => s
+ .HasParameter(c => c.Id)
+ .HasParameter(c => c.CustomerId)
+ .HasParameter(c => c.OrderDate))
+ .DeleteUsingStoredProcedure(s => s.HasParameter(b => b.Id));
+ }
+
ob.OwnsOne(
o => o.Details, odb =>
{
@@ -1159,8 +2227,61 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie
odb.HasOne(od => od.DateDetails).WithOne()
.HasForeignKey(o => o.OrderDate).HasPrincipalKey(o => o.Date);
- odb.OwnsOne(od => od.BillingAddress);
- odb.OwnsOne(od => od.ShippingAddress);
+ if (mapToSprocs)
+ {
+ odb
+ .InsertUsingStoredProcedure(
+ "OrderDetails_Insert", s => s
+ .HasParameter(c => c.OrderId)
+ .HasParameter(c => c.AlternateId)
+ .HasParameter(c => c.Active)
+ .HasParameter(c => c.OrderDate))
+ .UpdateUsingStoredProcedure(
+ "OrderDetails_Update", s => s
+ .HasParameter(c => c.OrderId)
+ .HasParameter(c => c.Active)
+ .HasParameter(c => c.OrderDate))
+ .DeleteUsingStoredProcedure(
+ "OrderDetails_Delete", s => s
+ .HasParameter(b => b.OrderId));
+
+ odb.OwnsOne(
+ od => od.BillingAddress, bab => bab
+ .InsertUsingStoredProcedure(
+ "BillingAddress_Insert", s => s
+ .HasParameter(c => c.City)
+ .HasParameter(c => c.Street)
+ .HasParameter("OrderDetailsOrderId"))
+ .UpdateUsingStoredProcedure(
+ "BillingAddress_Update", s => s
+ .HasParameter(c => c.City)
+ .HasParameter(c => c.Street)
+ .HasParameter("OrderDetailsOrderId"))
+ .DeleteUsingStoredProcedure(
+ "BillingAddress_Delete", s => s
+ .HasParameter("OrderDetailsOrderId")));
+
+ odb.OwnsOne(
+ od => od.ShippingAddress, sab => sab
+ .InsertUsingStoredProcedure(
+ "ShippingAddress_Insert", s => s
+ .HasParameter("OrderDetailsOrderId")
+ .HasParameter(c => c.City)
+ .HasParameter(c => c.Street))
+ .UpdateUsingStoredProcedure(
+ "ShippingAddress_Update", s => s
+ .HasParameter("OrderDetailsOrderId")
+ .HasParameter(c => c.City)
+ .HasParameter(c => c.Street))
+ .DeleteUsingStoredProcedure(
+ "ShippingAddress_Delete", s => s
+ .HasParameter("OrderDetailsOrderId")));
+ }
+ else
+ {
+ odb.OwnsOne(od => od.BillingAddress);
+ odb.OwnsOne(od => od.ShippingAddress);
+ }
odb.Navigation(od => od.BillingAddress).IsRequired();
odb.Navigation(od => od.ShippingAddress).IsRequired();
});
diff --git a/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs b/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs
index df861550b26..ba7efa3b850 100644
--- a/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs
+++ b/test/EFCore.Relational.Tests/ModelBuilding/RelationalModelBuilderTest.cs
@@ -14,6 +14,8 @@ public abstract class RelationalNonRelationshipTestBase : NonRelationshipTestBas
public virtual void Can_use_table_splitting()
{
var modelBuilder = CreateModelBuilder();
+ modelBuilder.HasDefaultSchema("dbo");
+
modelBuilder.Entity().SplitToTable(
"OrderDetails", s =>
{
@@ -40,15 +42,15 @@ public virtual void Can_use_table_splitting()
var entity = model.FindEntityType(typeof(Order))!;
Assert.False(entity.IsTableExcludedFromMigrations());
- Assert.False(entity.IsTableExcludedFromMigrations(StoreObjectIdentifier.Table("Order")));
- Assert.True(entity.IsTableExcludedFromMigrations(StoreObjectIdentifier.Table("OrderDetails")));
- Assert.Same(entity.GetMappingFragments().Single(), entity.FindMappingFragment(StoreObjectIdentifier.Table("OrderDetails")));
+ Assert.False(entity.IsTableExcludedFromMigrations(StoreObjectIdentifier.Table("Order", "dbo")));
+ Assert.True(entity.IsTableExcludedFromMigrations(StoreObjectIdentifier.Table("OrderDetails", "dbo")));
+ Assert.Same(entity.GetMappingFragments().Single(), entity.FindMappingFragment(StoreObjectIdentifier.Table("OrderDetails", "dbo")));
var customerId = entity.FindProperty(nameof(Order.CustomerId))!;
Assert.Equal("CustomerId", customerId.GetColumnName());
- Assert.Null(customerId.GetColumnName(StoreObjectIdentifier.Table("Order")));
- Assert.Equal("id", customerId.GetColumnName(StoreObjectIdentifier.Table("OrderDetails")));
- Assert.Same(customerId.GetOverrides().Single(), customerId.FindOverrides(StoreObjectIdentifier.Table("OrderDetails")));
+ Assert.Null(customerId.GetColumnName(StoreObjectIdentifier.Table("Order", "dbo")));
+ Assert.Equal("id", customerId.GetColumnName(StoreObjectIdentifier.Table("OrderDetails", "dbo")));
+ Assert.Same(customerId.GetOverrides().Single(), customerId.FindOverrides(StoreObjectIdentifier.Table("OrderDetails", "dbo")));
}
[ConditionalFact]
@@ -201,6 +203,7 @@ public virtual void Can_use_table_splitting()
public virtual void Sproc_overrides_update_when_renamed_in_TPH()
{
var modelBuilder = CreateModelBuilder();
+ modelBuilder.HasDefaultSchema("mySchema");
modelBuilder.Ignore();
modelBuilder.Ignore();
@@ -246,9 +249,13 @@ public virtual void Sproc_overrides_update_when_renamed_in_TPH()
var bookLabel = model.FindEntityType(typeof(BookLabel))!;
var insertSproc = bookLabel.GetInsertStoredProcedure()!;
Assert.Equal("Insert", insertSproc.Name);
- Assert.Null(insertSproc.Schema);
+ Assert.Equal("mySchema", insertSproc.Schema);
Assert.Equal(new[] { "BookId", "Discriminator" }, insertSproc.Parameters);
Assert.Equal(new[] { "Id" }, insertSproc.ResultColumns);
+ Assert.True(insertSproc.ContainsParameter("Discriminator"));
+ Assert.False(insertSproc.ContainsParameter("Id"));
+ Assert.False(insertSproc.ContainsResultColumn("Discriminator"));
+ Assert.True(insertSproc.ContainsResultColumn("Id"));
Assert.True(insertSproc.AreTransactionsSuppressed);
Assert.Equal("bar1", insertSproc["foo"]);
Assert.Same(bookLabel, insertSproc.EntityType);
@@ -264,7 +271,7 @@ public virtual void Sproc_overrides_update_when_renamed_in_TPH()
var deleteSproc = bookLabel.GetDeleteStoredProcedure()!;
Assert.Equal("BookLabel_Delete", deleteSproc.Name);
- Assert.Null(deleteSproc.Schema);
+ Assert.Equal("mySchema", deleteSproc.Schema);
Assert.Equal(new[] { "Id" }, deleteSproc.Parameters);
Assert.Empty(deleteSproc.ResultColumns);
Assert.True(deleteSproc.AreTransactionsSuppressed);
@@ -772,6 +779,10 @@ public abstract class TestSplitTableBuilder
public abstract TestColumnBuilder Property(string propertyName);
public abstract TestColumnBuilder Property(Expression> propertyExpression);
+
+ public abstract TestSplitTableBuilder HasAnnotation(
+ string annotation,
+ object? value);
}
public class GenericTestSplitTableBuilder : TestSplitTableBuilder, IInfrastructure>
@@ -807,6 +818,9 @@ public override TestColumnBuilder Property(string property
public override TestColumnBuilder Property(Expression> propertyExpression)
=> new GenericTestColumnBuilder(TableBuilder.Property(propertyExpression));
+
+ public override TestSplitTableBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(TableBuilder.HasAnnotation(annotation, value));
}
public class NonGenericTestSplitTableBuilder : TestSplitTableBuilder, IInfrastructure
@@ -842,6 +856,9 @@ public override TestColumnBuilder Property(string property
public override TestColumnBuilder Property(Expression> propertyExpression)
=> new NonGenericTestColumnBuilder(TableBuilder.Property(propertyExpression.GetPropertyAccess().Name));
+
+ public override TestSplitTableBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(TableBuilder.HasAnnotation(annotation, value));
}
public abstract class TestOwnedNavigationSplitTableBuilder
@@ -857,6 +874,10 @@ public abstract class TestOwnedNavigationSplitTableBuilder Property(string propertyName);
public abstract TestColumnBuilder Property(Expression> propertyExpression);
+
+ public abstract TestOwnedNavigationSplitTableBuilder HasAnnotation(
+ string annotation,
+ object? value);
}
public class GenericTestOwnedNavigationSplitTableBuilder :
@@ -894,6 +915,9 @@ public override TestColumnBuilder Property(string property
public override TestColumnBuilder Property(Expression> propertyExpression)
=> new GenericTestColumnBuilder(TableBuilder.Property(propertyExpression));
+
+ public override TestOwnedNavigationSplitTableBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(TableBuilder.HasAnnotation(annotation, value));
}
public class NonGenericTestOwnedNavigationSplitTableBuilder :
@@ -930,11 +954,18 @@ public override TestColumnBuilder Property(string property
public override TestColumnBuilder Property(Expression> propertyExpression)
=> new GenericTestColumnBuilder(TableBuilder.Property(propertyExpression.GetPropertyAccess().Name));
+
+ public override TestOwnedNavigationSplitTableBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(TableBuilder.HasAnnotation(annotation, value));
}
public abstract class TestColumnBuilder
{
public abstract TestColumnBuilder HasColumnName(string? name);
+
+ public abstract TestColumnBuilder HasAnnotation(
+ string annotation,
+ object? value);
}
public class GenericTestColumnBuilder : TestColumnBuilder, IInfrastructure>
@@ -954,6 +985,11 @@ protected virtual TestColumnBuilder Wrap(ColumnBuilder col
public override TestColumnBuilder HasColumnName(string? name)
=> Wrap(ColumnBuilder.HasColumnName(name));
+
+ public override TestColumnBuilder HasAnnotation(
+ string annotation,
+ object? value)
+ => Wrap(ColumnBuilder.HasAnnotation(annotation, value));
}
public class NonGenericTestColumnBuilder : TestColumnBuilder, IInfrastructure
@@ -973,6 +1009,11 @@ protected virtual TestColumnBuilder Wrap(ColumnBuilder tableBuilder)
public override TestColumnBuilder HasColumnName(string? name)
=> Wrap(ColumnBuilder.HasColumnName(name));
+
+ public override TestColumnBuilder HasAnnotation(
+ string annotation,
+ object? value)
+ => Wrap(ColumnBuilder.HasAnnotation(annotation, value));
}
public abstract class TestViewBuilder
@@ -1137,6 +1178,10 @@ public abstract class TestSplitViewBuilder
public abstract TestViewColumnBuilder Property(string propertyName);
public abstract TestViewColumnBuilder Property(Expression> propertyExpression);
+
+ public abstract TestSplitViewBuilder HasAnnotation(
+ string annotation,
+ object? value);
}
public class GenericTestSplitViewBuilder : TestSplitViewBuilder, IInfrastructure>
@@ -1166,6 +1211,9 @@ public override TestViewColumnBuilder Property(string prop
public override TestViewColumnBuilder Property(Expression> propertyExpression)
=> new GenericTestViewColumnBuilder(ViewBuilder.Property(propertyExpression));
+
+ public override TestSplitViewBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(ViewBuilder.HasAnnotation(annotation, value));
}
public class NonGenericTestSplitViewBuilder : TestSplitViewBuilder, IInfrastructure
@@ -1195,6 +1243,9 @@ public override TestViewColumnBuilder Property(string prop
public override TestViewColumnBuilder Property(Expression> propertyExpression)
=> new NonGenericTestViewColumnBuilder(ViewBuilder.Property(propertyExpression.GetPropertyAccess().Name));
+
+ public override TestSplitViewBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(ViewBuilder.HasAnnotation(annotation, value));
}
public abstract class TestOwnedNavigationSplitViewBuilder
@@ -1209,6 +1260,10 @@ public abstract class TestOwnedNavigationSplitViewBuilder