{"id":17182343,"url":"https://github.com/simoncropp/localdb","last_synced_at":"2026-01-27T04:11:15.339Z","repository":{"id":34887470,"uuid":"185792342","full_name":"SimonCropp/LocalDb","owner":"SimonCropp","description":"Provides a wrapper around SqlLocalDB to simplify running tests or samples that require a SQL Server Database","archived":false,"fork":false,"pushed_at":"2025-05-14T10:34:17.000Z","size":2772,"stargazers_count":191,"open_issues_count":1,"forks_count":10,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-14T11:47:14.103Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SimonCropp.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"license.txt","code_of_conduct":"code_of_conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"SimonCropp"}},"created_at":"2019-05-09T12:13:16.000Z","updated_at":"2025-05-14T10:34:21.000Z","dependencies_parsed_at":"2023-12-18T07:27:55.030Z","dependency_job_id":"a772c429-4ba1-4569-8ade-7d17260af035","html_url":"https://github.com/SimonCropp/LocalDb","commit_stats":{"total_commits":1824,"total_committers":7,"mean_commits":"260.57142857142856","dds":0.3042763157894737,"last_synced_commit":"adb2311e939c1e807b2eb92bec1f0fb3754f9fff"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FLocalDb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FLocalDb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FLocalDb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FLocalDb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SimonCropp","download_url":"https://codeload.github.com/SimonCropp/LocalDb/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254319717,"owners_count":22051072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-15T00:36:49.542Z","updated_at":"2026-01-27T04:11:15.333Z","avatar_url":"https://github.com/SimonCropp.png","language":"C#","readme":"\u003c!--\nGENERATED FILE - DO NOT EDIT\nThis file was generated by [MarkdownSnippets](https://github.com/SimonCropp/MarkdownSnippets).\nSource File: /readme.source.md\nTo change this file edit the source file and then run MarkdownSnippets.\n--\u003e\n\n# \u003cimg src=\"/src/icon.png\" height=\"30px\"\u003e LocalDb\n\n[![Build status](https://img.shields.io/appveyor/build/SimonCropp/LocalDb)](https://ci.appveyor.com/project/SimonCropp/LocalDb)\n[![NuGet Status](https://img.shields.io/nuget/v/LocalDb.svg?label=nuget:LocalDb)](https://www.nuget.org/packages/LocalDb/)\n[![NuGet Status](https://img.shields.io/nuget/v/EfLocalDb.svg?label=nuget:EfLocalDb)](https://www.nuget.org/packages/EfLocalDb/)\n[![NuGet Status](https://img.shields.io/nuget/v/EfClassicLocalDb.svg?label=nuget:EfClassicLocalDb)](https://www.nuget.org/packages/EfClassicLocalDb/)\n[![NuGet Status](https://img.shields.io/nuget/v/EfLocalDb.NUnit.svg?label=nuget:EfLocalDb.NUnit)](https://www.nuget.org/packages/EfLocalDb.NUnit/)\n\nProvides a wrapper around [SqlLocalDB](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-express-localdb) to simplify running tests against [Entity Framework](https://docs.microsoft.com/en-us/ef/core/) or a raw SQL Database.\u003c!-- singleLineInclude: intro. path: /docs/mdsource/intro.include.md --\u003e\n\n**See [Milestones](../../milestones?state=closed) for release notes.**\n\n\n**SqlLocalDB is only supported on Windows**\n\n\n## Sponsors\n\n### Entity Framework Extensions\u003c!-- include: zzz. path: /docs/mdsource/zzz.include.md --\u003e\n\n[Entity Framework Extensions](https://entityframework-extensions.net/?utm_source=simoncropp\u0026utm_medium=LocalDb) is a major sponsor and is proud to contribute to the development this project.\n\n[![Entity Framework Extensions](https://raw.githubusercontent.com/SimonCropp/LocalDb/refs/heads/main/docs/zzz.png)](https://entityframework-extensions.net/?utm_source=simoncropp\u0026utm_medium=LocalDb)\u003c!-- endInclude --\u003e\n\n\n\u003c!-- toc --\u003e\n## Contents\n\n  * [Why](#why)\n    * [Goals](#goals)\n    * [Why not SQLite](#why-not-sqlite)\n    * [Why not SQL Express or full SQL Server](#why-not-sql-express-or-full-sql-server)\n    * [Why not EntityFramework InMemory](#why-not-entityframework-inmemory)\n  * [References](#references)\n  * [Usage](#usage)\n    * [Raw SqlConnection](#raw-sqlconnection)\n    * [EntityFramework Classic](#entityframework-classic)\n    * [EntityFramework Core](#entityframework-core)\n  * [LocalDB](#localdb)\n  * [How this project works](#how-this-project-works)\n    * [Inputs](#inputs)\n    * [SqlInstance Startup Flow](#sqlinstance-startup-flow)\n    * [Create SqlDatabase Flow](#create-sqldatabase-flow)\n  * [Performance](#performance)\n    * [Hardware](#hardware)\n    * [Scenarios](#scenarios)\n    * [Results](#results)\n    * [Key Insights](#key-insights)\n  * [Debugging](#debugging)\n  * [SqlLocalDb](#sqllocaldb)\n  * [ReSharper Test Runner](#resharper-test-runner)\n  * [Credits](#credits)\u003c!-- endToc --\u003e\n  * [Design](/pages/design.md)\n  * [Raw Connection Usage](/pages/raw-usage.md)\n  * [EntityFramework Classic Usage](/pages/ef-classic-usage.md)\n  * [EntityFramework Core Usage](/pages/ef-usage.md)\n  * [EntityFramework Core Migrations](/pages/efmigrations.md)\n  * [Directory and name resolution](/pages/directory-and-name-resolution.md)\n  * [Sql Management Studio](/pages/sql-management-studio.md)\n  * [Logging](/pages/logging.md)\n  * [Template database size](/pages/template-database-size.md)\n  * [Template Re-generation](/pages/template-regen.md)\n  * [DB auto offline](/pages/db-auto-offline.md)\n  * [Shutdown Timeout](/pages/shutdown-timeout.md)\n\n\n## NuGet packages\n\n  * https://www.nuget.org/packages/LocalDb/\n  * https://www.nuget.org/packages/EfLocalDb/\n  * https://www.nuget.org/packages/EfClassicLocalDb/\n\n\n## Why\n\n\n### Goals\n\n * Have a isolated SQL Server Database for each unit test method.\n * Does not overly impact performance.\n * Results in a running SQL Server Database that can be accessed via [SQL Server Management Studio ](https://docs.microsoft.com/en-us/sql/ssms/sql-server-management-studio-ssms?view=sql-server-2017) (or other tooling) to diagnose issues when a test fails.\n\n\n### Why not SQLite\n\n * SQLite and SQL Server do not have compatible feature sets and there are [incompatibilities between their query languages](https://www.mssqltips.com/sqlservertip/4777/comparing-some-differences-of-sql-server-to-sqlite/).\n\n\n### Why not SQL Express or full SQL Server\n\n * Control over file location. SqlLocalDB connections support AttachDbFileName property, which allows developers to specify a database file location. SqlLocalDB will attach the specified database file and the connection will be made to it. This allows database files to be stored in a temporary location, and cleaned up, as required by tests.\n * No installed service is required. Processes are started and stopped automatically when needed.\n * Automatic cleanup. A few minutes after the last connection to this process is closed the process shuts down.\n * Full control of instances using the [Command-Line Management Tool: SqlLocalDB.exe](https://docs.microsoft.com/en-us/sql/relational-databases/express-localdb-instance-apis/command-line-management-tool-sqllocaldb-exe?view=sql-server-2017).\n\n\n### Why not [EntityFramework InMemory](https://docs.microsoft.com/en-us/ef/core/providers/in-memory/)\n\n * Difficult to debug the state. When debugging a test, or looking at the resultant state, it is helpful to be able to interrogate the Database using tooling\n * InMemory is implemented with shared mutable state between instance. This results in strange behaviors when running tests in parallel, for example when [creating keys](https://github.com/aspnet/EntityFrameworkCore/issues/6872).\n * InMemory is not intended to be an alternative to SqlServer, and as such it does not support the full suite of SqlServer features. For example:\n    * Does not support [Timestamp/row version](https://docs.microsoft.com/en-us/ef/core/modeling/concurrency#timestamprow-version).\n    * [Does not validate constraints](https://github.com/aspnet/EntityFrameworkCore/issues/2166).\n\nSee the official guidance: [InMemory is not a relational database](https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory#inmemory-is-not-a-relational-database).\n\n\n## References\n\n * [Which Edition of SQL Server is Best for Development Work?](https://www.red-gate.com/simple-talk/sql/sql-development/edition-sql-server-best-development-work/#8)\n * [Introducing SqlLocalDB, an improved SQL Express](https://blogs.msdn.microsoft.com/sqlexpress/2011/07/12/introducing-localdb-an-improved-sql-express/)\n * [SQL LocalDB 2022 Download](https://download.microsoft.com/download/3/8/d/38de7036-2433-4207-8eae-06e247e17b25/SqlLocalDB.msi)\n * [SQL Server 2022 Cumulative Update](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2022/build-versions) required to update LocalDb to the latest version.\n * [SQL Server 2022 Express via Chocolatey](https://community.chocolatey.org/packages/sql-server-express): `choco install sql-server-express`\n\n\n## Usage\n\nThis project supports several approaches.\n\n\n### Raw SqlConnection\n\nInteractions with SqlLocalDB via a [SqlConnection](https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection).\n\n[Full Usage](/pages/raw-usage.md)\n\n\n### EntityFramework Classic\n\nInteractions with SqlLocalDB via [Entity Framework Classic](https://docs.microsoft.com/en-us/ef/ef6/).\n\n[Full Usage](/pages/ef--classic-usage.md)\n\n\n### EntityFramework Core\n\nInteractions with SqlLocalDB via [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/).\n\n[Full Usage](/pages/ef-usage.md)\n\n\n## LocalDB\n\nHow MS SqlServer LocalDB is structured\n\n```mermaid\ngraph TB\n    subgraph PM[\"Physical Machine (Windows)\"]\n        subgraph SQLLocalDB[\"SqlServer LocalDB\"]\n            subgraph Instance1[\"LocalDB Instance: MSSQLLocalDB\"]\n                DB1A[(DB: MyApp)]\n                DB1B[(DB: TestDB)]\n                DB1C[(DB: master)]\n            end\n            \n            subgraph Instance2[\"LocalDB Instance: ProjectsV15\"]\n                DB2A[(DB: WebApi)]\n                DB2B[(DB: master)]\n            end\n            \n            subgraph Instance3[\"LocalDB Instance: MyCustomInstance\"]\n                DB3A[(DB: Analytics)]\n                DB3B[(DB: Staging)]\n                DB3C[(DB: master)]\n            end\n        end\n        \n        UserData[\"Default User Data Folder\u003cbr/\u003e%LOCALAPPDATA%\\Microsoft\\Microsoft SQL Server Local DB\\Instances\\\"]\n    end\n    \n    Instance1 --\u003e |\"Stores mdf/ldf files in\"| UserData\n    Instance2 --\u003e |\"Stores mdf/ldf files in\"| UserData\n    Instance3 --\u003e |\"Stores mdf/ldf files in\"| UserData\n```\n\nKey relationships:\n\n * Physical Machine → One Windows machine can have one LocalDB engine installed\n * LocalDB Engine → Can host multiple isolated instances (each is like a mini SQL Server)\n * Instance → Each contains multiple databases (always includes system DBs like master)\n * Storage → Each instance stores its .mdf and .ldf files in a subfolder under `%LOCALAPPDATA%\\Microsoft\\Microsoft SQL Server Local DB\\Instances\\`\n\n\n## How this project works\n\n### Inputs\n\n#### buildTemplate\n\nA delegate that builds the template database schema. Called zero or once based on the current state of the underlying LocalDB:\n\n * **Not called** if a valid template already exists (timestamp matches)\n * **Called once** if the template needs to be created or rebuilt\n\nThe delegate receives a connected DbContext (EF) or SqlConnection (raw) to create schema and seed initial data.\n\n\n#### timestamp\n\nA timestamp used to determine if the template database needs to be rebuilt:\n\n * If the timestamp is **newer** than the existing template, the template is recreated\n * Defaults to the last modified time of `buildTemplate` delegate's assembly, or the `TDbContext` assembly if `buildTemplate` is null\n\n\n#### callback\n\nA delegate executed after the template database has been created or mounted:\n\n * **Guaranteed to be called exactly once** per `SqlInstance` at startup\n * Receives a SqlConnection and DbContext (EF) for seeding reference data or post-creation setup\n * Called regardless of whether `buildTemplate` ran (useful for setup that must always occur)\n\n\n### SqlInstance Startup Flow\n\nThis flow happens once per `SqlInstance`, usually once before any tests run.\n\n```mermaid\nflowchart TD\n\n    start[Start]\n    checkExists{Instance\u003cbr\u003eExists?}\n    checkRunning{Instance\u003cbr\u003eRunning?}\n    startInstance[Start Instance]\n    checkDataFile{Data File\u003cbr\u003eExists?}\n    stopAndDelete[Stop \u0026 Delete\u003cbr\u003eInstance]\n    cleanDir[Clean Directory]\n    checkTimestamp{Timestamp\u003cbr\u003eMatch?}\n    checkCallback{Callback\u003cbr\u003eExists?}\n    createInstance[Create Instance]\n\n    subgraph openMasterForNewBox[Open Master Connection]\n        optimizeModel[Optimize Model DB]\n        deleteFiles[Delete Template Files]\n        createTemplateDb[Create Template DB]\n        subgraph openTemplateForNewBox[Open Template Connection]\n            runBuildTemplate[Run buildTemplate]\n            checkCallbackAfterBuild{Callback\u003cbr\u003eExists?}\n            runCallbackAfterBuild[Run Callback]\n        end\n        detachShrink[Shrink \u0026 Detach Template]\n    end\n    setTimestamp[Set Creation Timestamp]\n\n    subgraph openMasterForExistingBox[Open Master Connection]\n        attachTemplate[Attach Template DB]\n        subgraph openTemplateForExistingBox[Open Template Connection]\n            runCallback[Run Callback]\n        end\n        detachTemplate[Detach Template DB]\n    end\n\n\n    done[Done]\n\n    start --\u003e checkExists\n\n    checkExists --\u003e|No| cleanDir\n    checkExists --\u003e|Yes| checkRunning\n\n    checkRunning --\u003e|No| startInstance\n    startInstance --\u003e checkDataFile\n\n    checkRunning --\u003e|Yes| checkDataFile\n\n    checkDataFile --\u003e|No| stopAndDelete\n    stopAndDelete --\u003e cleanDir\n\n    checkDataFile --\u003e|Yes| checkTimestamp\n\n    checkTimestamp --\u003e|No| deleteFiles\n\n    checkTimestamp --\u003e|Yes| checkCallback\n    cleanDir --\u003e createInstance\n    createInstance --\u003e optimizeModel\n    optimizeModel --\u003e deleteFiles\n    deleteFiles --\u003e createTemplateDb\n    createTemplateDb --\u003e runBuildTemplate\n    runBuildTemplate --\u003e checkCallbackAfterBuild\n    checkCallbackAfterBuild --\u003e|Yes| runCallbackAfterBuild\n    checkCallbackAfterBuild --\u003e|No| detachShrink\n    runCallbackAfterBuild --\u003e detachShrink\n    detachShrink --\u003e setTimestamp\n\n    checkCallback --\u003e|Yes| attachTemplate\n    attachTemplate --\u003e runCallback\n    runCallback --\u003e detachTemplate\n\n    checkCallback --\u003e|No| done\n    detachTemplate --\u003e done\n    setTimestamp --\u003e done\n```\n\n\n### Create SqlDatabase Flow\n\nThis happens once per `SqlInstance.Build`, usually once per test method.\n\n```mermaid\nflowchart TD\n    entry[Start]\n\n    subgraph openMaster[Open Master Connection]\n        checkDbExists{DB Exists?}\n        takeOffline[Take DB Offline]\n        copyFilesExisting[Copy Data \u0026 Log Files]\n        setOnline[Set DB Online]\n        copyFilesNew[Copy Data \u0026 Log Files]\n        attachDb[Attach DB]\n    end\n    openNewConn[Open New Connection]\n    returnConn[Return Connection]\n\n    entry\n    entry --\u003e checkDbExists\n\n    checkDbExists --\u003e|Yes| takeOffline\n    takeOffline --\u003e copyFilesExisting\n    copyFilesExisting --\u003e setOnline\n    setOnline --\u003e openNewConn\n\n    checkDbExists --\u003e|No| copyFilesNew\n    copyFilesNew --\u003e attachDb\n    attachDb --\u003e openNewConn\n\n    openNewConn --\u003e returnConn\n```\n\n\n## Performance\n\nBenchmarks measuring SqlInstance startup performance under different LocalDB states.\nResults collected using [BenchmarkDotNet](https://benchmarkdotnet.org/).\n\n\n### Hardware\n\n * BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7623)\n * AMD Ryzen 9 5900X 3.70GHz, 1 CPU, 24 logical and 12 physical cores\n * .NET SDK 10.0.102\n\n\n### Scenarios\n\n| Scenario    | Description | When It Occurs |\n|-------------|-------------|----------------|\n| **Cold**    | LocalDB instance does not exist. Full startup from scratch. | First run on a machine, or after `sqllocaldb delete`. Rare in practice. |\n| **Stopped** | LocalDB instance exists but is stopped. Instance is started and existing template files are reused. | After LocalDB auto-shutdown (default: 5 min idle) or system restart. Performance similar to Warm/Rebuild. [More info](/pages/shutdown-timeout.md) |\n| **Rebuild** | LocalDB running, but template timestamp changed. | After code changes that modify the `buildTemplate` delegate's assembly. Common during development. [More info](/pages/template-regen.md) |\n| **Warm**    | LocalDB running with valid template. | Typical test runs when instance is already warm. **Most common scenario.** |\n\n\n### Results\n\nAll times in milliseconds.\n\n| DBs | Cold total | Cold per DB | Stopped total | Stopped per DB | Rebuild total | Rebuild per DB | Warm total | Warm per DB |\n|----:|-----:|--------:|--------:|-----------:|--------:|-----------:|-----:|--------:|\n| 0 | 6396 | - | 472 | - | 85 | - | 3 | - |\n| 1 | 6395 | 6395 | 514 | 514 | 120 | 120 | 40 | 40 |\n| 5 | 6542 | 1308 | 641 | 128 | 267 | 53 | 173 | 35 |\n| 10 | 6705 | 671 | 834 | 83 | 421 | 42 | 402 | 40 |\n| 100 | 10284 | 103 | 3900 | 39 | 3436 | 34 | 3328 | 33 |\n\n\n### Key Insights\n\n * **Stopped ≈ Warm/Rebuild**: When a stopped instance is detected, the library starts it and reuses the existing template files. This provides ~500ms performance instead of Cold (~6.4s). [More info](/pages/shutdown-timeout.md)\n * **Warm is 2000x faster than Cold**: With 0 databases, warm start takes ~3ms vs ~6.4s for cold start. This is the primary optimization the library provides.\n * **Rebuild is 75x faster than Cold**: When only the template needs rebuilding (code changed), startup is ~85ms vs ~6.4s.\n * **Marginal cost per database converges to ~35ms**: Regardless of startup scenario, each additional database adds approximately 35ms once the instance is running.\n * **At scale, database creation dominates**: With 100 databases, Warm/Rebuild/Stopped scenarios converge to similar total times (~3.3-3.9s) because database creation time dominates. Cold remains slower (~10s).\n * **Tests re-run after system restart** will now benefit from stopped instance reconstitution, avoiding cold start times. [More info](/pages/shutdown-timeout.md)\n * **Minimize databases per test** when possible, as each database adds ~35ms overhead\n\n\n## Debugging\n\nTo connect to a SqlLocalDB instance using [SQL Server Management Studio ](https://docs.microsoft.com/en-us/sql/ssms/sql-server-management-studio-ssms?view=sql-server-2017) use a server name with the following convention `(LocalDb)\\INSTANCENAME`.\n\nSo for a instance named `MyDb` the server name would be `(LocalDb)\\MyDb`. Note that the name will be different if a `name` or `instanceSuffix` have been defined for SqlInstance.\n\nThe server name will be written to [Trace.WriteLine](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.trace.writeline) when a SqlInstance is constructed. It can be accessed programmatically from `SqlInstance.ServerName`. See [Logging](/pages/logging.md).\n\n\n## SqlLocalDb\n\nThe [SqlLocalDb Utility (SqlLocalDB.exe)](https://docs.microsoft.com/en-us/sql/tools/sqllocaldb-utility) is a command line tool to enable users and developers to create and manage an instance of SqlLocalDB.\n\nUseful commands:\n\n * `sqllocaldb info`: list all instances\n * `sqllocaldb create InstanceName`: create a new instance\n * `sqllocaldb start InstanceName`: start an instance\n * `sqllocaldb stop InstanceName`: stop an instance\n * `sqllocaldb delete InstanceName`: delete an instance (this does not delete the file system data for the instance)\n\n\n## ReSharper Test Runner\n\nThe ReSharper Test Runner has a feature that detects spawned processes, and prompts if they do not shut down when a test ends. This is problematic when using SqlLocalDB since the Sql Server process continues to run:\n\n![](pages/resharper-spawned.png)\n\nTo avoid this error spawned processes can be ignored:\n\n![](pages/resharper-ignore-spawned.png)\n\n\n## Credits\n\nSqlLocalDB API code sourced from https://github.com/skyguy94/Simple.LocalDb\n\n\n## Icon\n\n[Robot](https://thenounproject.com/term/robot/960055/) designed by [Creaticca Creative Agency](https://thenounproject.com/creaticca/) from [The Noun Project](https://thenounproject.com/).\n","funding_links":["https://github.com/sponsors/SimonCropp"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Flocaldb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimoncropp%2Flocaldb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Flocaldb/lists"}