diff --git a/src/EFCore.PG/Design/Internal/NpgsqlAnnotationCodeGenerator.cs b/src/EFCore.PG/Design/Internal/NpgsqlAnnotationCodeGenerator.cs
index 25b2381c0..5ad63448b 100644
--- a/src/EFCore.PG/Design/Internal/NpgsqlAnnotationCodeGenerator.cs
+++ b/src/EFCore.PG/Design/Internal/NpgsqlAnnotationCodeGenerator.cs
@@ -75,7 +75,7 @@ public override MethodCallCodeFragment GenerateFluentApi(IModel model, IAnnotati
extension.Name);
}
- if (annotation.Name.StartsWith(NpgsqlAnnotationNames.EnumPrefix))
+ if (annotation.Name.StartsWith(NpgsqlAnnotationNames.EnumPrefix, StringComparison.Ordinal))
{
var enumTypeDef = new PostgresEnum(model, annotation.Name);
diff --git a/src/EFCore.PG/Metadata/PostgresEnum.cs b/src/EFCore.PG/Metadata/PostgresEnum.cs
index 929da007c..c42566ef6 100644
--- a/src/EFCore.PG/Metadata/PostgresEnum.cs
+++ b/src/EFCore.PG/Metadata/PostgresEnum.cs
@@ -9,43 +9,101 @@
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata
{
+ ///
+ /// Represents the metadata for a PostgreSQL enum.
+ ///
+ [PublicAPI]
public class PostgresEnum
{
- readonly IAnnotatable _annotatable;
- readonly string _annotationName;
-
- internal PostgresEnum(IAnnotatable annotatable, string annotationName)
+ [NotNull] readonly IAnnotatable _annotatable;
+ [NotNull] readonly string _annotationName;
+
+ ///
+ /// Creates a .
+ ///
+ /// The annotatable to search for the annotation.
+ /// The annotation name to search for in the annotatable.
+ ///
+ ///
+ internal PostgresEnum([NotNull] IAnnotatable annotatable, [NotNull] string annotationName)
{
- _annotatable = annotatable;
- _annotationName = annotationName;
+ _annotatable = Check.NotNull(annotatable, nameof(annotatable));
+ _annotationName = Check.NotNull(annotationName, nameof(annotationName));
}
+ ///
+ /// Gets or adds a from or to the .
+ ///
+ /// The annotatable from which to get or add the enum.
+ /// The enum schema or null to use the model's default schema.
+ /// The enum name.
+ /// The enum labels.
+ ///
+ /// The from the .
+ ///
+ ///
+ ///
+ ///
+ ///
+ [NotNull]
public static PostgresEnum GetOrAddPostgresEnum(
[NotNull] IMutableAnnotatable annotatable,
[CanBeNull] string schema,
[NotNull] string name,
[NotNull] string[] labels)
{
+ Check.NotNull(annotatable, nameof(annotatable));
+ Check.NullButNotEmpty(schema, nameof(schema));
+ Check.NotEmpty(name, nameof(name));
+ Check.NotNull(labels, nameof(labels));
+
if (FindPostgresEnum(annotatable, schema, name) is PostgresEnum enumType)
return enumType;
- enumType = new PostgresEnum(annotatable, BuildAnnotationName(schema, name));
- enumType.SetData(labels);
- return enumType;
+ var annotationName = BuildAnnotationName(schema, name);
+
+ return new PostgresEnum(annotatable, annotationName) { Labels = labels };
}
+ ///
+ /// Gets or adds a from or to the .
+ ///
+ /// The annotatable from which to get or add the enum.
+ /// The enum name.
+ /// The enum labels.
+ ///
+ /// The from the .
+ ///
+ ///
+ ///
+ ///
+ [NotNull]
public static PostgresEnum GetOrAddPostgresEnum(
[NotNull] IMutableAnnotatable annotatable,
[NotNull] string name,
[NotNull] string[] labels)
=> GetOrAddPostgresEnum(annotatable, null, name, labels);
+ ///
+ /// Finds a in the , or returns null if not found.
+ ///
+ /// The annotatable to search for the enum.
+ /// The enum schema or null to use the model's default schema.
+ /// The enum name.
+ ///
+ /// The from the .
+ ///
+ ///
+ ///
+ ///
+ [CanBeNull]
public static PostgresEnum FindPostgresEnum(
[NotNull] IAnnotatable annotatable,
[CanBeNull] string schema,
[NotNull] string name)
{
Check.NotNull(annotatable, nameof(annotatable));
+ Check.NullButNotEmpty(schema, nameof(schema));
Check.NotEmpty(name, nameof(name));
var annotationName = BuildAnnotationName(schema, name);
@@ -53,50 +111,72 @@ public static PostgresEnum FindPostgresEnum(
return annotatable[annotationName] == null ? null : new PostgresEnum(annotatable, annotationName);
}
+ [NotNull]
static string BuildAnnotationName(string schema, string name)
- => NpgsqlAnnotationNames.EnumPrefix + (schema == null ? name : schema + '.' + name);
-
+ => schema != null
+ ? $"{NpgsqlAnnotationNames.EnumPrefix}{schema}.{name}"
+ : $"{NpgsqlAnnotationNames.EnumPrefix}{name}";
+
+ ///
+ /// Gets the collection of stored in the .
+ ///
+ /// The annotatable to search for annotations.
+ ///
+ /// The collection of stored in the .
+ ///
+ ///
+ [NotNull]
+ [ItemNotNull]
public static IEnumerable GetPostgresEnums([NotNull] IAnnotatable annotatable)
- {
- Check.NotNull(annotatable, nameof(annotatable));
-
- return annotatable.GetAnnotations()
- .Where(a => a.Name.StartsWith(NpgsqlAnnotationNames.EnumPrefix, StringComparison.Ordinal))
- .Select(a => new PostgresEnum(annotatable, a.Name));
- }
-
+ => Check.NotNull(annotatable, nameof(annotatable))
+ .GetAnnotations()
+ .Where(a => a.Name.StartsWith(NpgsqlAnnotationNames.EnumPrefix, StringComparison.Ordinal))
+ .Select(a => new PostgresEnum(annotatable, a.Name));
+
+ ///
+ /// The that stores the enum.
+ ///
+ [NotNull]
public Annotatable Annotatable => (Annotatable)_annotatable;
+ ///
+ /// The enum schema or null to represent the default schema.
+ ///
+ [CanBeNull]
public string Schema => GetData().Schema;
+ ///
+ /// The enum name.
+ ///
+ [NotNull]
public string Name => GetData().Name;
- public string[] Labels
+ ///
+ /// The enum labels.
+ ///
+ [NotNull]
+ public IReadOnlyList Labels
{
get => GetData().Labels;
set => SetData(value);
}
(string Schema, string Name, string[] Labels) GetData()
- {
- return !(Annotatable[_annotationName] is string annotationValue)
- ? (null, null, null)
- : Deserialize(_annotationName, annotationValue);
- }
+ => Deserialize(Annotatable.FindAnnotation(_annotationName));
- void SetData(string[] labels)
+ void SetData([NotNull] IEnumerable labels)
=> Annotatable[_annotationName] = string.Join(",", labels);
- static (string schema, string name, string[] labels) Deserialize(
- [NotNull] string annotationName,
- [NotNull] string annotationValue)
+ static (string Schema, string Name, string[] Labels) Deserialize([CanBeNull] IAnnotation annotation)
{
- Check.NotEmpty(annotationValue, nameof(annotationValue));
+ if (annotation == null || !(annotation.Value is string value) || string.IsNullOrEmpty(value))
+ return (null, null, null);
- var labels = annotationValue.Split(',').ToArray();
+ var labels = value.Split(',');
+ // TODO: This would be a safer operation if we stored schema and name in the annotation value (see Sequence.cs).
// Yes, this doesn't support dots in the schema/enum name, let somebody complain first.
- var schemaAndName = annotationName.Substring(NpgsqlAnnotationNames.EnumPrefix.Length).Split('.');
+ var schemaAndName = annotation.Name.Substring(NpgsqlAnnotationNames.EnumPrefix.Length).Split('.');
switch (schemaAndName.Length)
{
case 1:
@@ -104,7 +184,7 @@ void SetData(string[] labels)
case 2:
return (schemaAndName[0], schemaAndName[1], labels);
default:
- throw new ArgumentException("Cannot parse enum name from annotation: " + annotationName);
+ throw new ArgumentException($"Cannot parse enum name from annotation: {annotation.Name}");
}
}
}
diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs
index bfc10b98e..9d335b33c 100644
--- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs
+++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs
@@ -638,6 +638,7 @@ protected override void Generate(
MigrationCommandListBuilder builder)
{
Check.NotNull(operation, nameof(operation));
+ Check.NotNull(model, nameof(model));
Check.NotNull(builder, nameof(builder));
GenerateEnumStatements(operation, model, builder);
@@ -691,7 +692,7 @@ protected virtual void GenerateEnumStatements(
foreach (var enumTypeToDrop in operation.Npgsql().OldPostgresEnums
.Where(oe => operation.Npgsql().PostgresEnums.All(ne => ne.Name != oe.Name)))
{
- GenerateDropEnum(enumTypeToDrop, operation.OldDatabase, builder);
+ GenerateDropEnum(enumTypeToDrop, model, builder);
}
// TODO: Some forms of enum alterations are actually supported...
@@ -708,7 +709,7 @@ protected virtual void GenerateCreateEnum(
[NotNull] IModel model,
[NotNull] MigrationCommandListBuilder builder)
{
- var schema = GetSchemaOrDefault(enumType.Schema, model);
+ var schema = enumType.Schema ?? model.Relational().DefaultSchema;
// Schemas are normally created (or rather ensured) by the model differ, which scans all tables, sequences
// and other database objects. However, it isn't aware of enums, so we always ensure schema on enum creation.
@@ -723,10 +724,10 @@ protected virtual void GenerateCreateEnum(
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));
var labels = enumType.Labels;
- for (var i = 0; i < labels.Length; i++)
+ for (var i = 0; i < labels.Count; i++)
{
builder.Append(stringTypeMapping.GenerateSqlLiteral(labels[i]));
- if (i < labels.Length - 1)
+ if (i < labels.Count - 1)
builder.Append(", ");
}
@@ -735,10 +736,10 @@ protected virtual void GenerateCreateEnum(
protected virtual void GenerateDropEnum(
[NotNull] PostgresEnum enumType,
- [CanBeNull] IAnnotatable oldDatabase,
+ [NotNull] IModel model,
[NotNull] MigrationCommandListBuilder builder)
{
- var schema = GetSchemaOrDefault(enumType.Schema, oldDatabase);
+ var schema = enumType.Schema ?? model.Relational().DefaultSchema;
builder
.Append("DROP TYPE ")
@@ -1117,10 +1118,6 @@ static string GenerateStorageParameterValue(object value)
#region Helpers
- [CanBeNull]
- static string GetSchemaOrDefault([CanBeNull] string schema, [CanBeNull] IAnnotatable model)
- => schema ?? model?.FindAnnotation(RelationalAnnotationNames.DefaultSchema)?.Value as string;
-
///
/// True if is null, greater than, or equal to the specified version.
///