Skip to main content

implementing-ef-core

Implementing Entity Framework Core repositories and migrations for PostgreSQL, MySQL, and SQLite at Bitwarden. Use when creating or modifying EF repositories, generating EF migrations, or working with non-MSSQL data access in the server repo. Also use when editing `EntityTypeConfiguration<T>` classes or debugging provider-specific LINQ translation issues.

来源信息

仓库
bitwarden/server
最近来源活动
2026年4月21日 13:42
检测到的 SKILL.md 语言
英语
星标
20,554
Forks
1,824

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
implementing-ef-core
description
Implementing Entity Framework Core repositories and migrations for PostgreSQL, MySQL, and SQLite at Bitwarden. Use when creating or modifying EF repositories, generating EF migrations, or working with non-MSSQL data access in the server repo. Also use when editing `EntityTypeConfiguration<T>` classes or debugging provider-specific LINQ translation issues.
## Repository Pattern EF implementations live in `src/Infrastructure/EntityFramework/Repositories/`. Each class implements the same interface as its Dapper counterpart. The EF repository uses `DbContext` and LINQ queries instead of stored procedures, but must produce identical behavior. ### Why behavior must match stored procedures exactly Bitwarden self-hosted runs on the customer's choice of database. If `CipherRepository.GetManyByUserId()` returns results in a different order on PostgreSQL than the stored procedure returns on MSSQL, or filters differently, or handles nulls differently — that's a bug. Users switching databases or comparing behavior across environments will see inconsistencies. The `[DatabaseData]` integration test attribute runs the same test against all configured databases. This is the primary safety net for parity. ### Cross-database considerations EF Core's LINQ-to-SQL translation varies by provider. Patterns that work on one database may fail on another: - **PostgreSQL** is stricter about types — operations like `Min()` on booleans or implicit string/int conversions that MySQL allows will throw - **SQLite** has limited ALTER TABLE support — some migrations that work elsewhere fail on SQLite - **Case sensitivity** depends on database collation, not on C# code — don't assume case-insensitive string comparison The pragmatic approach: write clean LINQ, run `[DatabaseData]` tests, and fix provider-specific failures as they surface rather than trying to predict every edge case. ## Migration Generation ### Workflow Run `pwsh ef_migrate.ps1 <MigrationName>` to generate migrations for all EF targets simultaneously. This creates migration files for each provider (PostgreSQL, MySQL, SQLite). ### Why the migration name matters The EF migration class name must exactly match the MSSQL migration name portion (from the `YYYY-MM-DD_##_MigrationName.sql` filename). This convention keeps migration history aligned across ORMs and makes it easy to trace which EF migration corresponds to which SQL script. ### Always review generated migrations EF's migration generator makes mechanical decisions that aren't always optimal: - It may drop and recreate indexes instead of renaming them - It may generate unnecessary column modifications when model annotations change - It doesn't know about Bitwarden's large table concerns (never add indexes to `Cipher`, `OrganizationUser` etc. without careful review) Review the generated `Up()` and `Down()` methods to ensure they align with the stored procedure migration's intent. ## Key Decisions That Trip Up AI Assistants ### Don't add navigation properties casually EF navigation properties (e.g., `public virtual Organization Organization { get; set; }`) affect query generation and lazy loading behavior. Only add them when the stored procedure equivalent also joins those tables. Unnecessary navigation properties cause N+1 queries that don't match the stored procedure's behavior. ### DbContext configuration lives in `EntityTypeConfiguration` classes Don't configure entities inline in `OnModelCreating`. Each entity has a configuration class that defines table mapping, relationships, and constraints. This keeps the DbContext clean and each entity's configuration self-contained. ### Respect the same GUID generation strategy Entity IDs are generated in application code via `CoreHelpers.GenerateComb()`, not by the database. Don't configure `ValueGeneratedOnAdd()` or database-generated defaults for ID columns in EF configuration. ## Critical Rules These are the most frequently violated conventions. Claude cannot fetch the linked docs at runtime, so these are inlined here: - **One `EntityTypeConfiguration<T>` class per entity** — never configure inline in `OnModelCreating` - **Migration name must match MSSQL migration name** from `YYYY-MM-DD_##_MigrationName.sql` - **Run `pwsh ef_migrate.ps1 <Name>`** to generate migrations for all providers simultaneously - **Review `Up()` and `Down()` methods** in every generated migration before committing - **No `ValueGeneratedOnAdd()` on ID columns** — IDs come from `CoreHelpers.GenerateComb()` in app code ## Examples ### GUID configuration ```csharp // CORRECT — ID generated in application code public void Configure(EntityTypeBuilder<Cipher> builder) { builder.HasKey(c => c.Id); // No ValueGeneratedOnAdd — CoreHelpers.GenerateComb() handles this } // WRONG — lets database generate IDs, breaks MSSQL parity public void Configure(EntityTypeBuilder<Cipher> builder) { builder.HasKey(c => c.Id); builder.Property(c => c.Id).ValueGeneratedOnAdd(); } ``` ### Navigation properties ```csharp // CORRECT — only add when the SP also joins this table public class Cipher { public Guid Id { get; set; } public Guid OrganizationId { get; set; } // No navigation property — the SP doesn't JOIN Organization } // WRONG — causes N+1 queries that don't match SP behavior public class Cipher { public Guid Id { get; set; } public Guid OrganizationId { get; set; } public virtual Organization Organization { get; set; } } ``` ## Further Reading - [Database migrations (EF)](https://contributing.bitwarden.com/contributing/database-migrations/ef) - [SQL code style](https://contributing.bitwarden.com/contributing/code-style/sql/)
在 GitHub 查看