Skip to content

Commit

Permalink
Add flag to ignore the MigrationHistory table from the naming convent…
Browse files Browse the repository at this point in the history
…ions

Closes #1
  • Loading branch information
EraYaN committed Oct 20, 2021
1 parent 9002c9b commit 5594847
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 32 deletions.
26 changes: 22 additions & 4 deletions EFCore.NamingConventions.Test/NameRewritingConventionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
Expand All @@ -33,6 +34,23 @@ public void Column()
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
}

//[Fact]
//public void ColumnInMigrationTable()
//{
// var entityType = BuildEntityType(b => b.Entity<HistoryRow>(build => new HistoryRow("Migration1", "7.0.0")));
// Assert.Equal("migration_id", entityType.FindProperty(nameof(HistoryRow.MigrationId))
// .GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
//}

//[Fact]
//public void ColumnInMigrationTableIgnored()
//{
// var entityType = BuildEntityType(b => new HistoryRow("Migration1", "7.0.0"), ignoreMigrationTable: true);
// Assert.Equal("MigrationId", entityType.FindProperty(nameof(HistoryRow.MigrationId))
// .GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
//}


[Fact]
public void Column_with_turkish_culture()
{
Expand Down Expand Up @@ -404,10 +422,10 @@ public void Owned_entity_without_table_splitting()
.GetColumnName(StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value));
}

private IEntityType BuildEntityType(Action<ModelBuilder> builderAction, CultureInfo culture = null)
=> BuildModel(builderAction, culture).GetEntityTypes().Single();
private IEntityType BuildEntityType(Action<ModelBuilder> builderAction, CultureInfo culture = null, bool ignoreMigrationTable = false)
=> BuildModel(builderAction, culture, ignoreMigrationTable).GetEntityTypes().Single();

private IModel BuildModel(Action<ModelBuilder> builderAction, CultureInfo culture = null)
private IModel BuildModel(Action<ModelBuilder> builderAction, CultureInfo culture = null, bool ignoreMigrationTable = false)
{
var conventionSet = SqliteTestHelpers
.Instance
Expand All @@ -417,7 +435,7 @@ private IModel BuildModel(Action<ModelBuilder> builderAction, CultureInfo cultur

var optionsBuilder = new DbContextOptionsBuilder();
SqliteTestHelpers.Instance.UseProviderOptions(optionsBuilder);
optionsBuilder.UseSnakeCaseNamingConvention(culture);
optionsBuilder.UseSnakeCaseNamingConvention(culture, ignoreMigrationTable);
var plugin = new NamingConventionSetPlugin(optionsBuilder.Options);
plugin.ModifyConventions(conventionSet);

Expand Down
14 changes: 12 additions & 2 deletions EFCore.NamingConventions/Internal/NameRewritingConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ private static readonly StoreObjectType[] _storeObjectTypes

private readonly INameRewriter _namingNameRewriter;

public NameRewritingConvention(INameRewriter nameRewriter)
=> _namingNameRewriter = nameRewriter;
private readonly bool _ignoreMigrationTable;

public NameRewritingConvention(INameRewriter nameRewriter, bool ignoreMigrationTable = false)
{
_namingNameRewriter = nameRewriter;
_ignoreMigrationTable = ignoreMigrationTable;
}

public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
Expand Down Expand Up @@ -233,6 +238,11 @@ private void RewriteColumnName(IConventionPropertyBuilder propertyBuilder)
// Remove any previous setting of the column name we may have done, so we can get the default recalculated below.
property.Builder.HasNoAnnotation(RelationalAnnotationNames.ColumnName);

if (_ignoreMigrationTable && entityType.ClrType.FullName == "Microsoft.EntityFrameworkCore.Migrations.HistoryRow")
{
return;
}

// TODO: The following is a temporary hack. We should probably just always set the relational override below,
// but https://github.com/dotnet/efcore/pull/23834
var baseColumnName = StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is { } tableIdentifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public ConventionSet ModifyConventions(ConventionSet conventionSet)
var extension = _options.FindExtension<NamingConventionsOptionsExtension>();
var namingStyle = extension.NamingConvention;
var culture = extension.Culture;
var ignoreMigrationTable = extension.IgnoreMigrationTable;
if (namingStyle == NamingConvention.None)
{
return conventionSet;
Expand All @@ -30,7 +31,7 @@ public ConventionSet ModifyConventions(ConventionSet conventionSet)
NamingConvention.UpperCase => new UpperCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.UpperSnakeCase => new UpperSnakeCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
_ => throw new ArgumentOutOfRangeException("Unhandled enum value: " + namingStyle)
});
}, ignoreMigrationTable);

conventionSet.EntityTypeAddedConventions.Add(convention);
conventionSet.EntityTypeAnnotationChangedConventions.Add(convention);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ public class NamingConventionsOptionsExtension : IDbContextOptionsExtension
private DbContextOptionsExtensionInfo _info;
private NamingConvention _namingConvention;
private CultureInfo _culture;
private bool _ignoreMigrationTable;

public NamingConventionsOptionsExtension() {}
protected NamingConventionsOptionsExtension([NotNull] NamingConventionsOptionsExtension copyFrom)
{
_namingConvention = copyFrom._namingConvention;
_culture = copyFrom._culture;
_ignoreMigrationTable = copyFrom._ignoreMigrationTable;
}

public virtual DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
Expand All @@ -27,6 +29,7 @@ protected NamingConventionsOptionsExtension([NotNull] NamingConventionsOptionsEx

internal virtual NamingConvention NamingConvention => _namingConvention;
internal virtual CultureInfo Culture => _culture;
internal virtual bool IgnoreMigrationTable => _ignoreMigrationTable;

public virtual NamingConventionsOptionsExtension WithoutNaming()
{
Expand All @@ -35,43 +38,48 @@ public virtual NamingConventionsOptionsExtension WithoutNaming()
return clone;
}

public virtual NamingConventionsOptionsExtension WithSnakeCaseNamingConvention(CultureInfo culture = null)
public virtual NamingConventionsOptionsExtension WithSnakeCaseNamingConvention(CultureInfo culture = null, bool ignoreMigrationTable = false)
{
var clone = Clone();
clone._namingConvention = NamingConvention.SnakeCase;
clone._culture = culture;
clone._ignoreMigrationTable = ignoreMigrationTable;
return clone;
}

public virtual NamingConventionsOptionsExtension WithLowerCaseNamingConvention(CultureInfo culture = null)
public virtual NamingConventionsOptionsExtension WithLowerCaseNamingConvention(CultureInfo culture = null, bool ignoreMigrationTable = false)
{
var clone = Clone();
clone._namingConvention = NamingConvention.LowerCase;
clone._culture = culture;
clone._ignoreMigrationTable = ignoreMigrationTable;
return clone;
}

public virtual NamingConventionsOptionsExtension WithUpperCaseNamingConvention(CultureInfo culture = null)
public virtual NamingConventionsOptionsExtension WithUpperCaseNamingConvention(CultureInfo culture = null, bool ignoreMigrationTable = false)
{
var clone = Clone();
clone._namingConvention = NamingConvention.UpperCase;
clone._culture = culture;
clone._ignoreMigrationTable = ignoreMigrationTable;
return clone;
}

public virtual NamingConventionsOptionsExtension WithUpperSnakeCaseNamingConvention(CultureInfo culture = null)
public virtual NamingConventionsOptionsExtension WithUpperSnakeCaseNamingConvention(CultureInfo culture = null, bool ignoreMigrationTable = false)
{
var clone = Clone();
clone._namingConvention = NamingConvention.UpperSnakeCase;
clone._culture = culture;
clone._ignoreMigrationTable = ignoreMigrationTable;
return clone;
}

public virtual NamingConventionsOptionsExtension WithCamelCaseNamingConvention(CultureInfo culture = null)
public virtual NamingConventionsOptionsExtension WithCamelCaseNamingConvention(CultureInfo culture = null, bool ignoreMigrationTable = false)
{
var clone = Clone();
clone._namingConvention = NamingConvention.CamelCase;
clone._culture = culture;
clone._ignoreMigrationTable = ignoreMigrationTable;
return clone;
}

Expand Down Expand Up @@ -109,6 +117,12 @@ public override string LogFragment
_ => throw new ArgumentOutOfRangeException("Unhandled enum value: " + Extension._namingConvention)
});

if (Extension._ignoreMigrationTable)
{
builder
.Append(" ignoring the migrations table");
}

if (Extension._culture is null)
{
builder
Expand All @@ -128,6 +142,7 @@ public override int GetServiceProviderHashCode()
{
var hashCode = Extension._namingConvention.GetHashCode();
hashCode = (hashCode * 3) ^ (Extension._culture?.GetHashCode() ?? 0);
hashCode = (hashCode * 7) ^ (Extension._ignoreMigrationTable.GetHashCode());
return hashCode;
}

Expand All @@ -138,6 +153,10 @@ public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
debugInfo["Naming:UseNamingConvention"]
= Extension._namingConvention.GetHashCode().ToString(CultureInfo.InvariantCulture);

debugInfo["Naming:IgnoreMigrationTable"]
= Extension._ignoreMigrationTable.GetHashCode().ToString(CultureInfo.InvariantCulture);

if (Extension._culture != null)
{
debugInfo["Naming:Culture"]
Expand Down
40 changes: 20 additions & 20 deletions EFCore.NamingConventions/NamingConventionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,98 +9,98 @@ namespace Microsoft.EntityFrameworkCore
public static class NamingConventionsExtensions
{
public static DbContextOptionsBuilder UseSnakeCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder , CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder optionsBuilder , CultureInfo culture = null, bool ignoreMigrationTable = false)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));

var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithSnakeCaseNamingConvention(culture);
.WithSnakeCaseNamingConvention(culture, ignoreMigrationTable);

((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}

public static DbContextOptionsBuilder<TContext> UseSnakeCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder , CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder , CultureInfo culture = null, bool ignoreMigrationTable = false)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
=> (DbContextOptionsBuilder<TContext>)UseSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture, ignoreMigrationTable);

public static DbContextOptionsBuilder UseLowerCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));

var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithLowerCaseNamingConvention(culture);
.WithLowerCaseNamingConvention(culture, ignoreMigrationTable);

((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}

public static DbContextOptionsBuilder<TContext> UseLowerCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseLowerCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder ,culture);
=> (DbContextOptionsBuilder<TContext>)UseLowerCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder ,culture, ignoreMigrationTable);

public static DbContextOptionsBuilder UseUpperCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));

var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithUpperCaseNamingConvention(culture);
.WithUpperCaseNamingConvention(culture, ignoreMigrationTable);

((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}

public static DbContextOptionsBuilder<TContext> UseUpperCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseUpperCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
=> (DbContextOptionsBuilder<TContext>)UseUpperCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture, ignoreMigrationTable);

public static DbContextOptionsBuilder UseUpperSnakeCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));

var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithUpperSnakeCaseNamingConvention(culture);
.WithUpperSnakeCaseNamingConvention(culture, ignoreMigrationTable);

((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}

public static DbContextOptionsBuilder<TContext> UseUpperSnakeCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseUpperSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
=> (DbContextOptionsBuilder<TContext>)UseUpperSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture, ignoreMigrationTable);

public static DbContextOptionsBuilder UseCamelCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));

var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithCamelCaseNamingConvention(culture);
.WithCamelCaseNamingConvention(culture, ignoreMigrationTable);

((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}

public static DbContextOptionsBuilder<TContext> UseCamelCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null, bool ignoreMigrationTable = false)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseCamelCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
=> (DbContextOptionsBuilder<TContext>)UseCamelCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture, ignoreMigrationTable);
}
}
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ SELECT c.id, c.full_name
WHERE c.full_name = 'John Doe';
```

## Ignoring the Migration Table `__EFMigrationsHistory`

To make migrations of existing databases more robust one might want to leave the migrations table out of the naming conventions.

```c#
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseNpgsql(...)
.UseSnakeCaseNamingConvention(ignoreMigrationTable: true);
```

## Supported naming conventions

* UseSnakeCaseNamingConvention: `FullName` becomes `full_name`
Expand Down

0 comments on commit 5594847

Please sign in to comment.