File-scoped namespaces

This commit is contained in:
Shay Rojansky
2022-05-17 14:44:27 +02:00
parent 5e2f346a8b
commit d876e7e4ca
17 changed files with 1358 additions and 1375 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,33 +2,32 @@ using System.Globalization;
using EFCore.NamingConventions.Internal;
using Xunit;
namespace EFCore.NamingConventions.Test
namespace EFCore.NamingConventions.Test;
public class RewriterTest
{
public class RewriterTest
{
[Fact]
public void SnakeCase()
=> Assert.Equal("full_name",
new SnakeCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void SnakeCase()
=> Assert.Equal("full_name",
new SnakeCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void UpperSnakeCase()
=> Assert.Equal("FULL_NAME",
new UpperSnakeCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void UpperSnakeCase()
=> Assert.Equal("FULL_NAME",
new UpperSnakeCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void LowerCase()
=> Assert.Equal("fullname",
new LowerCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void LowerCase()
=> Assert.Equal("fullname",
new LowerCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void CamelCase()
=> Assert.Equal("fullName",
new CamelCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void CamelCase()
=> Assert.Equal("fullName",
new CamelCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
[Fact]
public void UpperCase()
=> Assert.Equal("FULLNAME",
new UpperCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
}
}
[Fact]
public void UpperCase()
=> Assert.Equal("FULLNAME",
new UpperCaseNameRewriter(CultureInfo.InvariantCulture).RewriteName("FullName"));
}

View File

@@ -7,24 +7,23 @@ using Microsoft.EntityFrameworkCore.Sqlite.Diagnostics.Internal;
using Microsoft.Extensions.DependencyInjection;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.TestUtilities
namespace Microsoft.EntityFrameworkCore.TestUtilities;
public class SqliteTestHelpers : TestHelpers
{
public class SqliteTestHelpers : TestHelpers
protected SqliteTestHelpers()
{
protected SqliteTestHelpers()
{
}
}
public static SqliteTestHelpers Instance { get; } = new();
public static SqliteTestHelpers Instance { get; } = new();
public override IServiceCollection AddProviderServices(IServiceCollection services)
=> services.AddEntityFrameworkSqlite();
public override IServiceCollection AddProviderServices(IServiceCollection services)
=> services.AddEntityFrameworkSqlite();
public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite(new SqliteConnection("Data Source=:memory:"));
public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite(new SqliteConnection("Data Source=:memory:"));
#pragma warning disable EF1001
public override LoggingDefinitions LoggingDefinitions { get; } = new SqliteLoggingDefinitions();
public override LoggingDefinitions LoggingDefinitions { get; } = new SqliteLoggingDefinitions();
#pragma warning restore EF1001
}
}

View File

@@ -6,105 +6,104 @@ using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore
namespace Microsoft.EntityFrameworkCore;
[DebuggerStepThrough]
internal static class Check
{
[DebuggerStepThrough]
internal static class Check
[ContractAnnotation("value:null => halt")]
public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
{
[ContractAnnotation("value:null => halt")]
public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
if (ReferenceEquals(value, null))
{
if (ReferenceEquals(value, null))
{
NotEmpty(parameterName, nameof(parameterName));
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentNullException(parameterName);
}
return value;
throw new ArgumentNullException(parameterName);
}
[ContractAnnotation("value:null => halt")]
public static T NotNull<T>(
[NoEnumeration] T value,
[InvokerParameterName] [NotNull] string parameterName,
[NotNull] string propertyName)
{
if (ReferenceEquals(value, null))
{
NotEmpty(parameterName, nameof(parameterName));
NotEmpty(propertyName, nameof(propertyName));
throw new ArgumentException(CoreStrings.ArgumentPropertyNull(propertyName, parameterName));
}
return value;
}
[ContractAnnotation("value:null => halt")]
public static IReadOnlyList<T> NotEmpty<T>(IReadOnlyList<T> value, [InvokerParameterName] [NotNull] string parameterName)
{
NotNull(value, parameterName);
if (value.Count == 0)
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(AbstractionsStrings.CollectionArgumentIsEmpty(parameterName));
}
return value;
}
[ContractAnnotation("value:null => halt")]
public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
{
Exception e = null;
if (ReferenceEquals(value, null))
{
e = new ArgumentNullException(parameterName);
}
else if (value.Trim().Length == 0)
{
e = new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName));
}
if (e != null)
{
NotEmpty(parameterName, nameof(parameterName));
throw e;
}
return value;
}
public static string NullButNotEmpty([CanBeNull] string value, [InvokerParameterName] [NotNull] string parameterName)
{
if (!ReferenceEquals(value, null)
&& (value.Length == 0))
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName));
}
return value;
}
public static IReadOnlyList<T> HasNoNulls<T>(IReadOnlyList<T> value, [InvokerParameterName] [NotNull] string parameterName)
where T : class
{
NotNull(value, parameterName);
if (value.Any(e => e == null))
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(parameterName);
}
return value;
}
return value;
}
}
[ContractAnnotation("value:null => halt")]
public static T NotNull<T>(
[NoEnumeration] T value,
[InvokerParameterName] [NotNull] string parameterName,
[NotNull] string propertyName)
{
if (ReferenceEquals(value, null))
{
NotEmpty(parameterName, nameof(parameterName));
NotEmpty(propertyName, nameof(propertyName));
throw new ArgumentException(CoreStrings.ArgumentPropertyNull(propertyName, parameterName));
}
return value;
}
[ContractAnnotation("value:null => halt")]
public static IReadOnlyList<T> NotEmpty<T>(IReadOnlyList<T> value, [InvokerParameterName] [NotNull] string parameterName)
{
NotNull(value, parameterName);
if (value.Count == 0)
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(AbstractionsStrings.CollectionArgumentIsEmpty(parameterName));
}
return value;
}
[ContractAnnotation("value:null => halt")]
public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
{
Exception e = null;
if (ReferenceEquals(value, null))
{
e = new ArgumentNullException(parameterName);
}
else if (value.Trim().Length == 0)
{
e = new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName));
}
if (e != null)
{
NotEmpty(parameterName, nameof(parameterName));
throw e;
}
return value;
}
public static string NullButNotEmpty([CanBeNull] string value, [InvokerParameterName] [NotNull] string parameterName)
{
if (!ReferenceEquals(value, null)
&& (value.Length == 0))
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName));
}
return value;
}
public static IReadOnlyList<T> HasNoNulls<T>(IReadOnlyList<T> value, [InvokerParameterName] [NotNull] string parameterName)
where T : class
{
NotNull(value, parameterName);
if (value.Any(e => e == null))
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(parameterName);
}
return value;
}
}

View File

@@ -1,108 +1,107 @@
using System;
// ReSharper disable once CheckNamespace
namespace JetBrains.Annotations
namespace JetBrains.Annotations;
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field)]
internal sealed class NotNullAttribute : Attribute
{
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field)]
internal sealed class NotNullAttribute : Attribute
}
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field)]
internal sealed class CanBeNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class InvokerParameterNameAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class NoEnumerationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
internal sealed class ContractAnnotationAttribute : Attribute
{
public string Contract { get; }
public bool ForceFullStates { get; }
public ContractAnnotationAttribute([NotNull] string contract)
: this(contract, false)
{
}
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field)]
internal sealed class CanBeNullAttribute : Attribute
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class InvokerParameterNameAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class NoEnumerationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
internal sealed class ContractAnnotationAttribute : Attribute
{
public string Contract { get; }
public bool ForceFullStates { get; }
public ContractAnnotationAttribute([NotNull] string contract)
: this(contract, false)
{
}
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
{
Contract = contract;
ForceFullStates = forceFullStates;
}
}
[AttributeUsage(AttributeTargets.All)]
internal sealed class UsedImplicitlyAttribute : Attribute
{
public UsedImplicitlyAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
{
}
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default)
{
}
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags)
{
}
public UsedImplicitlyAttribute(
ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
public ImplicitUseKindFlags UseKindFlags { get; }
public ImplicitUseTargetFlags TargetFlags { get; }
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)]
internal sealed class StringFormatMethodAttribute : Attribute
{
public StringFormatMethodAttribute([NotNull] string formatParameterName)
=> FormatParameterName = formatParameterName;
[NotNull]
public string FormatParameterName { get; }
}
[Flags]
internal enum ImplicitUseKindFlags
{
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
Access = 1,
Assign = 2,
InstantiatedWithFixedConstructorSignature = 4,
InstantiatedNoFixedConstructorSignature = 8
}
[Flags]
internal enum ImplicitUseTargetFlags
{
Default = Itself,
Itself = 1,
Members = 2,
WithMembers = Itself | Members
Contract = contract;
ForceFullStates = forceFullStates;
}
}
[AttributeUsage(AttributeTargets.All)]
internal sealed class UsedImplicitlyAttribute : Attribute
{
public UsedImplicitlyAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
{
}
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default)
{
}
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags)
{
}
public UsedImplicitlyAttribute(
ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
public ImplicitUseKindFlags UseKindFlags { get; }
public ImplicitUseTargetFlags TargetFlags { get; }
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)]
internal sealed class StringFormatMethodAttribute : Attribute
{
public StringFormatMethodAttribute([NotNull] string formatParameterName)
=> FormatParameterName = formatParameterName;
[NotNull]
public string FormatParameterName { get; }
}
[Flags]
internal enum ImplicitUseKindFlags
{
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
Access = 1,
Assign = 2,
InstantiatedWithFixedConstructorSignature = 4,
InstantiatedNoFixedConstructorSignature = 8
}
[Flags]
internal enum ImplicitUseTargetFlags
{
Default = Itself,
Itself = 1,
Members = 2,
WithMembers = Itself | Members
}

View File

@@ -1,14 +1,13 @@
using System.Globalization;
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public class CamelCaseNameRewriter : INameRewriter
{
public class CamelCaseNameRewriter : INameRewriter
{
private readonly CultureInfo _culture;
private readonly CultureInfo _culture;
public CamelCaseNameRewriter(CultureInfo culture) => _culture = culture;
public CamelCaseNameRewriter(CultureInfo culture) => _culture = culture;
public string RewriteName(string name) =>
string.IsNullOrEmpty(name) ? name: char.ToLower(name[0], _culture) + name.Substring(1);
}
}
public string RewriteName(string name) =>
string.IsNullOrEmpty(name) ? name: char.ToLower(name[0], _culture) + name.Substring(1);
}

View File

@@ -1,7 +1,6 @@
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public interface INameRewriter
{
public interface INameRewriter
{
string RewriteName(string name);
}
}
string RewriteName(string name);
}

View File

@@ -1,12 +1,11 @@
using System.Globalization;
namespace EFCore.NamingConventions.Internal
{
public class LowerCaseNameRewriter : INameRewriter
{
private readonly CultureInfo _culture;
namespace EFCore.NamingConventions.Internal;
public LowerCaseNameRewriter(CultureInfo culture) => _culture = culture;
public string RewriteName(string name) => name.ToLower(_culture);
}
}
public class LowerCaseNameRewriter : INameRewriter
{
private readonly CultureInfo _culture;
public LowerCaseNameRewriter(CultureInfo culture) => _culture = culture;
public string RewriteName(string name) => name.ToLower(_culture);
}

View File

@@ -5,278 +5,277 @@ using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public class NameRewritingConvention :
IEntityTypeAddedConvention,
IEntityTypeAnnotationChangedConvention,
IPropertyAddedConvention,
IForeignKeyOwnershipChangedConvention,
IKeyAddedConvention,
IForeignKeyAddedConvention,
IIndexAddedConvention,
IEntityTypeBaseTypeChangedConvention,
IModelFinalizingConvention
{
public class NameRewritingConvention :
IEntityTypeAddedConvention,
IEntityTypeAnnotationChangedConvention,
IPropertyAddedConvention,
IForeignKeyOwnershipChangedConvention,
IKeyAddedConvention,
IForeignKeyAddedConvention,
IIndexAddedConvention,
IEntityTypeBaseTypeChangedConvention,
IModelFinalizingConvention
private static readonly StoreObjectType[] _storeObjectTypes
= { StoreObjectType.Table, StoreObjectType.View, StoreObjectType.Function, StoreObjectType.SqlQuery };
private readonly INameRewriter _namingNameRewriter;
public NameRewritingConvention(INameRewriter nameRewriter)
=> _namingNameRewriter = nameRewriter;
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
{
private static readonly StoreObjectType[] _storeObjectTypes
= { StoreObjectType.Table, StoreObjectType.View, StoreObjectType.Function, StoreObjectType.SqlQuery };
var entityType = entityTypeBuilder.Metadata;
private readonly INameRewriter _namingNameRewriter;
public NameRewritingConvention(INameRewriter nameRewriter)
=> _namingNameRewriter = nameRewriter;
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
// Note that the base type is null when the entity type is first added - a base type only gets added later
// (see ProcessEntityTypeBaseTypeChanged). But we still have this check for safety.
if (entityType.BaseType is null)
{
var entityType = entityTypeBuilder.Metadata;
entityTypeBuilder.ToTable(_namingNameRewriter.RewriteName(entityType.GetTableName()), entityType.GetSchema());
// Note that the base type is null when the entity type is first added - a base type only gets added later
// (see ProcessEntityTypeBaseTypeChanged). But we still have this check for safety.
if (entityType.BaseType is null)
if (entityType.GetViewNameConfigurationSource() == ConfigurationSource.Convention)
{
entityTypeBuilder.ToTable(_namingNameRewriter.RewriteName(entityType.GetTableName()), entityType.GetSchema());
if (entityType.GetViewNameConfigurationSource() == ConfigurationSource.Convention)
{
entityTypeBuilder.ToView(_namingNameRewriter.RewriteName(entityType.GetViewName()), entityType.GetViewSchema());
}
entityTypeBuilder.ToView(_namingNameRewriter.RewriteName(entityType.GetViewName()), entityType.GetViewSchema());
}
}
}
public void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType newBaseType,
IConventionEntityType oldBaseType,
IConventionContext<IConventionEntityType> context)
public void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType newBaseType,
IConventionEntityType oldBaseType,
IConventionContext<IConventionEntityType> context)
{
var entityType = entityTypeBuilder.Metadata;
if (newBaseType is null)
{
var entityType = entityTypeBuilder.Metadata;
// The entity is getting removed from a hierarchy. Set the (rewritten) TableName.
entityTypeBuilder.ToTable(_namingNameRewriter.RewriteName(entityType.GetTableName()), entityType.GetSchema());
}
else
{
// The entity is getting a new base type (e.g. joining a hierarchy).
// If this is TPH, we remove the previously rewritten TableName (and non-rewritten Schema) which we set when the
// entity type was first added to the model (see ProcessEntityTypeAdded).
// If this is TPT, TableName and Schema are set explicitly, so the following will be ignored.
entityTypeBuilder.HasNoAnnotation(RelationalAnnotationNames.TableName);
entityTypeBuilder.HasNoAnnotation(RelationalAnnotationNames.Schema);
}
}
if (newBaseType is null)
public virtual void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
IConventionContext<IConventionPropertyBuilder> context)
=> RewriteColumnName(propertyBuilder);
public void ProcessForeignKeyOwnershipChanged(IConventionForeignKeyBuilder relationshipBuilder, IConventionContext<bool?> context)
{
var foreignKey = relationshipBuilder.Metadata;
var ownedEntityType = foreignKey.DeclaringEntityType;
// An entity type is becoming owned - this is a bit complicated.
// Unless it's a collection navigation, or the owned entity table name was explicitly set by the user, this triggers table
// splitting, which means we need to undo rewriting which we've done previously.
if (foreignKey.IsOwnership
&& !foreignKey.GetNavigation(false).IsCollection
&& ownedEntityType.GetTableNameConfigurationSource() != ConfigurationSource.Explicit)
{
// Reset the table name which we've set when the entity type was added.
// If table splitting was configured by explicitly setting the table name, the following
// does nothing.
ownedEntityType.Builder.HasNoAnnotation(RelationalAnnotationNames.TableName);
ownedEntityType.Builder.HasNoAnnotation(RelationalAnnotationNames.Schema);
ownedEntityType.FindPrimaryKey()?.Builder.HasNoAnnotation(RelationalAnnotationNames.Name);
// We've previously set rewritten column names when the entity was originally added (before becoming owned).
// These need to be rewritten again to include the owner prefix.
foreach (var property in ownedEntityType.GetProperties())
{
// The entity is getting removed from a hierarchy. Set the (rewritten) TableName.
entityTypeBuilder.ToTable(_namingNameRewriter.RewriteName(entityType.GetTableName()), entityType.GetSchema());
RewriteColumnName(property.Builder);
}
}
}
public void ProcessEntityTypeAnnotationChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
string name,
IConventionAnnotation annotation,
IConventionAnnotation oldAnnotation,
IConventionContext<IConventionAnnotation> context)
{
var entityType = entityTypeBuilder.Metadata;
// If the View/SqlQuery/Function name is being set on the entity type, and its table name is set by convention, then we assume
// we're the one who set the table name back when the entity type was originally added. We now undo this as the entity type
// should only be mapped to the View/SqlQuery/Function.
if (name is RelationalAnnotationNames.ViewName or RelationalAnnotationNames.SqlQuery or RelationalAnnotationNames.FunctionName
&& annotation.Value is not null
&& entityType.GetTableNameConfigurationSource() == ConfigurationSource.Convention)
{
entityType.SetTableName(null);
}
if (name != RelationalAnnotationNames.TableName
|| StoreObjectIdentifier.Create(entityType, StoreObjectType.Table) is not StoreObjectIdentifier tableIdentifier)
{
return;
}
// The table's name is changing - rewrite keys, index names
if (entityType.FindPrimaryKey() is IConventionKey primaryKey)
{
// We need to rewrite the PK name.
// However, this isn't yet supported with TPT, see https://github.com/dotnet/efcore/issues/23444.
// So we need to check if the entity is within a TPT hierarchy, or is an owned entity within a TPT hierarchy.
var rootType = entityType.GetRootType();
var isTPT = rootType.GetDerivedTypes().FirstOrDefault() is { } derivedType
&& derivedType.GetTableName() != rootType.GetTableName();
if (entityType.FindRowInternalForeignKeys(tableIdentifier).FirstOrDefault() is null && !isTPT)
{
primaryKey.Builder.HasName(_namingNameRewriter.RewriteName(primaryKey.GetDefaultName()));
}
else
{
// The entity is getting a new base type (e.g. joining a hierarchy).
// If this is TPH, we remove the previously rewritten TableName (and non-rewritten Schema) which we set when the
// entity type was first added to the model (see ProcessEntityTypeAdded).
// If this is TPT, TableName and Schema are set explicitly, so the following will be ignored.
entityTypeBuilder.HasNoAnnotation(RelationalAnnotationNames.TableName);
entityTypeBuilder.HasNoAnnotation(RelationalAnnotationNames.Schema);
}
}
public virtual void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
IConventionContext<IConventionPropertyBuilder> context)
=> RewriteColumnName(propertyBuilder);
public void ProcessForeignKeyOwnershipChanged(IConventionForeignKeyBuilder relationshipBuilder, IConventionContext<bool?> context)
{
var foreignKey = relationshipBuilder.Metadata;
var ownedEntityType = foreignKey.DeclaringEntityType;
// An entity type is becoming owned - this is a bit complicated.
// Unless it's a collection navigation, or the owned entity table name was explicitly set by the user, this triggers table
// splitting, which means we need to undo rewriting which we've done previously.
if (foreignKey.IsOwnership
&& !foreignKey.GetNavigation(false).IsCollection
&& ownedEntityType.GetTableNameConfigurationSource() != ConfigurationSource.Explicit)
{
// Reset the table name which we've set when the entity type was added.
// If table splitting was configured by explicitly setting the table name, the following
// does nothing.
ownedEntityType.Builder.HasNoAnnotation(RelationalAnnotationNames.TableName);
ownedEntityType.Builder.HasNoAnnotation(RelationalAnnotationNames.Schema);
ownedEntityType.FindPrimaryKey()?.Builder.HasNoAnnotation(RelationalAnnotationNames.Name);
// We've previously set rewritten column names when the entity was originally added (before becoming owned).
// These need to be rewritten again to include the owner prefix.
foreach (var property in ownedEntityType.GetProperties())
// This hierarchy is being transformed into TPT via the explicit setting of the table name.
// We not only have to reset our own key name, but also the parents'. Otherwise, the parent's key name
// is used as the child's (see RelationalKeyExtensions.GetName), and we get a "duplicate key name in database" error
// since both parent and child have the same key name;
foreach (var type in entityType.GetRootType().GetDerivedTypesInclusive())
{
RewriteColumnName(property.Builder);
}
}
}
public void ProcessEntityTypeAnnotationChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
string name,
IConventionAnnotation annotation,
IConventionAnnotation oldAnnotation,
IConventionContext<IConventionAnnotation> context)
{
var entityType = entityTypeBuilder.Metadata;
// If the View/SqlQuery/Function name is being set on the entity type, and its table name is set by convention, then we assume
// we're the one who set the table name back when the entity type was originally added. We now undo this as the entity type
// should only be mapped to the View/SqlQuery/Function.
if (name is RelationalAnnotationNames.ViewName or RelationalAnnotationNames.SqlQuery or RelationalAnnotationNames.FunctionName
&& annotation.Value is not null
&& entityType.GetTableNameConfigurationSource() == ConfigurationSource.Convention)
{
entityType.SetTableName(null);
}
if (name != RelationalAnnotationNames.TableName
|| StoreObjectIdentifier.Create(entityType, StoreObjectType.Table) is not StoreObjectIdentifier tableIdentifier)
{
return;
}
// The table's name is changing - rewrite keys, index names
if (entityType.FindPrimaryKey() is IConventionKey primaryKey)
{
// We need to rewrite the PK name.
// However, this isn't yet supported with TPT, see https://github.com/dotnet/efcore/issues/23444.
// So we need to check if the entity is within a TPT hierarchy, or is an owned entity within a TPT hierarchy.
var rootType = entityType.GetRootType();
var isTPT = rootType.GetDerivedTypes().FirstOrDefault() is { } derivedType
&& derivedType.GetTableName() != rootType.GetTableName();
if (entityType.FindRowInternalForeignKeys(tableIdentifier).FirstOrDefault() is null && !isTPT)
{
primaryKey.Builder.HasName(_namingNameRewriter.RewriteName(primaryKey.GetDefaultName()));
}
else
{
// This hierarchy is being transformed into TPT via the explicit setting of the table name.
// We not only have to reset our own key name, but also the parents'. Otherwise, the parent's key name
// is used as the child's (see RelationalKeyExtensions.GetName), and we get a "duplicate key name in database" error
// since both parent and child have the same key name;
foreach (var type in entityType.GetRootType().GetDerivedTypesInclusive())
if (type.FindPrimaryKey() is IConventionKey pk)
{
if (type.FindPrimaryKey() is IConventionKey pk)
{
pk.Builder.HasNoAnnotation(RelationalAnnotationNames.Name);
}
pk.Builder.HasNoAnnotation(RelationalAnnotationNames.Name);
}
}
}
foreach (var foreignKey in entityType.GetForeignKeys())
{
foreignKey.Builder.HasConstraintName(_namingNameRewriter.RewriteName(foreignKey.GetDefaultName()));
}
foreach (var index in entityType.GetIndexes())
{
index.Builder.HasDatabaseName(_namingNameRewriter.RewriteName(index.GetDefaultDatabaseName()));
}
if (annotation?.Value is not null
&& entityType.FindOwnership() is IConventionForeignKey ownership
&& (string)annotation.Value != ownership.PrincipalEntityType.GetTableName())
{
// An owned entity's table is being set explicitly - this is the trigger to undo table splitting (which is the default).
// When the entity became owned, we prefixed all of its properties - we must now undo that.
foreach (var property in entityType.GetProperties()
.Except(entityType.FindPrimaryKey().Properties)
.Where(p => p.Builder.CanSetColumnName(null)))
{
RewriteColumnName(property.Builder);
}
// We previously rewrote the owned entity's primary key name, when the owned entity was still in table splitting.
// Now that its getting its own table, rewrite the primary key constraint name again.
if (entityType.FindPrimaryKey() is IConventionKey key)
{
key.Builder.HasName(_namingNameRewriter.RewriteName(key.GetDefaultName()));
}
}
}
public void ProcessForeignKeyAdded(
IConventionForeignKeyBuilder relationshipBuilder,
IConventionContext<IConventionForeignKeyBuilder> context)
=> relationshipBuilder.HasConstraintName(_namingNameRewriter.RewriteName(relationshipBuilder.Metadata.GetDefaultName()));
public void ProcessKeyAdded(IConventionKeyBuilder keyBuilder, IConventionContext<IConventionKeyBuilder> context)
=> keyBuilder.HasName(_namingNameRewriter.RewriteName(keyBuilder.Metadata.GetName()));
public void ProcessIndexAdded(
IConventionIndexBuilder indexBuilder,
IConventionContext<IConventionIndexBuilder> context)
=> indexBuilder.HasDatabaseName(_namingNameRewriter.RewriteName(indexBuilder.Metadata.GetDefaultDatabaseName()));
/// <summary>
/// EF Core's <see cref="SharedTableConvention" /> runs at model finalization time, and adds entity type prefixes to
/// clashing columns. These prefixes also needs to be rewritten by us, so we run after that convention to do that.
/// </summary>
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
foreach (var foreignKey in entityType.GetForeignKeys())
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
foreignKey.Builder.HasConstraintName(_namingNameRewriter.RewriteName(foreignKey.GetDefaultName()));
}
foreach (var index in entityType.GetIndexes())
{
index.Builder.HasDatabaseName(_namingNameRewriter.RewriteName(index.GetDefaultDatabaseName()));
}
if (annotation?.Value is not null
&& entityType.FindOwnership() is IConventionForeignKey ownership
&& (string)annotation.Value != ownership.PrincipalEntityType.GetTableName())
{
// An owned entity's table is being set explicitly - this is the trigger to undo table splitting (which is the default).
// When the entity became owned, we prefixed all of its properties - we must now undo that.
foreach (var property in entityType.GetProperties()
.Except(entityType.FindPrimaryKey().Properties)
.Where(p => p.Builder.CanSetColumnName(null)))
{
foreach (var property in entityType.GetProperties())
RewriteColumnName(property.Builder);
}
// We previously rewrote the owned entity's primary key name, when the owned entity was still in table splitting.
// Now that its getting its own table, rewrite the primary key constraint name again.
if (entityType.FindPrimaryKey() is IConventionKey key)
{
key.Builder.HasName(_namingNameRewriter.RewriteName(key.GetDefaultName()));
}
}
}
public void ProcessForeignKeyAdded(
IConventionForeignKeyBuilder relationshipBuilder,
IConventionContext<IConventionForeignKeyBuilder> context)
=> relationshipBuilder.HasConstraintName(_namingNameRewriter.RewriteName(relationshipBuilder.Metadata.GetDefaultName()));
public void ProcessKeyAdded(IConventionKeyBuilder keyBuilder, IConventionContext<IConventionKeyBuilder> context)
=> keyBuilder.HasName(_namingNameRewriter.RewriteName(keyBuilder.Metadata.GetName()));
public void ProcessIndexAdded(
IConventionIndexBuilder indexBuilder,
IConventionContext<IConventionIndexBuilder> context)
=> indexBuilder.HasDatabaseName(_namingNameRewriter.RewriteName(indexBuilder.Metadata.GetDefaultDatabaseName()));
/// <summary>
/// EF Core's <see cref="SharedTableConvention" /> runs at model finalization time, and adds entity type prefixes to
/// clashing columns. These prefixes also needs to be rewritten by us, so we run after that convention to do that.
/// </summary>
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
var columnName = property.GetColumnBaseName();
if (columnName.StartsWith(entityType.ShortName() + '_', StringComparison.Ordinal))
{
var columnName = property.GetColumnBaseName();
property.Builder.HasColumnName(
_namingNameRewriter.RewriteName(entityType.ShortName()) + columnName.Substring(entityType.ShortName().Length));
}
foreach (var storeObjectType in _storeObjectTypes)
{
var identifier = StoreObjectIdentifier.Create(entityType, storeObjectType);
if (identifier is null)
{
continue;
}
if (property.GetColumnNameConfigurationSource(identifier.Value) != ConfigurationSource.Convention)
{
continue;
}
columnName = property.GetColumnName(identifier.Value);
if (columnName.StartsWith(entityType.ShortName() + '_', StringComparison.Ordinal))
{
property.Builder.HasColumnName(
_namingNameRewriter.RewriteName(entityType.ShortName()) + columnName.Substring(entityType.ShortName().Length));
_namingNameRewriter.RewriteName(entityType.ShortName())
+ columnName.Substring(entityType.ShortName().Length));
}
foreach (var storeObjectType in _storeObjectTypes)
{
var identifier = StoreObjectIdentifier.Create(entityType, storeObjectType);
if (identifier is null)
{
continue;
}
if (property.GetColumnNameConfigurationSource(identifier.Value) != ConfigurationSource.Convention)
{
continue;
}
columnName = property.GetColumnName(identifier.Value);
if (columnName.StartsWith(entityType.ShortName() + '_', StringComparison.Ordinal))
{
property.Builder.HasColumnName(
_namingNameRewriter.RewriteName(entityType.ShortName())
+ columnName.Substring(entityType.ShortName().Length));
}
}
}
}
}
private void RewriteColumnName(IConventionPropertyBuilder propertyBuilder)
{
var property = propertyBuilder.Metadata;
var entityType = property.DeclaringEntityType;
// 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);
// 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
? property.GetDefaultColumnName(tableIdentifier)
: property.GetDefaultColumnBaseName();
propertyBuilder.HasColumnName(_namingNameRewriter.RewriteName(baseColumnName));
foreach (var storeObjectType in _storeObjectTypes)
{
var identifier = StoreObjectIdentifier.Create(entityType, storeObjectType);
if (identifier is null)
{
continue;
}
if (property.GetColumnNameConfigurationSource(identifier.Value) == ConfigurationSource.Convention)
{
propertyBuilder.HasColumnName(
_namingNameRewriter.RewriteName(property.GetColumnName(identifier.Value)), identifier.Value);
}
}
}
}
private void RewriteColumnName(IConventionPropertyBuilder propertyBuilder)
{
var property = propertyBuilder.Metadata;
var entityType = property.DeclaringEntityType;
// 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);
// 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
? property.GetDefaultColumnName(tableIdentifier)
: property.GetDefaultColumnBaseName();
propertyBuilder.HasColumnName(_namingNameRewriter.RewriteName(baseColumnName));
foreach (var storeObjectType in _storeObjectTypes)
{
var identifier = StoreObjectIdentifier.Create(entityType, storeObjectType);
if (identifier is null)
{
continue;
}
if (property.GetColumnNameConfigurationSource(identifier.Value) == ConfigurationSource.Convention)
{
propertyBuilder.HasColumnName(
_namingNameRewriter.RewriteName(property.GetColumnName(identifier.Value)), identifier.Value);
}
}
}
}

View File

@@ -1,12 +1,11 @@
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public enum NamingConvention
{
public enum NamingConvention
{
None,
SnakeCase,
LowerCase,
CamelCase,
UpperCase,
UpperSnakeCase
}
None,
SnakeCase,
LowerCase,
CamelCase,
UpperCase,
UpperSnakeCase
}

View File

@@ -5,44 +5,43 @@ using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using JetBrains.Annotations;
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public class NamingConventionSetPlugin : IConventionSetPlugin
{
public class NamingConventionSetPlugin : IConventionSetPlugin
private readonly IDbContextOptions _options;
public NamingConventionSetPlugin([NotNull] IDbContextOptions options) => _options = options;
public ConventionSet ModifyConventions(ConventionSet conventionSet)
{
private readonly IDbContextOptions _options;
public NamingConventionSetPlugin([NotNull] IDbContextOptions options) => _options = options;
public ConventionSet ModifyConventions(ConventionSet conventionSet)
var extension = _options.FindExtension<NamingConventionsOptionsExtension>();
var namingStyle = extension.NamingConvention;
var culture = extension.Culture;
if (namingStyle == NamingConvention.None)
{
var extension = _options.FindExtension<NamingConventionsOptionsExtension>();
var namingStyle = extension.NamingConvention;
var culture = extension.Culture;
if (namingStyle == NamingConvention.None)
{
return conventionSet;
}
var convention = new NameRewritingConvention(namingStyle switch
{
NamingConvention.SnakeCase => new SnakeCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.LowerCase => new LowerCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.CamelCase => new CamelCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.UpperCase => new UpperCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.UpperSnakeCase => new UpperSnakeCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
_ => throw new ArgumentOutOfRangeException("Unhandled enum value: " + namingStyle)
});
conventionSet.EntityTypeAddedConventions.Add(convention);
conventionSet.EntityTypeAnnotationChangedConventions.Add(convention);
conventionSet.PropertyAddedConventions.Add(convention);
conventionSet.ForeignKeyOwnershipChangedConventions.Add(convention);
conventionSet.KeyAddedConventions.Add(convention);
conventionSet.ForeignKeyAddedConventions.Add(convention);
conventionSet.IndexAddedConventions.Add(convention);
conventionSet.EntityTypeBaseTypeChangedConventions.Add(convention);
conventionSet.ModelFinalizingConventions.Add(convention);
return conventionSet;
}
var convention = new NameRewritingConvention(namingStyle switch
{
NamingConvention.SnakeCase => new SnakeCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.LowerCase => new LowerCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.CamelCase => new CamelCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.UpperCase => new UpperCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
NamingConvention.UpperSnakeCase => new UpperSnakeCaseNameRewriter(culture ?? CultureInfo.InvariantCulture),
_ => throw new ArgumentOutOfRangeException("Unhandled enum value: " + namingStyle)
});
conventionSet.EntityTypeAddedConventions.Add(convention);
conventionSet.EntityTypeAnnotationChangedConventions.Add(convention);
conventionSet.PropertyAddedConventions.Add(convention);
conventionSet.ForeignKeyOwnershipChangedConventions.Add(convention);
conventionSet.KeyAddedConventions.Add(convention);
conventionSet.ForeignKeyAddedConventions.Add(convention);
conventionSet.IndexAddedConventions.Add(convention);
conventionSet.EntityTypeBaseTypeChangedConventions.Add(convention);
conventionSet.ModelFinalizingConventions.Add(convention);
return conventionSet;
}
}
}

View File

@@ -6,144 +6,143 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using JetBrains.Annotations;
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public class NamingConventionsOptionsExtension : IDbContextOptionsExtension
{
public class NamingConventionsOptionsExtension : IDbContextOptionsExtension
private DbContextOptionsExtensionInfo _info;
private NamingConvention _namingConvention;
private CultureInfo _culture;
public NamingConventionsOptionsExtension() {}
protected NamingConventionsOptionsExtension([NotNull] NamingConventionsOptionsExtension copyFrom)
{
private DbContextOptionsExtensionInfo _info;
private NamingConvention _namingConvention;
private CultureInfo _culture;
_namingConvention = copyFrom._namingConvention;
_culture = copyFrom._culture;
}
public NamingConventionsOptionsExtension() {}
protected NamingConventionsOptionsExtension([NotNull] NamingConventionsOptionsExtension copyFrom)
public virtual DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
protected virtual NamingConventionsOptionsExtension Clone() => new(this);
internal virtual NamingConvention NamingConvention => _namingConvention;
internal virtual CultureInfo Culture => _culture;
public virtual NamingConventionsOptionsExtension WithoutNaming()
{
var clone = Clone();
clone._namingConvention = NamingConvention.None;
return clone;
}
public virtual NamingConventionsOptionsExtension WithSnakeCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.SnakeCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithLowerCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.LowerCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithUpperCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.UpperCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithUpperSnakeCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.UpperSnakeCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithCamelCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.CamelCase;
clone._culture = culture;
return clone;
}
public void Validate(IDbContextOptions options) {}
public void ApplyServices(IServiceCollection services)
=> services.AddEntityFrameworkNamingConventions();
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
private string _logFragment;
public ExtensionInfo(IDbContextOptionsExtension extension) : base(extension) {}
private new NamingConventionsOptionsExtension Extension
=> (NamingConventionsOptionsExtension)base.Extension;
public override bool IsDatabaseProvider => false;
public override string LogFragment
{
_namingConvention = copyFrom._namingConvention;
_culture = copyFrom._culture;
}
public virtual DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
protected virtual NamingConventionsOptionsExtension Clone() => new(this);
internal virtual NamingConvention NamingConvention => _namingConvention;
internal virtual CultureInfo Culture => _culture;
public virtual NamingConventionsOptionsExtension WithoutNaming()
{
var clone = Clone();
clone._namingConvention = NamingConvention.None;
return clone;
}
public virtual NamingConventionsOptionsExtension WithSnakeCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.SnakeCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithLowerCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.LowerCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithUpperCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.UpperCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithUpperSnakeCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.UpperSnakeCase;
clone._culture = culture;
return clone;
}
public virtual NamingConventionsOptionsExtension WithCamelCaseNamingConvention(CultureInfo culture = null)
{
var clone = Clone();
clone._namingConvention = NamingConvention.CamelCase;
clone._culture = culture;
return clone;
}
public void Validate(IDbContextOptions options) {}
public void ApplyServices(IServiceCollection services)
=> services.AddEntityFrameworkNamingConventions();
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
private string _logFragment;
public ExtensionInfo(IDbContextOptionsExtension extension) : base(extension) {}
private new NamingConventionsOptionsExtension Extension
=> (NamingConventionsOptionsExtension)base.Extension;
public override bool IsDatabaseProvider => false;
public override string LogFragment
get
{
get
if (_logFragment == null)
{
if (_logFragment == null)
var builder = new StringBuilder();
builder.Append(Extension._namingConvention switch
{
var builder = new StringBuilder();
NamingConvention.SnakeCase => "using snake-case naming ",
NamingConvention.LowerCase => "using lower case naming",
NamingConvention.UpperCase => "using upper case naming",
NamingConvention.UpperSnakeCase => "using upper snake-case naming",
NamingConvention.CamelCase => "using camel-case naming",
_ => throw new ArgumentOutOfRangeException("Unhandled enum value: " + Extension._namingConvention)
});
builder.Append(Extension._namingConvention switch
{
NamingConvention.SnakeCase => "using snake-case naming ",
NamingConvention.LowerCase => "using lower case naming",
NamingConvention.UpperCase => "using upper case naming",
NamingConvention.UpperSnakeCase => "using upper snake-case naming",
NamingConvention.CamelCase => "using camel-case naming",
_ => throw new ArgumentOutOfRangeException("Unhandled enum value: " + Extension._namingConvention)
});
if (Extension._culture is null)
{
builder
.Append(" (culture=")
.Append(Extension._culture)
.Append(")");
}
_logFragment = builder.ToString();
if (Extension._culture is null)
{
builder
.Append(" (culture=")
.Append(Extension._culture)
.Append(")");
}
return _logFragment;
_logFragment = builder.ToString();
}
return _logFragment;
}
}
public override int GetServiceProviderHashCode()
public override int GetServiceProviderHashCode()
{
var hashCode = Extension._namingConvention.GetHashCode();
hashCode = (hashCode * 3) ^ (Extension._culture?.GetHashCode() ?? 0);
return hashCode;
}
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
debugInfo["Naming:UseNamingConvention"]
= Extension._namingConvention.GetHashCode().ToString(CultureInfo.InvariantCulture);
if (Extension._culture != null)
{
var hashCode = Extension._namingConvention.GetHashCode();
hashCode = (hashCode * 3) ^ (Extension._culture?.GetHashCode() ?? 0);
return hashCode;
}
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
debugInfo["Naming:UseNamingConvention"]
= Extension._namingConvention.GetHashCode().ToString(CultureInfo.InvariantCulture);
if (Extension._culture != null)
{
debugInfo["Naming:Culture"]
= Extension._culture.GetHashCode().ToString(CultureInfo.InvariantCulture);
}
debugInfo["Naming:Culture"]
= Extension._culture.GetHashCode().ToString(CultureInfo.InvariantCulture);
}
}
}
}
}

View File

@@ -2,37 +2,37 @@ using System;
using System.Globalization;
using System.Text;
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public class SnakeCaseNameRewriter : INameRewriter
{
public class SnakeCaseNameRewriter : INameRewriter
private readonly CultureInfo _culture;
public SnakeCaseNameRewriter(CultureInfo culture) => _culture = culture;
public virtual string RewriteName(string name)
{
private readonly CultureInfo _culture;
public SnakeCaseNameRewriter(CultureInfo culture) => _culture = culture;
public virtual string RewriteName(string name)
if (string.IsNullOrEmpty(name))
{
if (string.IsNullOrEmpty(name))
return name;
}
var builder = new StringBuilder(name.Length + Math.Min(2, name.Length / 5));
var previousCategory = default(UnicodeCategory?);
for (var currentIndex = 0; currentIndex < name.Length; currentIndex++)
{
var currentChar = name[currentIndex];
if (currentChar == '_')
{
return name;
builder.Append('_');
previousCategory = null;
continue;
}
var builder = new StringBuilder(name.Length + Math.Min(2, name.Length / 5));
var previousCategory = default(UnicodeCategory?);
for (var currentIndex = 0; currentIndex < name.Length; currentIndex++)
var currentCategory = char.GetUnicodeCategory(currentChar);
switch (currentCategory)
{
var currentChar = name[currentIndex];
if (currentChar == '_')
{
builder.Append('_');
previousCategory = null;
continue;
}
var currentCategory = char.GetUnicodeCategory(currentChar);
switch (currentCategory)
{
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.TitlecaseLetter:
if (previousCategory == UnicodeCategory.SpaceSeparator ||
@@ -63,13 +63,12 @@ namespace EFCore.NamingConventions.Internal
previousCategory = UnicodeCategory.SpaceSeparator;
}
continue;
}
builder.Append(currentChar);
previousCategory = currentCategory;
}
return builder.ToString();
builder.Append(currentChar);
previousCategory = currentCategory;
}
return builder.ToString();
}
}
}

View File

@@ -1,12 +1,11 @@
using System.Globalization;
namespace EFCore.NamingConventions.Internal
{
public class UpperCaseNameRewriter : INameRewriter
{
private readonly CultureInfo _culture;
namespace EFCore.NamingConventions.Internal;
public UpperCaseNameRewriter(CultureInfo culture) => _culture = culture;
public string RewriteName(string name) => name.ToUpper(_culture);
}
}
public class UpperCaseNameRewriter : INameRewriter
{
private readonly CultureInfo _culture;
public UpperCaseNameRewriter(CultureInfo culture) => _culture = culture;
public string RewriteName(string name) => name.ToUpper(_culture);
}

View File

@@ -1,13 +1,12 @@
using System.Globalization;
namespace EFCore.NamingConventions.Internal
namespace EFCore.NamingConventions.Internal;
public class UpperSnakeCaseNameRewriter : SnakeCaseNameRewriter
{
public class UpperSnakeCaseNameRewriter : SnakeCaseNameRewriter
{
private readonly CultureInfo _culture;
private readonly CultureInfo _culture;
public UpperSnakeCaseNameRewriter(CultureInfo culture) : base(culture) => _culture = culture;
public UpperSnakeCaseNameRewriter(CultureInfo culture) : base(culture) => _culture = culture;
public override string RewriteName(string name) => base.RewriteName(name).ToUpper(_culture);
}
}
public override string RewriteName(string name) => base.RewriteName(name).ToUpper(_culture);
}

View File

@@ -4,103 +4,102 @@ using JetBrains.Annotations;
using EFCore.NamingConventions.Internal;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore
namespace Microsoft.EntityFrameworkCore;
public static class NamingConventionsExtensions
{
public static class NamingConventionsExtensions
public static DbContextOptionsBuilder UseSnakeCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder , CultureInfo culture = null)
{
public static DbContextOptionsBuilder UseSnakeCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder , CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithSnakeCaseNamingConvention(culture);
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithSnakeCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseSnakeCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder , CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
public static DbContextOptionsBuilder UseLowerCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithLowerCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseLowerCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseLowerCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder ,culture);
public static DbContextOptionsBuilder UseUpperCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithUpperCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseUpperCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseUpperCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
public static DbContextOptionsBuilder UseUpperSnakeCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithUpperSnakeCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseUpperSnakeCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseUpperSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
public static DbContextOptionsBuilder UseCamelCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithCamelCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseCamelCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseCamelCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
return optionsBuilder;
}
}
public static DbContextOptionsBuilder<TContext> UseSnakeCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder , CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
public static DbContextOptionsBuilder UseLowerCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithLowerCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseLowerCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseLowerCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder ,culture);
public static DbContextOptionsBuilder UseUpperCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithUpperCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseUpperCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseUpperCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
public static DbContextOptionsBuilder UseUpperSnakeCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithUpperSnakeCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseUpperSnakeCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseUpperSnakeCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
public static DbContextOptionsBuilder UseCamelCaseNamingConvention(
[NotNull] this DbContextOptionsBuilder optionsBuilder, CultureInfo culture = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
var extension = (optionsBuilder.Options.FindExtension<NamingConventionsOptionsExtension>()
?? new NamingConventionsOptionsExtension())
.WithCamelCaseNamingConvention(culture);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseCamelCaseNamingConvention<TContext>(
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder, CultureInfo culture = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseCamelCaseNamingConvention((DbContextOptionsBuilder)optionsBuilder, culture);
}

View File

@@ -5,38 +5,37 @@ using EFCore.NamingConventions.Internal;
using JetBrains.Annotations;
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Extension methods for <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection" />.
/// </summary>
public static class NamingConventionsServiceCollectionExtensions
{
/// <summary>
/// Extension methods for <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection" />.
/// <para>
/// Adds the services required for applying naming conventions in Entity Framework Core.
/// You use this method when using dependency injection in your application, such as with ASP.NET.
/// For more information on setting up dependency injection, see http://go.microsoft.com/fwlink/?LinkId=526890.
/// </para>
/// <para>
/// You only need to use this functionality when you want Entity Framework to resolve the services it uses
/// from an external dependency injection container. If you are not using an external
/// dependency injection container, Entity Framework will take care of creating the services it requires.
/// </para>
/// </summary>
public static class NamingConventionsServiceCollectionExtensions
/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
public static IServiceCollection AddEntityFrameworkNamingConventions(
[NotNull] this IServiceCollection serviceCollection)
{
/// <summary>
/// <para>
/// Adds the services required for applying naming conventions in Entity Framework Core.
/// You use this method when using dependency injection in your application, such as with ASP.NET.
/// For more information on setting up dependency injection, see http://go.microsoft.com/fwlink/?LinkId=526890.
/// </para>
/// <para>
/// You only need to use this functionality when you want Entity Framework to resolve the services it uses
/// from an external dependency injection container. If you are not using an external
/// dependency injection container, Entity Framework will take care of creating the services it requires.
/// </para>
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
public static IServiceCollection AddEntityFrameworkNamingConventions(
[NotNull] this IServiceCollection serviceCollection)
{
Check.NotNull(serviceCollection, nameof(serviceCollection));
Check.NotNull(serviceCollection, nameof(serviceCollection));
new EntityFrameworkServicesBuilder(serviceCollection)
.TryAdd<IConventionSetPlugin, NamingConventionSetPlugin>();
new EntityFrameworkServicesBuilder(serviceCollection)
.TryAdd<IConventionSetPlugin, NamingConventionSetPlugin>();
return serviceCollection;
}
return serviceCollection;
}
}
}