Refactoring
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using AMWD.Common.EntityFrameworkCore.Attributes;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
#if NET5_0_OR_GREATER
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
#endif
|
||||
|
||||
namespace AMWD.Common.EntityFrameworkCore.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for the <see cref="ModelBuilder"/> of entity framework core.
|
||||
/// </summary>
|
||||
public static class ModelBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies indices and unique constraints to the properties.
|
||||
/// </summary>
|
||||
/// <param name="builder">The database model builder.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0019", Justification = "No pattern comparison in this case due to readability.")]
|
||||
public static ModelBuilder ApplyIndexAttributes(this ModelBuilder builder)
|
||||
{
|
||||
foreach (var entityType in builder.Model.GetEntityTypes())
|
||||
{
|
||||
foreach (var property in entityType.GetProperties())
|
||||
{
|
||||
var indexAttribute = entityType.ClrType
|
||||
.GetProperty(property.Name)
|
||||
?.GetCustomAttribute(typeof(DatabaseIndexAttribute), false) as DatabaseIndexAttribute;
|
||||
if (indexAttribute != null)
|
||||
{
|
||||
var index = entityType.AddIndex(property);
|
||||
index.IsUnique = indexAttribute.IsUnique;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(indexAttribute.Name))
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
index.SetDatabaseName(indexAttribute.Name.Trim());
|
||||
#else
|
||||
index.SetName(indexAttribute.Name.Trim());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts all table and column names to snake_case_names.
|
||||
/// </summary>
|
||||
/// <param name="builder">The database model builder.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static ModelBuilder ApplySnakeCase(this ModelBuilder builder)
|
||||
{
|
||||
foreach (var entityType in builder.Model.GetEntityTypes())
|
||||
{
|
||||
// skip conversion when table name is explicitly set
|
||||
if ((entityType.ClrType.GetCustomAttribute(typeof(TableAttribute), false) as TableAttribute) == null)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
entityType.SetTableName(ConvertToSnakeCase(entityType.GetTableName()));
|
||||
#else
|
||||
entityType.SetTableName(ConvertToSnakeCase(entityType.GetTableName()));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
var identifier = StoreObjectIdentifier.Table(entityType.GetTableName(), entityType.GetSchema());
|
||||
#endif
|
||||
foreach (var property in entityType.GetProperties())
|
||||
{
|
||||
// skip conversion when column name is explicitly set
|
||||
if ((entityType.ClrType.GetProperty(property.Name)?.GetCustomAttribute(typeof(ColumnAttribute), false) as ColumnAttribute) == null)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
property.SetColumnName(ConvertToSnakeCase(property.GetColumnName(identifier)));
|
||||
#else
|
||||
property.SetColumnName(ConvertToSnakeCase(property.GetColumnName()));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to its snake_case equivalent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Code borrowed from Npgsql.NameTranslation.NpgsqlSnakeCaseNameTranslator.
|
||||
/// See https://github.com/npgsql/npgsql/blob/f2b2c98f45df6d2a78eec00ae867f18944d717ca/src/Npgsql/NameTranslation/NpgsqlSnakeCaseNameTranslator.cs#L76-L136.
|
||||
/// </remarks>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
private static string ConvertToSnakeCase(string value)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var state = SnakeCaseState.Start;
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
if (value[i] == ' ')
|
||||
{
|
||||
if (state != SnakeCaseState.Start)
|
||||
state = SnakeCaseState.NewWord;
|
||||
}
|
||||
else if (char.IsUpper(value[i]))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SnakeCaseState.Upper:
|
||||
bool hasNext = (i + 1 < value.Length);
|
||||
if (i > 0 && hasNext)
|
||||
{
|
||||
char nextChar = value[i + 1];
|
||||
if (!char.IsUpper(nextChar) && nextChar != '_')
|
||||
{
|
||||
sb.Append('_');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SnakeCaseState.Lower:
|
||||
case SnakeCaseState.NewWord:
|
||||
sb.Append('_');
|
||||
break;
|
||||
}
|
||||
|
||||
sb.Append(char.ToLowerInvariant(value[i]));
|
||||
state = SnakeCaseState.Upper;
|
||||
}
|
||||
else if (value[i] == '_')
|
||||
{
|
||||
sb.Append('_');
|
||||
state = SnakeCaseState.Start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state == SnakeCaseState.NewWord)
|
||||
sb.Append('_');
|
||||
|
||||
sb.Append(value[i]);
|
||||
state = SnakeCaseState.Lower;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private enum SnakeCaseState
|
||||
{
|
||||
Start,
|
||||
Lower,
|
||||
Upper,
|
||||
NewWord
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user