Skip to content

SQL Server

Simon Hughes edited this page Aug 30, 2026 · 2 revisions

SQL Server

SQL Server is the reference implementation. Everything the generator can do, it can do here.

Practical floor is SQL Server 2012, because sequence reading uses sys.sequences. Against anything older the sequence read fails on its own and the tool returns partial results rather than giving up; everything else still comes through.

v4 note. If you used v3 you may remember editing machine.config or running gacutil for the other dialects. None of that applies any more, to any database. The efrpg tool ships Microsoft.Data.SqlClient as an ordinary NuGet dependency. Install the tool and you are done: dotnet tool install -g Efrpg.

Settings

Settings.DatabaseType     = DatabaseType.SqlServer;
Settings.TemplateType     = TemplateType.EfCore10;   // or EfCore9, EfCore8, Ef6
Settings.GeneratorType    = GeneratorType.EfCore;    // or GeneratorType.Ef6
Settings.ConnectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=True;Encrypt=false;TrustServerCertificate=true";

Your project needs Microsoft.EntityFrameworkCore.SqlServer (EF Core) or EntityFramework (EF 6).

For Azure SQL, Entra ID and the other connection string shapes, see Connection strings.

Permissions

db_datareader plus VIEW DEFINITION on the database is enough to read tables, views, columns, keys, indexes, triggers, sequences, synonyms and extended properties.

EXECUTE on your stored procedures is additionally required if you want return models generated for them (see below). db_ddladmin is the blunt instrument that covers everything, if you are stuck arguing with a DBA.

What gets read

Feature Supported Notes
Tables, views, columns
Primary and foreign keys Including composite and self-referencing
Indexes Including unique, clustered and INCLUDE columns
Identity columns
Computed columns Given a private setter when Settings.UsePrivateSetterForComputedColumns = true
Default constraints Emitted as property initialisers, and as .HasDefaultValueSql() when Settings.GenerateHasDefaultValueSql = true
Extended properties ✔ (not Azure SQL) Emitted as XML doc comments and readable in your own callbacks. See Extended Property Names Feature
Stored procedures Including multiple result sets, and input/output parameters
Table-valued functions EF 6 also needs the EntityFramework.CodeFirstStoreFunctions NuGet package
Scalar-valued functions
Synonyms Including cross-database synonyms. Opt in with FilterSettings.IncludeSynonyms = true
Sequences Wire them to identity columns with Settings.HiLoSequences
Triggers Emitted as .ToTable(tb => tb.HasTrigger("...")) where EF Core needs it
Temporal tables SQL Server 2016+ Detected from the server version
Memory-optimised tables Via sys.tables.is_memory_optimized
json and vector types SQL Server 2025+ / Azure SQL Detected from the server version
Spatial types See Spatial Types
hierarchyid See HierarchyId
rowversion / timestamp See RowVersion and TimeStamp columns
Multi-context settings tables SQL Server only. See Generating Multiple Database Contexts in a Single Go

Azure SQL Database does not report extended properties. That read is skipped on Azure, so column descriptions will not appear as comments. Everything else works normally.

How stored procedure return models are discovered

This one surprises people, so it is worth stating plainly.

To work out what a stored procedure returns, the generator executes it with SET FMTONLY ON. In that mode SQL Server returns column metadata without running the statements, so no rows are produced and nothing is written. It is a metadata call, not a real execution - but it does mean the connecting account needs EXECUTE.

SET FMTONLY is an old feature with old edges. Procedures that build temp tables, or use dynamic SQL, can report the wrong shape or no shape at all. Four escape hatches, in the order I would try them:

  1. Tell the generator the procedure returns an entity you already have:

    Settings.StoredProcedureReturnTypes.Add("SalesByYear", "SummaryOfSalesByYear");
  2. Declare the shape by hand in Settings.ReadStoredProcReturnObjectException. See Stored Procedure Return Model Errors.

  3. Exclude just that procedure:

    FilterSettings.StoredProcedureFilters.Add(new RegexExcludeFilter("^usp_Awkward$"));
  4. Turn off procedure reading entirely:

    FilterSettings.IncludeStoredProcedures      = false;
    FilterSettings.IncludeTableValuedFunctions  = false;
    FilterSettings.IncludeScalarValuedFunctions = false;

Option 4 is also the single biggest speed-up available on a large database.

Schemas

Multiple schemas are read in one pass. By default the schema name is prepended to the generated class name so that dbo.Order and sales.Order do not collide:

  • dbo.Hello becomes Hello - dbo is never prepended
  • abc.Hello becomes abc_Hello

Note the schema keeps the case the database gives it. It is prepended verbatim, unlike the table name, which is singularised and PascalCased.

Turn it off with Settings.PrependSchemaName = false, or control it per table with Settings.PrependSchemaNameForTable.

To read only some schemas:

FilterSettings.SchemaFilters.Add(new RegexIncludeFilter("^dbo$|^sales$"));

Generation is slow

Two things to try, in this order:

  1. Turn off stored procedures, TVFs and scalar functions as shown above. Reading procedure result shapes is by far the slowest part of a run, and this usually accounts for all of it.
  2. Update the statistics on the system tables. A real, well-documented SQL Server problem, and the fix takes seconds: Speed up Reverse generating by updating statistics on sys tables.

On SQL Server 2014 specifically there was a faulty query plan optimisation that could make schema reads appear to hang. If you are still on it, set Settings.IncludeQueryTraceOn9481Flag = true (you will need elevated privileges).

Filtering down to the tables you actually need is worth doing regardless: a DbContext with 40 entities builds its model faster at run time than one with 900. See Filtering.

See also

Clone this wiki locally