← Back to Blog

Connection Strings in .NET 8: Every Provider, Every Gotcha

Every database provider has its own connection string dialect, its own security defaults, and its own way of silently failing when you get one flag wrong. Here's what actually differs between them — and where the password should never live.

Pankaj Kumar
Senior Software Engineer — .NET, Blazor, ASP.NET Core
4+ years building production .NET and Blazor applications. Every DevToolsHub tool and article comes from real daily development work — not documentation summaries.
Published 11 Aug 2026· Last reviewed Aug 2026· 8 min read · About the author →
The staging database rejected the exact connection string that worked in production, character for character except one thing: staging still used TrustServerCertificate=True from a self-signed cert nobody had replaced.
Key takeaways
  • Every provider's connection string is a different key=value dialect — SQL Server's User Id/Password, PostgreSQL's Username/Host, MySQL's Uid/Pwd, and Cosmos DB's single AccountKey are not interchangeable
  • Azure SQL requires Encrypt=True and rejects TrustServerCertificate=True for its own hostnames — on-prem SQL Server accepts both unencrypted connections and self-signed certificate bypasses that must never reach production
  • Check your installed Microsoft.Data.SqlClient version before assuming Encrypt's historical false default still applies — recent versions changed it
  • A connection string with a literal password is one accidental git commit away from a public credential leak — user secrets locally, Key Vault or App Service settings in production
  • Azure SQL supports Active Directory / Managed Identity authentication, removing the password from the connection string entirely for apps hosted in Azure
Table of Contents

One concept, five dialects

A connection string is conceptually the same thing everywhere: enough information for a driver to open a socket, authenticate, and select a database. In practice, every ADO.NET provider invented its own key names for that same information, and mixing them up doesn't throw a helpful compile-time error — it throws a runtime connection failure, usually with a message that doesn't mention which key was wrong.

The provider comparison

// SQL Server (Microsoft.Data.SqlClient)
Server=localhost;Database=AppDb;User Id=appuser;Password=***;Encrypt=True;TrustServerCertificate=False;

// Azure SQL — tcp: prefix, explicit port 1433, encryption mandatory
Server=tcp:myserver.database.windows.net,1433;Initial Catalog=AppDb;User ID=appuser;Password=***;Encrypt=True;TrustServerCertificate=False;

// PostgreSQL (Npgsql)
Host=localhost;Port=5432;Database=AppDb;Username=appuser;Password=***;SSL Mode=Require;

// MySQL (MySqlConnector / MySql.Data)
Server=localhost;Port=3306;Database=AppDb;Uid=appuser;Pwd=***;SslMode=Required;

// Cosmos DB
AccountEndpoint=https://my-account.documents.azure.com:443/;AccountKey=***;

Four different names for "the username" (User Id, User ID, Username, Uid), three different names for "the password" (Password, Pwd, AccountKey), and Azure SQL's requirement for a tcp: prefix plus explicit port that on-prem SQL Server doesn't need at all. None of this is discoverable from the driver's compile-time API — the connection string is just a string until the provider parses it at runtime, so a typo'd key name is usually silently ignored rather than rejected, and you're left debugging why a setting you definitely configured "isn't taking effect."

Encrypt and TrustServerCertificate: the flags that actually matter

Encrypt=True wraps the connection in TLS, so credentials and query results can't be read by anything positioned between your app and the database. Azure SQL has required an encrypted connection since 2022 and rejects TrustServerCertificate=True for its own hostnames outright. On-premises SQL Server has no such requirement baked in — it will happily hand you an unencrypted connection if you don't ask for Encrypt=True explicitly, which is exactly the gap that makes a dev-to-prod parity check worth doing before every deployment.

TrustServerCertificate=True skips validating the server's TLS certificate against a trusted root. It exists for one legitimate reason — connecting to a local SQL Server instance using a self-signed development certificate — and it defeats the entire purpose of Encrypt=True if it ships to production, because it means your app will happily encrypt a connection to anything presenting any certificate at all, including a machine-in-the-middle. If TrustServerCertificate=True shows up in a production connection string, that's not a style preference, it's a real gap.

The default that changed under everyone's feet

Historically, Encrypt defaulted to false in both the legacy System.Data.SqlClient and early Microsoft.Data.SqlClient — meaning a connection string with no Encrypt key at all was unencrypted by default.

More recent versions of Microsoft.Data.SqlClient changed this default to encourage encryption out of the box, which is a good direction — but it also means the exact same connection string, with Encrypt simply omitted, can behave differently depending on which package version your project references. If a connection behaves differently after a NuGet update, checking the installed Microsoft.Data.SqlClient version against its changelog is worth doing before assuming your connection string itself is the problem.

Connection pooling and MARS: the silent defaults

Every SQL Server connection string is also implicitly a pooling configuration, whether you write any pooling keys or not. Max Pool Size defaults to 100 — under normal load that's plenty, but a service with a connection leak (a SqlConnection not disposed, or an await using missing on a hot path) will exhaust that pool under load and start throwing InvalidOperationException: Timeout expired errors that have nothing to do with the database itself being slow. Raising Max Pool Size masks the symptom; it doesn't fix a leak.

MultipleActiveResultSets=True (MARS) allows more than one pending command on a single connection at once — useful if your code interleaves reads without fully consuming one SqlDataReader before opening another, which is common with certain ORMs' internal query batching. It isn't free: MARS connections have measurably more overhead per command than a plain connection, and enabling it as a blanket default to work around one specific query pattern often costs more in aggregate than fixing the one query that actually needed it. Most application code that goes through EF Core exclusively doesn't need MARS at all, since EF Core fully consumes each command's results before issuing the next one on that connection.

Connection Timeout is a third silent default worth checking explicitly rather than assuming: it governs how long the driver waits to establish a new connection (15 seconds by default for SQL Server), which is a completely different setting from a command's own execution timeout. A slow, congested network path to a database — common right after a failover, or when connecting across regions — can exhaust the connection timeout well before any query has even started running, producing an error that looks like a query problem but is actually a connection-establishment problem with a different fix.

Where the password should never live

A connection string with a literal password sitting in appsettings.json is one accidental git add . away from a public GitHub credential leak — this remains one of the most common ways real production database credentials end up scraped by bots within minutes of a push. For local development, dotnet user-secrets keeps secrets in a JSON file outside the project directory entirely, so they never get committed by accident:

dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Default" "Server=localhost;Database=AppDb;User Id=appuser;Password=dev-only-value;Encrypt=True;"

For production, the connection string — and especially the password inside it — belongs in Azure Key Vault (referenced via Azure.Extensions.AspNetCore.Configuration.Secrets and builder.Configuration.AddAzureKeyVault(...)) or your hosting platform's own secret store, such as Azure App Service Application Settings, injected as environment variables at runtime. Either way, the goal is the same: the literal password value should never exist as text inside a file that source control tracks. Generating a strong value for that secret in the first place is worth doing properly too — the Password Generator produces a cryptographically random value rather than a memorable one, which is exactly what a database credential should be.

Managed Identity: the connection string with no password

Azure SQL supports Azure AD / Managed Identity authentication as a connection string mode, which removes the password entirely: Authentication=Active Directory Default (or the more specific Managed Identity variant) tells the driver to fetch a token from Azure's identity infrastructure at connection time instead of sending a username and password at all. For an app already running inside Azure — App Service, Container Apps, AKS with workload identity — this is the strongest option available, since there's no password to rotate, leak, or accidentally commit, because there isn't one.

Validate before you deploy

The Connection String Builder & Validator builds a correctly-shaped string for any of the five dialects above from form fields, so you don't have to remember which provider uses Uid and which uses Username. Its Parse mode does the reverse — paste an existing connection string and it flags a missing Encrypt=True, an enabled TrustServerCertificate, or a plaintext credential before that string gets pasted somewhere it shouldn't go. Comparing the resulting values across environment-specific config files is the same instinct behind catching config drift between environments before it reaches production — worth doing with the Appsettings Diff tool alongside it. It also pairs naturally with scaffolding your EF Core entities once the connection string itself is confirmed correct — scaffolding is the next command you'll run once you know the connection string it depends on is right.

Try the free tool

Connection String Builder & Validator

Build a connection string for SQL Server, Azure SQL, PostgreSQL, MySQL, or Cosmos DB from form fields, or paste an existing one to parse it and flag missing encryption or plaintext credentials.

Open Connection String Builder & Validator