Skip to content

Settings.PrependSchemaNameForTable

Simon Hughes edited this page Aug 30, 2026 · 1 revision

Settings.PrependSchemaNameForTable

Decides, table by table, whether the schema name is prepended to the generated class name.

Type Func<Table, bool> - return true to prepend, false to leave the name alone
Default Returns true for every table
Applies to EF 6 and EF Core
Databases Databases with real schemas: SQL Server, PostgreSQL, Oracle. MySQL has no schema within a database, and SQLite is always main
In Database.tt? No. You have to add it yourself

This setting is not in the shipped Database.tt, so most people never find out it exists. If you have ever turned Settings.PrependSchemaName off entirely because one schema was annoying you, this is the setting you wanted instead.

What it does

When two schemas contain a table with the same name, dbo.Order and sales.Order cannot both generate a class called Order. So by default the generator prepends the schema to every table outside the default schema: sales.Order becomes sales_Order.

That is the right call when there is a genuine clash, and clumsy when there is not. sales_Order, sales_Customer, sales_Invoice reads badly if nothing in dbo shares those names.

Settings.PrependSchemaName is the blunt version - it turns prepending off for every table at once, and then a real clash silently generates two classes with the same name. PrependSchemaNameForTable is the per-table version: keep prepending on, and exempt the tables where you know there is no clash.

The callback is only consulted for tables outside the default schema. Tables in the default schema (dbo on SQL Server, public on PostgreSQL) are never prepended and never reach it.

Example

The sample schema has dbo.Category, dbo.Product and sales.Order.

Default - every non-default schema is prepended

public interface IMyDbContext : IDisposable
public class MyDbContext : DbContext, IMyDbContext
public class MyDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
public class Category
public class Product
public class sales_Order
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
public class ProductConfiguration : IEntityTypeConfiguration<Product>
public class sales_OrderConfiguration : IEntityTypeConfiguration<sales_Order>

Exempting the sales schema

Settings.PrependSchemaNameForTable = delegate(Table table)
{
    // Nothing in dbo is called Order, so the prefix buys nothing here
    return !table.Schema.DbName.Equals("sales", StringComparison.OrdinalIgnoreCase);
};
public interface IMyDbContext : IDisposable
public class MyDbContext : DbContext, IMyDbContext
public class MyDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
public class Category
public class Order
public class Product
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
public class OrderConfiguration : IEntityTypeConfiguration<Order>
public class ProductConfiguration : IEntityTypeConfiguration<Product>

sales_Order became Order. The database mapping is untouched - the configuration still says builder.ToTable("Order", "sales") - so this only changes the C# name.

When to use it

  • One tidy schema among several. Exempt the one you use constantly, keep the prefix on the rest.
  • A legacy schema you are migrating away from. Prefix the old one, leave the new one clean.
  • Reserved-word schemas. A schema literally named dbo in a non-SQL-Server database, or one whose name is not a legal C# identifier prefix.

A common shape is an allow-list:

Settings.PrependSchemaNameForTable = table =>
{
    var clean = new[] { "sales", "reporting" };
    return !clean.Contains(table.Schema.DbName, StringComparer.OrdinalIgnoreCase);
};

Gotchas

Name clashes become your problem. Exempt sales while dbo.Order also exists and you generate two classes called Order in one namespace. The generator does not detect this; the C# compiler does, and its error points at the generated file.

The schema is prepended exactly as the database spells it, not PascalCased. sales.Order becomes sales_Order, not Sales_Order, and the singularisation still applies to the table part. If you want a different shape entirely, use Settings.TableRename instead - it runs earlier and controls the whole name.

Settings.PrependSchemaName = false overrides this callback completely. The callback is only reached when PrependSchemaName is true. If prepending is off globally, this delegate never runs.

Foreign key navigation properties do not consult it. The class name a foreign key points at is derived separately and honours Settings.PrependSchemaName but not this per-table callback, so a relationship crossing into an exempted schema can still name the prefixed form. Check the generated navigation properties after exempting a schema that other schemas point at.

Stored procedures have their own callback, Settings.PrependSchemaNameForStoredProcedure, with the same shape and the same default.

See also

Clone this wiki locally