Open-source project
fsprojects/SQLProvider avatar
fsprojects/SQLProvider

SQLProvider: a type provider that turns a database schema into F# types

A general F# SQL database erasing type provider, supporting LINQ queries, schema exploration, individuals, CRUD operations and much more besides.

627 stars148 forksF#NOASSERTION

At a glance

What is it?
SQLProvider is an F# type provider that compiles your database schema into .NET types at compile time, so you query a live SQL Server, PostgreSQL, Oracle, MySQL, SQLite, Firebird, DuckDB or Access database with LINQ and get statically typed records. Each vendor has its own NuGet package and its own provider class, and the project is maintained by the F# community project.
Who is it for?
Adopt SQLProvider if you are working in F# and want database access where a schema change is a compile error rather than a runtime surprise, and if your database is on the list of explicit vendor implementations so you get the full feature set including lazy schema exploration and automatic constraint navigation. Do not adopt it if you are outside F#, and do not adopt it for a vendor outside the list without accepting the manual configuration path and its narrower features.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly F#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What a type provider actually buys you here

The README summarises the project as a general .NET SQL database type provider, and the example image makes the claim in one caption: no OR-mapping, F# compiles your database to .NET types. That is the whole idea in a sentence, and it is worth being precise about what it means in practice.

A type provider is a compiler plugin that runs at compile time, inspects something, and generates types from what it found. Here the something is your database schema, and what it generates is a set of F# record types and a query interface bound to the live database. The consequence is that a column renamed in the database becomes a compile error in your F# code rather than a runtime null or an exception.

The feature list covers considerably more than query typing. There are LINQ queries, lazy schema exploration so the schema is only read when needed, automatic constraint navigation so foreign keys become navigable properties, individuals for the ActiveRecord style of record manipulation, transactional CRUD operations with identity support, two-way data binding, stored procedures, functions, packages for Oracle, composable query integration, optional option types so a nullable column is typed as an option, mapping to record types, custom operators, asynchronous operations, .NET Standard and .NET Core support, and the ability to save the database schema offline or as SQL Server dacpac files.

The last of those is worth pausing on, because it points at a workflow that the other features do not. If you can export the schema offline, then the type generation can be driven from a file rather than from a live connection, which is what makes it usable in a build pipeline that has no database credentials.

The project is F# first, with a general FSharp.Data.Sql.SqlDataProvider base and per-vendor subclasses. The homepage is fsprojects.github.io/SQLProvider, the licence is a custom one that GitHub cannot classify, and the last push was on 2026-10-17 with the project not archived.

Nine databases, and why each one is a separate package

The README is explicit that the provider has explicit implementations for a list of vendors: SQL Server, SQLite, PostgreSQL, Oracle, MySQL and MariaDB, MsAccess, Firebird and DuckDB. There is also an ODBC provider that will let you connect to any ODBC source with limited features.

The packaging follows from that. There is one NuGet package per database, each with its own type provider class in the FSharp.Data.Sql namespace, and the table in the README gives the exact class name for each. SQL Server is FSharp.Data.Sql.MsSql.SqlDataProvider, PostgreSQL is FSharp.Data.Sql.PostgreSql.SqlDataProvider, Oracle is FSharp.Data.Sql.Oracle.SqlDataProvider, SQLite is FSharp.Data.Sql.SQLite.SqlDataProvider, Firebird is FSharp.Data.Sql.Firebird.SqlDataProvider, DuckDB is FSharp.Data.Sql.DuckDb.SqlDataProvider, and Access is FSharp.Data.Sql.MsAccess.SqlDataProvider.

MySQL is the interesting case, because it has two. There is SQLProvider.MySqlConnector with the class FSharp.Data.Sql.MySqlConnector.SqlDataProvider, and SQLProvider.MySql with FSharp.Data.Sql.MySql.SqlDataProvider. The split reflects the two different client stacks available, and the two providers are not interchangeable, so choosing the package is a real decision rather than a formality.

There is also a general package, SQLProvider itself with the class FSharp.Data.Sql.SqlDataProvider, described as the option for using it generally, all via manual config. That is the one to reach for when you are on a database that has no dedicated package, and it is the path the ODBC provider also takes.

So the choice for a new project is straightforward: take the vendor package if there is one, because it gives you the full feature set including schema exploration, and fall back to the general package with manual configuration when there is not. The trade-off of the general package is that you configure the connection and the schema by hand rather than having it discovered.

The DuckDB and Firebird entries are worth noting as a signal about where the project is going. Both are analytical and embedded databases rather than traditional servers, and both being supported suggests the provider is not only aimed at line-of-business transactional databases.

The known issue with dynamic assembly loading, and the fix

The known issues section contains one item, and it is a real one. Database vendors other than SQL Server and Access use dynamic assembly loading, and the README says this may cause some security problems depending on your system's configuration and which version of the .NET framework you are using. The advice for anyone who hits problems loading dynamic assemblies is to apply a configuration element to the configuration files of fsi.exe, devenv.exe and your program or the program using your library, with a link to the Microsoft documentation for the relevant setting.

This is a genuine architectural consequence rather than a small bug. A type provider for SQL Server can generate a strongly typed assembly at compile time, because the SQL Server client libraries are available as a normal reference. The other vendors load their client library dynamically at runtime, so the provider has to bring in the ADO.NET provider for that database at the moment the types are used. That is why the exception is exactly two vendors, and it is why the fix is a configuration change rather than a code change: you have to tell the runtime that loading those assemblies is permitted.

The practical advice for an adopter is straightforward. If you deploy to a locked-down environment with a restrictive .NET configuration, check this before you commit to a vendor other than SQL Server or Access, and apply the configuration element to every host that will load the library, not just your development machine. The developer tool fsi.exe and the IDE devenv.exe are named separately because the configuration that permits the load has to be present in each of those hosts independently.

The second build note is also worth understanding, because it affects how you compile the project itself. On Linux and macOS you run build.sh, and on Windows build.cmd, and both use the dotnet command underneath. If you need to build with .NET Framework v4.x or Mono, you rename the .paket_netfx file to .paket first, before restoring and building manually with paket and msbuild.

The repository tree explains why there is a .paket_netfx directory at all. The project uses paket for dependency management, with a paket.dependencies file, a paket.lock, a build.fsx script, and separate solution files, SQLProvider.sln and SQLProvider.Tests.sln, plus a .devcontainer directory and a global.json pinning the SDK. The .paket_netfx variant exists so the .NET Framework build path can have its own dependency resolution without disturbing the main one.

Schema exploration, offline schema and what they enable

Three features interlock here, and together they are what makes the provider usable in a build pipeline rather than only in an interactive session.

Schema exploration is lazy, meaning the schema is read when it is needed rather than eagerly at startup. That matters for two reasons. It keeps start-up fast on large schemas, and it means the connection parameters can be supplied at the point of use.

Saving the database schema offline goes further. If the schema can be persisted, then the type generation can be driven from a file rather than from a live connection, so a CI build does not need credentials for the production database to compile code against its schema. The same idea appears in the dacpac support, since SQL Server dacpac files are another way to carry a schema snapshot.

Automatic constraint navigation is the feature that changes how the code reads. Because foreign keys are known from the schema, related rows become reachable as properties on the record rather than through a hand-written join. Combined with individuals, which give you the ActiveRecord style of insert, update and delete, and with transactional CRUD operations with identity support, the provider covers a large part of what an application usually hand-writes.

The remaining items on the list are about filling the gaps between a query surface and a real application. Stored procedures and functions are exposed rather than left to raw ADO.NET calls. Optional option types mean a nullable column is typed as an option, so the compiler forces you to handle the null case. Mapping to record types lets a query result be projected into your own types. Custom operators extend the query language. Asynchronous operations are supported, and composable query integration means the provider can feed a wider query-composition pipeline rather than being an island.

Two-way data binding is on the list as well, which is unusual for a server-side data library and points at the F# and Windows desktop heritage of the project.

Where the project sits, and who maintains it

The maintainers are listed as @pezipink, @colinbull and @Thorium, and the README adds that the default maintainer account for projects under fsprojects is @fsprojectsgit, the F# Community Project Incubation Space for repository management. That is the governance context: this is a community project, which is why the master branch is the default rather than main, and why the repository carries an fsharplint.json file and a .config/ directory for linting configuration.

The repository has no GitHub releases. The distribution channel is NuGet, and the README's table links each vendor package to its NuGet page with download and version badges. The project page at fsprojects.github.io/SQLProvider holds the core documentation and samples, and that documentation originates from the docs/content/ directory in the repository, with the README explicitly inviting pull requests for typo fixes and additional samples and documentation.

That last point is the useful one for a would-be contributor. The documentation lives in the repository rather than in a separate wiki, so the same pull request that fixes a sample can also fix the page, and the review path is the same. The README also carries a Gitter chat badge for the community, and the issue and pull request status badges at the top are the ordinary GitHub ones.

The licence is the one loose end in the documentation. The repository licence field is recorded as a custom licence that GitHub cannot classify, and the tree contains a LICENSE.txt file, so the actual terms live in that file rather than in a well-known identifier. For a library that sits in the middle of a data access layer, that is the file to read before you adopt it into a commercial codebase.

How to evaluate it, and what to watch for

The evaluation is mostly about three questions.

First, is your database on the list of explicit implementations, and if so, does the type provider class in the table match the namespace you expect. If it is not, the general package with manual configuration is the fallback, with a narrower feature set.

Second, what does your deployment environment allow. The dynamic assembly loading note applies to every vendor except SQL Server and Access, so if your runtime has a locked-down .NET configuration, that is the thing to verify early, and the fix is a configuration element applied to every host that loads the library.

Third, how the schema reaches the compiler in your build. If the build server can reach the database, the lazy schema exploration is enough. If it cannot, the offline schema and dacpac support is the path, and that is worth setting up before you need it rather than during a broken pipeline.

The thing to watch over time is the release cadence, and the repository does not give you that from the README because there are no GitHub releases to look at. The NuGet version badges on the vendor packages are the signal to watch, and the per-vendor split means a project on PostgreSQL is not affected by a change to the SQL Server package and vice versa, which is a real advantage of the packaging design.

What you get in exchange for the complexity is the removal of a whole category of bug. In a conventional data access layer, a schema change surfaces as a runtime failure in whatever code path happens to touch the changed column. Here it surfaces as a build failure, and the compiler names the code that needs attention. For an F# team that has decided to keep its data model in the type system, that is the whole argument.

Editorial conclusion

Adopt SQLProvider if you are working in F# and want database access where a schema change is a compile error rather than a runtime surprise, and if your database is on the list of explicit vendor implementations so you get the full feature set including lazy schema exploration and automatic constraint navigation. Do not adopt it if you are outside F#, and do not adopt it for a vendor outside the list without accepting the manual configuration path and its narrower features. Before you commit, read LICENSE.txt, because the repository licence field is not a standard identifier, check whether your runtime configuration permits the dynamic assembly loading that every vendor except SQL Server and Access needs, and decide early whether the build server will reach the database or whether you will use the offline schema and dacpac support to compile against a snapshot.

Frequently asked questions

What is SQLProvider in F#?

It is a general .NET SQL database type provider that compiles a database schema into .NET types at compile time, so queries against a live database return statically typed F# records. The README describes it as no OR-mapping, with F# compiling your database to .NET types.

Which databases does SQLProvider support?

Explicit implementations exist for SQL Server, SQLite, PostgreSQL, Oracle, MySQL and MariaDB, MsAccess, Firebird and DuckDB, plus an ODBC provider for any ODBC source with limited features. There is also a general SQLProvider package for manual configuration across any database.

Why does SQLProvider use dynamic assembly loading, and what is the known issue?

Vendors other than SQL Server and Access use dynamic assembly loading, which the README says may cause security problems depending on your system configuration and .NET version. The fix, when you hit problems, is to apply the referenced configuration element to the configuration files of fsi.exe, devenv.exe and your own program.

How do I build SQLProvider from source?

On Linux and macOS run build.sh, and on Windows run build.cmd, both of which use the dotnet command underneath. If you need to build against .NET Framework v4.x or Mono, rename the .paket_netfx file to .paket first, before restoring and building manually with paket and msbuild.

What does SQLProvider offer beyond typed LINQ queries?

Lazy schema exploration, automatic constraint navigation, individuals, transactional CRUD operations with identity support, two-way data binding, stored procedures, functions, Oracle packages, composable query integration, optional option types, mapping to record types, custom operators, asynchronous operations, and saving the schema offline or as SQL Server dacpac files.

Official sources

  1. fsprojects/SQLProvider on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fsprojects-sqlprovider.svg)](https://hysenlabs.com/projects/fsprojects-sqlprovider)

Was this useful?