Schema Provider (Sergen)
The schema provider is the abstraction Sergen uses to read database metadata — tables, columns, primary keys, identity columns, and foreign keys — so it can generate rows, forms, grids, pages, and services from an existing database. It is an internal piece of the code generator, but understanding it helps when you want to know how Sergen discovers tables and columns, or when you need to support a database that Serenity does not ship a provider for.
When Sergen reads the schema
When you run dotnet sergen generate, the first thing Sergen does is open the
selected connection and ask the schema provider for the list of tables:
using (var connection = sqlConnections.NewByKey(connectionKey))
{
schemaProvider = SchemaHelper.GetSchemaProvider(connection.GetDialect().ServerType);
allTableEntries = schemaProvider.GetTableNames(connection).ToList();
}
The "Available Tables" list you see in the interactive wizard comes from
GetTableNames. Once you pick a table, the same provider supplies the column
metadata used to build the row, form, grid, page, and service code.
ISchemaProvider
ISchemaProvider
is the interface every provider implements. It has six members:
| Member | Returns | Purpose |
|---|---|---|
DefaultSchema |
string |
The default schema for the server (e.g. dbo for SQL Server) |
GetTableNames(connection) |
IEnumerable<TableName> |
All tables and views in the database |
GetFieldInfos(connection, schema, table) |
IEnumerable<FieldInfo> |
Column metadata for a table |
GetPrimaryKeyFields(connection, schema, table) |
IEnumerable<string> |
Primary key column names |
GetIdentityFields(connection, schema, table) |
IEnumerable<string> |
Identity / auto-increment column names |
GetForeignKeys(connection, schema, table) |
IEnumerable<ForeignKeyInfo> |
Foreign keys referencing other tables |
Metadata types
The provider returns three small metadata types.
TableName
TableName
describes a table or view:
Schema— schema nameTable— table nameIsView— whether it is a viewTablename— convenienceSchema.Table(or justTablewhen there is no schema)
FieldInfo
FieldInfo
describes a single column:
FieldName,DataType,Size,ScaleIsPrimaryKey,IsIdentity,IsNullablePKSchema,PKTable,PKColumn— when the column is a foreign key, the referenced table and column
ForeignKeyInfo
ForeignKeyInfo
describes a foreign key:
FKName,FKColumn— the constraint and column on the current tablePKSchema,PKTable,PKColumn— the referenced table and column
Built-in providers
Serenity ships a provider for each supported server, in the
Serenity.Data.Schema namespace:
| Provider | Server | Default schema |
|---|---|---|
SqlServerSchemaProvider |
SQL Server | dbo |
MySqlSchemaProvider |
MySQL | — |
PostgresSchemaProvider |
PostgreSQL | public |
SqliteSchemaProvider |
SQLite | — |
OracleSchemaProvider |
Oracle | — |
FirebirdSchemaProvider |
Firebird | — |
Each provider runs database-specific metadata queries. For example,
SqlServerSchemaProvider.GetFieldInfos reads INFORMATION_SCHEMA.COLUMNS and
normalizes the SQL type (e.g. timestamp → rowversion) and nullability:
SELECT
COLUMN_NAME [FieldName],
CASE WHEN DATA_TYPE = 'timestamp' THEN 'rowversion' ELSE DATA_TYPE END [DataType],
CASE WHEN IS_NULLABLE = 'NO' THEN 0 ELSE 1 END [IsNullable],
COALESCE(CHARACTER_MAXIMUM_LENGTH,
CASE WHEN DATA_TYPE in ('decimal', 'money', 'numeric')
THEN NUMERIC_PRECISION ELSE 0 END) [Size],
NUMERIC_SCALE [Scale]
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @sma AND TABLE_NAME = @tbl
ORDER BY ORDINAL_POSITION
How Sergen picks a provider
Sergen does not ask you which provider to use. It derives it from the
connection's dialect: SchemaHelper.GetSchemaProvider receives the dialect's
ServerType and looks up a type named
Serenity.Data.Schema.<ServerType>SchemaProvider:
public static ISchemaProvider GetSchemaProvider(string serverType)
{
var providerType = Type.GetType("Serenity.Data.Schema." + serverType + "SchemaProvider, Serenity.Net.Services") ??
Type.GetType("Serenity.Data.Schema." + serverType + "SchemaProvider, SerenityData");
if (providerType == null || !typeof(ISchemaProvider).GetTypeInfo().IsAssignableFrom(providerType))
throw new ArgumentOutOfRangeException(nameof(serverType), serverType, "Unknown server type");
return (ISchemaProvider)Activator.CreateInstance(providerType);
}
So the provider is chosen by the connection's dialect ServerType (see
SQL Dialects). If you set the
dialect for a connection in appsettings.json, Sergen uses the matching
provider.
From metadata to generated code
The provider is wrapped by EntityDataSchema (behind the IEntityDataSchema
interface) and consumed by EntityModelFactory when it builds the row model:
GetFieldInfosproduces the row's fields, with[Size],[Scale],[NotNull], etc. derived from the column metadata.GetPrimaryKeyFields/GetIdentityFieldsdetermine the[PrimaryKey],[Identity]/[AutoIncrement]attributes and the row'sIdField.GetForeignKeysfinds columns that reference other tables. For each single-column foreign key, Sergen pulls the referenced table's fields in as view fields (with[ForeignKey],[LeftJoin], etc.), which is why generated rows include fields from related tables.
Two sergen.json options control how this foreign-key expansion behaves:
RemoveForeignFields— a list of field names to drop from foreign joins (e.g. logging fields likeInsertUserId,UpdateDate).BaseRowClasses— if a table has all the fields listed for a base row class (e.g.LoggingRow), Sergen derives the row from that class and does not regenerate those fields.
Custom providers
ISchemaProvider is a public interface, so you can implement your own provider
if you need to generate code from a database Serenity does not ship a provider
for. Because SchemaHelper.GetSchemaProvider resolves providers by the
<ServerType>SchemaProvider naming convention, a custom provider must be named
Serenity.Data.Schema.<ServerType>SchemaProvider and be discoverable by that
reflection lookup.