1

In database I have tables in different schemas:

[marketing].[ClientsGroups]
[sales].[ClientsGroups]

tables

After I run command "Scaffold-DbContext ... ", I found 2 models

ClientsGroups.cs
ClientsGroups1.cs

models

They look the same and are completely confusing.

How I can add schema name to models names?

The database is constantly being updated and I have to execute the command "Scaffold-DbContext" periodically. In this regard, I am interested in a solution that can be used when reusing the "Scaffold-DbContext"command.

upd. to ErikEJ EF Core Power Tool Where is this option? screenshot of EF Core Power Tool options

5
  • Use EF Core Power Tools, with entity renaming Commented May 16 at 10:40
  • There's nothing wrong with doing some manual modifications after scaffolding. I.e. just rename the class(es). I consider scaffolding a one-time operation. That said, I would prefer different table names even in different schemas. Bugs and confusion are lurking here. Commented May 16 at 13:06
  • What version of EF (Core) are you using? Commented May 16 at 13:17
  • @GuruStron - 8.0.15 Commented May 20 at 6:41
  • @ErikEJ - Thank you for the quick reply. Can you answer in more detail? I did not find such functionality there, I added a screenshot to the question. Commented May 20 at 6:43

2 Answers 2

2

I found a solution that covers 90% of my needs. I haven't found yet is how to rename the generated files, and how to exclude auto-number from end of ambiguous names.

So, the solution. We need to install T4, following the instructions
https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates

dotnet new install Microsoft.EntityFrameworkCore.Templates
dotnet new ef-templates

Next, edit the T4 files by adding the schema name to the table names. And for me it work.

In short - I change:

EntityType.Name

To

EntityType.GetSchema() + "_" + EntityType.Name

In detail, my T4 files for Entity Framework Core 8 are below.

DbContext.t4:

<#@ template hostSpecific="true" #>
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
<#@ assembly name="Microsoft.Extensions.DependencyInjection.Abstractions" #>
<#@ parameter name="Model" type="Microsoft.EntityFrameworkCore.Metadata.IModel" #>
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
<#@ parameter name="NamespaceHint" type="System.String" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
<#@ import namespace="Microsoft.EntityFrameworkCore.Design" #>
<#@ import namespace="Microsoft.EntityFrameworkCore.Infrastructure" #>
<#@ import namespace="Microsoft.EntityFrameworkCore.Scaffolding" #>
<#@ import namespace="Microsoft.Extensions.DependencyInjection" #>

<#
    if (!ProductInfo.GetVersion().StartsWith("9.0"))
    {
        Warning("Your templates were created using an older version of Entity Framework. Additional features and bug fixes may be available. See https://aka.ms/efcore-docs-updating-templates for more information.");
    }

    var services = (IServiceProvider)Host;
    var providerCode = services.GetRequiredService<IProviderConfigurationCodeGenerator>();
    var annotationCodeGenerator = services.GetRequiredService<IAnnotationCodeGenerator>();
    var code = services.GetRequiredService<ICSharpHelper>();

    var usings = new List<string>
    {
        "System",
        "System.Collections.Generic",
        "Microsoft.EntityFrameworkCore"
    };

    if (NamespaceHint != Options.ModelNamespace
        && !string.IsNullOrEmpty(Options.ModelNamespace))
    {
        usings.Add(Options.ModelNamespace);
    }

    if (!string.IsNullOrEmpty(NamespaceHint))
    {
#>
namespace <#= NamespaceHint #>;

<#
    }
#>
public partial class <#= Options.ContextName #> : DbContext
{
<#
    if (!Options.SuppressOnConfiguring)
    {
#>
    public <#= Options.ContextName #>()
    {
    }

<#
    }
#>
    public <#= Options.ContextName #>(DbContextOptions<<#= Options.ContextName #>> options)
        : base(options)
    {
    }

<#
    foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
    {
#>
    public virtual DbSet<<#= FirstUp(entityType.GetSchema() ?? entityType.GetDefaultSchema()) + "_" + entityType.Name #>> <#= FirstUp(entityType.GetSchema() ?? entityType.GetDefaultSchema()) + "_" + entityType.GetDbSetName() #> { get; set; }

<#
    }

    if (!Options.SuppressOnConfiguring)
    {
#>
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
<#
    if (!Options.SuppressConnectionStringWarning)
    {
#>
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
<#
    }

    var useProviderCall = providerCode.GenerateUseProvider(Options.ConnectionString);
    usings.AddRange(useProviderCall.GetRequiredUsings());
#>
        => optionsBuilder<#= code.Fragment(useProviderCall, indent: 3) #>;

<#
    }

#>
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
<#
    var anyConfiguration = false;

    var modelFluentApiCalls = Model.GetFluentApiCalls(annotationCodeGenerator);
    if (modelFluentApiCalls != null)
    {
        usings.AddRange(modelFluentApiCalls.GetRequiredUsings());
#>
        modelBuilder<#= code.Fragment(modelFluentApiCalls, indent: 3) #>;
<#
        anyConfiguration = true;
    }

    StringBuilder mainEnvironment;
    foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
    {
        // Save all previously generated code, and start generating into a new temporary environment
        mainEnvironment = GenerationEnvironment;
        GenerationEnvironment = new StringBuilder();

        if (anyConfiguration)
        {
            WriteLine("");
        }

        var anyEntityTypeConfiguration = false;
#>
        modelBuilder.Entity<<#= FirstUp(entityType.GetSchema() ?? entityType.GetDefaultSchema()) + "_" + entityType.Name #>>(entity =>
        {
<#
        var key = entityType.FindPrimaryKey();
        if (key != null)
        {
            var keyFluentApiCalls = key.GetFluentApiCalls(annotationCodeGenerator);
            if (keyFluentApiCalls != null
                || (!key.IsHandledByConvention() && !Options.UseDataAnnotations))
            {
                if (keyFluentApiCalls != null)
                {
                    usings.AddRange(keyFluentApiCalls.GetRequiredUsings());
                }
#>
            entity.HasKey(<#= code.Lambda(key.Properties, "e") #>)<#= code.Fragment(keyFluentApiCalls, indent: 4) #>;
<#
                anyEntityTypeConfiguration = true;
            }
        }

        var entityTypeFluentApiCalls = entityType.GetFluentApiCalls(annotationCodeGenerator)
            ?.FilterChain(c => !(Options.UseDataAnnotations && c.IsHandledByDataAnnotations));
        if (entityTypeFluentApiCalls != null)
        {
            usings.AddRange(entityTypeFluentApiCalls.GetRequiredUsings());

            if (anyEntityTypeConfiguration)
            {
                WriteLine("");
            }
#>
            entity<#= code.Fragment(entityTypeFluentApiCalls, indent: 4) #>;
<#
            anyEntityTypeConfiguration = true;
        }

        foreach (var index in entityType.GetIndexes()
            .Where(i => !(Options.UseDataAnnotations && i.IsHandledByDataAnnotations(annotationCodeGenerator))))
        {
            if (anyEntityTypeConfiguration)
            {
                WriteLine("");
            }

            var indexFluentApiCalls = index.GetFluentApiCalls(annotationCodeGenerator);
            if (indexFluentApiCalls != null)
            {
                usings.AddRange(indexFluentApiCalls.GetRequiredUsings());
            }
#>
            entity.HasIndex(<#= code.Lambda(index.Properties, "e") #>, <#= code.Literal(index.GetDatabaseName()) #>)<#= code.Fragment(indexFluentApiCalls, indent: 4) #>;
<#
            anyEntityTypeConfiguration = true;
        }

        var firstProperty = true;
        foreach (var property in entityType.GetProperties())
        {
            var propertyFluentApiCalls = property.GetFluentApiCalls(annotationCodeGenerator)
                ?.FilterChain(c => !(Options.UseDataAnnotations && c.IsHandledByDataAnnotations)
                    && !(c.Method == "IsRequired" && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType));
            if (propertyFluentApiCalls == null)
            {
                continue;
            }

            usings.AddRange(propertyFluentApiCalls.GetRequiredUsings());

            if (anyEntityTypeConfiguration && firstProperty)
            {
                WriteLine("");
            }
#>
            entity.Property(e => e.<#= property.Name #>)<#= code.Fragment(propertyFluentApiCalls, indent: 4) #>;
<#
            anyEntityTypeConfiguration = true;
            firstProperty = false;
        }

        foreach (var foreignKey in entityType.GetForeignKeys())
        {
            var foreignKeyFluentApiCalls = foreignKey.GetFluentApiCalls(annotationCodeGenerator)
                ?.FilterChain(c => !(Options.UseDataAnnotations && c.IsHandledByDataAnnotations));
            if (foreignKeyFluentApiCalls == null)
            {
                continue;
            }

            usings.AddRange(foreignKeyFluentApiCalls.GetRequiredUsings());

            if (anyEntityTypeConfiguration)
            {
                WriteLine("");
            }
#>
            entity.HasOne(d => d.<#= foreignKey.DependentToPrincipal.Name #>).<#= foreignKey.IsUnique ? "WithOne" : "WithMany" #>(<#= foreignKey.PrincipalToDependent != null ? $"p => p.{foreignKey.PrincipalToDependent.Name}" : "" #>)<#= code.Fragment(foreignKeyFluentApiCalls, indent: 4) #>;
<#
            anyEntityTypeConfiguration = true;
        }

        foreach (var skipNavigation in entityType.GetSkipNavigations().Where(n => n.IsLeftNavigation()))
        {
            if (anyEntityTypeConfiguration)
            {
                WriteLine("");
            }

            var left = skipNavigation.ForeignKey;
            var leftFluentApiCalls = left.GetFluentApiCalls(annotationCodeGenerator, useStrings: true);
            var right = skipNavigation.Inverse.ForeignKey;
            var rightFluentApiCalls = right.GetFluentApiCalls(annotationCodeGenerator, useStrings: true);
            var joinEntityType = skipNavigation.JoinEntityType;

            if (leftFluentApiCalls != null)
            {
                usings.AddRange(leftFluentApiCalls.GetRequiredUsings());
            }

            if (rightFluentApiCalls != null)
            {
                usings.AddRange(rightFluentApiCalls.GetRequiredUsings());
            }
#>
            entity.HasMany(d => d.<#= skipNavigation.Name #>).WithMany(p => p.<#= skipNavigation.Inverse.Name #>)
                .UsingEntity<Dictionary<string, object>>(
                    <#= code.Literal(joinEntityType.Name) #>,
                    r => r.HasOne<<#= FirstUp(right.PrincipalEntityType.GetSchema() ?? right.PrincipalEntityType.GetDefaultSchema()) + "_" + right.PrincipalEntityType.Name #>>().WithMany()<#= code.Fragment(rightFluentApiCalls, indent: 6) #>,
                    l => l.HasOne<<#= FirstUp(left.PrincipalEntityType.GetSchema() ?? left.PrincipalEntityType.GetDefaultSchema()) + "_" + left.PrincipalEntityType.Name #>>().WithMany()<#= code.Fragment(leftFluentApiCalls, indent: 6) #>,
                    j =>
                    {
<#
            var joinKey = joinEntityType.FindPrimaryKey();
            var joinKeyFluentApiCalls = joinKey.GetFluentApiCalls(annotationCodeGenerator);

            if (joinKeyFluentApiCalls != null)
            {
                usings.AddRange(joinKeyFluentApiCalls.GetRequiredUsings());
            }
#>
                        j.HasKey(<#= code.Arguments(joinKey.Properties.Select(e => e.Name)) #>)<#= code.Fragment(joinKeyFluentApiCalls, indent: 7) #>;
<#
            var joinEntityTypeFluentApiCalls = joinEntityType.GetFluentApiCalls(annotationCodeGenerator);
            if (joinEntityTypeFluentApiCalls != null)
            {
                usings.AddRange(joinEntityTypeFluentApiCalls.GetRequiredUsings());
#>
                        j<#= code.Fragment(joinEntityTypeFluentApiCalls, indent: 7) #>;
<#
            }

            foreach (var index in joinEntityType.GetIndexes())
            {
                var indexFluentApiCalls = index.GetFluentApiCalls(annotationCodeGenerator);
                if (indexFluentApiCalls != null)
                {
                    usings.AddRange(indexFluentApiCalls.GetRequiredUsings());
                }
#>
                        j.HasIndex(<#= code.Literal(index.Properties.Select(e => e.Name).ToArray()) #>, <#= code.Literal(index.GetDatabaseName()) #>)<#= code.Fragment(indexFluentApiCalls, indent: 7) #>;
<#
            }

            foreach (var property in joinEntityType.GetProperties())
            {
                var propertyFluentApiCalls = property.GetFluentApiCalls(annotationCodeGenerator);
                if (propertyFluentApiCalls == null)
                {
                    continue;
                }

                usings.AddRange(propertyFluentApiCalls.GetRequiredUsings());
#>
                        j.IndexerProperty<<#= code.Reference(property.ClrType) #>>(<#= code.Literal(property.Name) #>)<#= code.Fragment(propertyFluentApiCalls, indent: 7) #>;
<#
            }
#>
                    });
<#
            anyEntityTypeConfiguration = true;
        }
#>
        });
<#
        // If any signicant code was generated, append it to the main environment
        if (anyEntityTypeConfiguration)
        {
            mainEnvironment.Append(GenerationEnvironment);
            anyConfiguration = true;
        }

        // Resume generating code into the main environment
        GenerationEnvironment = mainEnvironment;
    }

    foreach (var sequence in Model.GetSequences())
    {
        var needsType = sequence.Type != typeof(long);
        var needsSchema = !string.IsNullOrEmpty(sequence.Schema) && sequence.Schema != sequence.Model.GetDefaultSchema();
        var sequenceFluentApiCalls = sequence.GetFluentApiCalls(annotationCodeGenerator);
#>
        modelBuilder.HasSequence<#= needsType ? $"<{code.Reference(sequence.Type)}>" : "" #>(<#= code.Literal(sequence.Name) #><#= needsSchema ? $", {code.Literal(sequence.Schema)}" : "" #>)<#= code.Fragment(sequenceFluentApiCalls, indent: 3) #>;
<#
    }

    if (anyConfiguration)
    {
        WriteLine("");
    }
#>
        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
<#
    mainEnvironment = GenerationEnvironment;
    GenerationEnvironment = new StringBuilder();

    foreach (var ns in usings.Distinct().OrderBy(x => x, new NamespaceComparer()))
    {
#>
using <#= ns #>;
<#
    }

    WriteLine("");

    GenerationEnvironment.Append(mainEnvironment);
#>

<#+
    private string FirstUp(string name)
    {
        if (name is null) return name;
        return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name);
    }
#>

EntityType.t4:

<#@ template hostSpecific="true" #>
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
<#@ assembly name="Microsoft.Extensions.DependencyInjection.Abstractions" #>
<#@ parameter name="EntityType" type="Microsoft.EntityFrameworkCore.Metadata.IEntityType" #>
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
<#@ parameter name="NamespaceHint" type="System.String" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.ComponentModel.DataAnnotations" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
<#@ import namespace="Microsoft.EntityFrameworkCore.Design" #>
<#@ import namespace="Microsoft.Extensions.DependencyInjection" #>
<#
    if (EntityType.IsSimpleManyToManyJoinEntityType())
    {
        // Don't scaffold these
        return "";
    }

    var services = (IServiceProvider)Host;
    var annotationCodeGenerator = services.GetRequiredService<IAnnotationCodeGenerator>();
    var code = services.GetRequiredService<ICSharpHelper>();

    var usings = new List<string>
    {
        "System",
        "System.Collections.Generic"
    };

    if (Options.UseDataAnnotations)
    {
        usings.Add("System.ComponentModel.DataAnnotations");
        usings.Add("System.ComponentModel.DataAnnotations.Schema");
        usings.Add("Microsoft.EntityFrameworkCore");
    }

    if (!string.IsNullOrEmpty(NamespaceHint))
    {
#>
namespace <#= NamespaceHint #>;

<#
    }

    if (!string.IsNullOrEmpty(EntityType.GetComment()))
    {
#>
/// <summary>
/// <#= code.XmlComment(EntityType.GetComment()) #>
/// </summary>
<#
    }

    if (Options.UseDataAnnotations)
    {
        foreach (var dataAnnotation in EntityType.GetDataAnnotations(annotationCodeGenerator))
        {
#>
<#= code.Fragment(dataAnnotation) #>
<#
        }
    }
#>
public partial class <#= FirstUp(EntityType.GetSchema() ?? EntityType.GetDefaultSchema()) + "_" + EntityType.Name  #>
{ 
<#
    var firstProperty = true;
    foreach (var property in EntityType.GetProperties().OrderBy(p => p.GetColumnOrder() ?? -1))
    {
        if (!firstProperty)
        {
            WriteLine("");
        }

        if (!string.IsNullOrEmpty(property.GetComment()))
        {
#>
    /// <summary>
    /// <#= code.XmlComment(property.GetComment(), indent: 1) #>
    /// </summary>
<#
        }

        if (Options.UseDataAnnotations)
        {
            var dataAnnotations = property.GetDataAnnotations(annotationCodeGenerator)
                .Where(a => !(a.Type == typeof(RequiredAttribute) && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType));
            foreach (var dataAnnotation in dataAnnotations)
            {
#>
    <#= code.Fragment(dataAnnotation) #>
<#
            }
        }

        usings.AddRange(code.GetRequiredUsings(property.ClrType));

        var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !property.ClrType.IsValueType;
        var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !property.ClrType.IsValueType;
#>
    public <#= code.Reference(property.ClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
        firstProperty = false;
    }

    foreach (var navigation in EntityType.GetNavigations())
    {
        WriteLine("");

        if (Options.UseDataAnnotations)
        {
            foreach (var dataAnnotation in navigation.GetDataAnnotations(annotationCodeGenerator))
            {
#>
    <#= code.Fragment(dataAnnotation) #>
<#
            }
        }

        var targetType = FirstUp(navigation.TargetEntityType.GetSchema() ?? navigation.TargetEntityType.GetDefaultSchema()) + "_" + navigation.TargetEntityType.Name;
        if (navigation.IsCollection)
        {
#>
    public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; set; } = new List<<#= targetType #>>();
<#
        }
        else
        {
            var needsNullable = Options.UseNullableReferenceTypes && !(navigation.ForeignKey.IsRequired && navigation.IsOnDependent);
            var needsInitializer = Options.UseNullableReferenceTypes && navigation.ForeignKey.IsRequired && navigation.IsOnDependent;
#>
    public virtual <#= targetType #><#= needsNullable ? "?" : "" #> <#= navigation.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
        }
    }

    foreach (var skipNavigation in EntityType.GetSkipNavigations())
    {
        WriteLine("");

        if (Options.UseDataAnnotations)
        {
            foreach (var dataAnnotation in skipNavigation.GetDataAnnotations(annotationCodeGenerator))
            {
#>
    <#= code.Fragment(dataAnnotation) #>
<#
            }
        }
        //skipNavigation.TargetEntityType.Name = FirstUp(skipNavigation.TargetEntityType.GetSchema() ?? skipNavigation.TargetEntityType.GetDefaultSchema()) + "_" + skipNavigation.TargetEntityType.Name;
#>
    public virtual ICollection<<#= FirstUp(skipNavigation.TargetEntityType.GetSchema() ?? skipNavigation.TargetEntityType.GetDefaultSchema()) +"_"+ skipNavigation.TargetEntityType.Name #>> <#= skipNavigation.Name #> { get; set; } = new List<<#=FirstUp(skipNavigation.TargetEntityType.GetSchema() ?? skipNavigation.TargetEntityType.GetDefaultSchema()) +"_"+  skipNavigation.TargetEntityType.Name #>>();
<#
    }
#>
}
<#
    var previousOutput = GenerationEnvironment;
    GenerationEnvironment = new StringBuilder();

    foreach (var ns in usings.Distinct().OrderBy(x => x, new NamespaceComparer()))
    {
#>
using <#= ns #>;
<#
    }

    WriteLine("");

    GenerationEnvironment.Append(previousOutput);
#>

<#+
    private string FirstUp(string name)
    {
        if (name is null) return name;
        return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name);
    }
#>

Sign up to request clarification or add additional context in comments.

Comments

-1

When you're using Scaffold-DbContext in Entity Framework Core and your database has tables with the same name but in different schemas (like [marketing].[ClientsGroups] and [sales].[ClientsGroups]), EF Core by default will generate two classes with the same name, which causes conflicts.

To fix this, and to include the schema name in the class name (so you get something like MarketingClientsGroups and SalesClientsGroups), you can’t do it directly through a simple switch in the Scaffold-DbContext command.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.