

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1234567891011# 数据库与 EF Core 迁移规范1213## 📋 目录14151. [多数据库架构](#多数据库架构)162. [DbContext 设计模式](#dbcontext-设计模式)173. [迁移命令规范](#迁移命令规范)184. [实体 ID 配置](#实体-id-配置)195. [实体配置规范](#实体配置规范)2021---2223## 多数据库架构2425CodeSpirit 支持多数据库(SQL Server / MySQL),每个 API 服务需要定义:2627```28Data/29├── {Service}DbContext.cs # 基础 DbContext(运行时使用)30├── SqlServer{Service}DbContext.cs # SQL Server 专用 DbContext31├── SqlServer{Service}DbContextFactory.cs # SQL Server 设计时工厂32├── MySql{Service}DbContext.cs # MySQL 专用 DbContext33├── MySql{Service}DbContextFactory.cs # MySQL 设计时工厂34├── DatabaseSpecificConfigurations.cs # 数据库特定配置35├── Configurations/ # 实体配置36│ └── {Entity}Configuration.cs37└── Migrations/38 ├── SqlServer/ # SQL Server 迁移39 │ └── {timestamp}_{MigrationName}.cs40 └── MySql/ # MySQL 迁移41 └── {timestamp}_{MigrationName}.cs42```4344---4546## DbContext 设计模式4748### 基础 DbContext4950运行时使用的 DbContext,继承自 `MultiDatabaseDbContextBase`:5152```csharp53public class MallDbContext : MultiDatabaseDbContextBase54{55 public MallDbContext(56 DbContextOptions options,57 IServiceProvider serviceProvider,58 ICurrentUser currentUser,59 IHttpContextAccessor httpContextAccessor)60 : base(options, serviceProvider, currentUser, httpContextAccessor)61 {62 }6364 // DbSet 属性65 public DbSet<Product> Products => Set<Product>();66 public DbSet<Order> Orders => Set<Order>();6768 protected override void OnModelCreating(ModelBuilder modelBuilder)69 {70 base.OnModelCreating(modelBuilder);71 modelBuilder.ApplyConfigurationsFromAssembly(typeof(MallDbContext).Assembly);72 }73}74```7576### 数据库特定 DbContext7778**SQL Server 版本**:7980```csharp81/// <summary>82/// SQL Server 特定的数据库上下文(用于迁移)83/// </summary>84public class SqlServerMallDbContext : MallDbContext85{86 public SqlServerMallDbContext(87 DbContextOptions<SqlServerMallDbContext> options,88 IServiceProvider serviceProvider,89 ICurrentUser currentUser,90 IHttpContextAccessor httpContextAccessor)91 : base((DbContextOptions)options, serviceProvider, currentUser, httpContextAccessor)92 {93 }9495 protected override void ApplyDatabaseSpecificConfigurations(ModelBuilder modelBuilder)96 {97 DatabaseSpecificConfigurations.ApplySqlServerConfigurations(modelBuilder);98 }99}100```101102**MySQL 版本**:103104```csharp105/// <summary>106/// MySQL 特定的数据库上下文(用于迁移)107/// </summary>108public class MySqlMallDbContext : MallDbContext109{110 public MySqlMallDbContext(111 DbContextOptions<MySqlMallDbContext> options,112 IServiceProvider serviceProvider,113 ICurrentUser currentUser,114 IHttpContextAccessor httpContextAccessor)115 : base((DbContextOptions)options, serviceProvider, currentUser, httpContextAccessor)116 {117 }118119 protected override void ApplyDatabaseSpecificConfigurations(ModelBuilder modelBuilder)120 {121 DatabaseSpecificConfigurations.ApplyMySqlConfigurations(modelBuilder);122 }123}124```125126### 设计时工厂127128用于 `dotnet ef` 命令的设计时 DbContext 创建:129130**SQL Server**:131132```csharp133public class SqlServerMallDbContextFactory : IDesignTimeDbContextFactory<SqlServerMallDbContext>134{135 public SqlServerMallDbContext CreateDbContext(string[] args)136 {137 var optionsBuilder = new DbContextOptionsBuilder<SqlServerMallDbContext>();138139 optionsBuilder.UseSqlServer(140 "Server=localhost;Database=Mall;User Id=sa;Password=Password123!;TrustServerCertificate=True;",141 options => options.MigrationsHistoryTable("__EFMigrationsHistory", "mall")142 );143144 var services = new ServiceCollection();145 var serviceProvider = services.BuildServiceProvider();146 var currentUser = new DesignTimeCurrentUser();147 var httpContextAccessor = new HttpContextAccessor();148149 return new SqlServerMallDbContext(optionsBuilder.Options, serviceProvider, currentUser, httpContextAccessor);150 }151}152```153154**MySQL**:155156```csharp157public class MySqlMallDbContextFactory : IDesignTimeDbContextFactory<MySqlMallDbContext>158{159 public MySqlMallDbContext CreateDbContext(string[] args)160 {161 var optionsBuilder = new DbContextOptionsBuilder<MySqlMallDbContext>();162163 optionsBuilder.UseMySql(164 "Server=localhost;Database=Mall;User=root;Password=password;",165 new MySqlServerVersion(new Version(8, 0, 21)),166 options => options.MigrationsHistoryTable("__EFMigrationsHistory")167 );168169 var services = new ServiceCollection();170 var serviceProvider = services.BuildServiceProvider();171 var currentUser = new DesignTimeCurrentUser();172 var httpContextAccessor = new HttpContextAccessor();173174 return new MySqlMallDbContext(optionsBuilder.Options, serviceProvider, currentUser, httpContextAccessor);175 }176}177```178179---180181## 迁移命令规范182183### ⚠️ 重要:必须使用数据库特定的 DbContext184185**❌ 错误**:使用基础 DbContext186```bash187dotnet ef migrations add InitialCreate --context MallDbContext188```189190**✅ 正确**:使用数据库特定的 DbContext191192#### SQL Server 迁移193194```bash195# 创建迁移196dotnet ef migrations add InitialCreate --context SqlServerMallDbContext --output-dir Data/Migrations/SqlServer197198# 更新数据库199dotnet ef database update --context SqlServerMallDbContext200201# 删除最后一次迁移202dotnet ef migrations remove --context SqlServerMallDbContext203```204205#### MySQL 迁移206207```bash208# 创建迁移209dotnet ef migrations add InitialCreate --context MySqlMallDbContext --output-dir Data/Migrations/MySql210211# 更新数据库212dotnet ef database update --context MySqlMallDbContext213214# 删除最后一次迁移215dotnet ef migrations remove --context MySqlMallDbContext216```217218### 迁移命名规范219220| 场景 | 命名示例 |221|------|---------|222| 初始创建 | `InitialCreate` |223| 添加实体 | `Add{EntityName}` |224| 添加字段 | `Add{FieldName}To{EntityName}` |225| 修改字段 | `Update{FieldName}In{EntityName}` |226| 删除字段 | `Remove{FieldName}From{EntityName}` |227| 添加索引 | `AddIndexTo{EntityName}` |228229---230231## 实体 ID 配置232233### 雪花 ID 配置234235当实体使用应用层生成的雪花 ID(通过 `IIdGenerator`)时,**必须**在实体配置中添加 `ValueGeneratedNever()`:236237```csharp238public class ProductConfiguration : IEntityTypeConfiguration<Product>239{240 public void Configure(EntityTypeBuilder<Product> builder)241 {242 builder.ToTable("Products");243 builder.HasKey(x => x.Id);244245 // ✅ 必须:禁用数据库自动生成 ID246 builder.Property(x => x.Id).ValueGeneratedNever();247248 // 其他配置...249 }250}251```252253### 常见错误254255**❌ 缺少 `ValueGeneratedNever()` 导致的错误**:256257```258当 IDENTITY_INSERT 设置为 OFF 时,不能为表 'Products' 中的标识列插入显式值259```260261或 MySQL:262263```264Cannot insert explicit value for identity column in table 'Products' when IDENTITY_INSERT is set to OFF265```266267**✅ 解决方案**:2682691. 在实体配置中添加 `ValueGeneratedNever()`2702. 删除现有迁移2713. 使用正确的 DbContext 重新生成迁移272273### 需要配置 ValueGeneratedNever 的场景274275| 场景 | 需要配置 | 说明 |276|------|---------|------|277| 使用 `IIdGenerator` 生成 ID | ✅ 是 | 应用层生成雪花 ID |278| DemoDataService 中设置显式 ID | ✅ 是 | 测试数据生成 |279| 数据导入时保留原始 ID | ✅ 是 | 数据迁移 |280| 使用数据库自增 ID | ❌ 否 | 默认行为 |281282### 检查清单283284创建新实体时,确认以下事项:285286- [ ] 实体是否继承 `AuditableEntityBase<long>` 或类似基类?287- [ ] 是否在代码中使用 `_idGenerator.NewId()` 设置 ID?288- [ ] 实体配置中是否添加了 `ValueGeneratedNever()`?289290---291292## 实体配置规范293294### 配置文件位置295296```297Data/Configurations/{EntityName}Configuration.cs298```299300### 标准配置模板301302```csharp303using Microsoft.EntityFrameworkCore;304using Microsoft.EntityFrameworkCore.Metadata.Builders;305306namespace CodeSpirit.{Service}Api.Data.Configurations;307308/// <summary>309/// {EntityName} 实体配置310/// </summary>311public class {EntityName}Configuration : IEntityTypeConfiguration<{EntityName}>312{313 public void Configure(EntityTypeBuilder<{EntityName}> builder)314 {315 // 表名316 builder.ToTable("{TableName}");317318 // 主键319 builder.HasKey(x => x.Id);320 builder.Property(x => x.Id).ValueGeneratedNever(); // 雪花 ID321322 // 租户 ID(多租户实体必须)323 builder.Property(x => x.TenantId).IsRequired().HasMaxLength(50);324325 // 必填字符串字段326 builder.Property(x => x.Name).IsRequired().HasMaxLength(100);327328 // 可选字符串字段329 builder.Property(x => x.Description).HasMaxLength(500);330331 // 金额字段332 builder.Property(x => x.Amount).HasColumnType("decimal(18,2)");333334 // 长文本字段335 builder.Property(x => x.Content).HasColumnType("text");336337 // 带默认值的字段338 builder.Property(x => x.IsEnabled).HasDefaultValue(true);339 builder.Property(x => x.SortOrder).HasDefaultValue(0);340341 // 索引342 builder.HasIndex(x => new { x.TenantId, x.Code })343 .IsUnique()344 .HasDatabaseName("IX_{TableName}_TenantId_Code");345346 // 关系配置347 builder.HasOne(x => x.Parent)348 .WithMany(x => x.Children)349 .HasForeignKey(x => x.ParentId)350 .OnDelete(DeleteBehavior.Restrict);351 }352}353```354355---356357## 注意事项3583591. **始终使用数据库特定的 DbContext 进行迁移操作**3602. **SQL Server 和 MySQL 的迁移必须分别生成**3613. **使用雪花 ID 的实体必须配置 `ValueGeneratedNever()`**3624. **迁移文件按数据库类型分目录存放**3635. **设计时工厂中的连接字符串仅用于迁移生成,不用于运行时**364
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| xin-lai/CodeSpirit.cursor/rules/api-design.mdc · 56 | Cursor rules | no sections | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/controller.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/dependency-injection.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/dto.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/js.mdc · 56 | Cursor rules | apidocs | 46/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/security.mdc · 56 | Cursor rules | database | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/service.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpiritAGENTS.md · 56 | AGENTS.md | styleagent-behaviourdocs | 66/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/ai-development.mdc · 56 | Cursor rules | api | 46/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/amis-cards.mdc · 56 | Cursor rules | no sections | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/csproj.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/naming-conventions.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/package-management.mdc · 56 | Cursor rules | no sections | 74/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/project-structure.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/testing.mdc · 56 | Cursor rules | testing-strategy | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/cs.mdc · 56 | Cursor rules | no sections | 25/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/all.mdc · 56 | Cursor rules | testing-strategyapi | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/css.mdc · 56 | Cursor rules | ui | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/enum.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/i18n.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 46 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 46 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/xin-lai-codespirit-cursor-rules-database)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.