microsoft/migrating-ef6-code-first-to-ef-core
> Migrates Entity Framework 6 Code-First projects to EF Core. Use when upgrading projects that already use DbContext with Code-First models (no EDMX files) and need to move to EF Core. Triggers for "migrate EF6 to EF Core", "upgrade Entity Framework to EF Core", "convert EF6 Code-First to EF Core", "replace EntityFramework package with EF Core", or "modernize Entity Framework". Also relevant when encountering EF6-specific APIs like EntityTypeConfiguration, DbModelBuilder, Database.SetInitializer, or HasDatabaseGeneratedOption during .NET modernization.
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill migrating-ef6-code-first-to-ef-core
Migrates Entity Framework 6 Code-First projects to EF Core. This skill covers projects that already use DbContext with fluent or data-annotation-based models — no EDMX files involved.
> Scope: This skill targets EF6 Code-First projects (no .edmx files). For EDMX-based projects (Database-First/Model-First), use the migrating-edmx-to-code-first skill instead. For DbContext registration and DI setup during ASP.NET Core migration, also apply the migrating-ef-dbcontext skill — it is complementary to this one.
Migration Progress:
- [ ] Step 1: Assess EF6 usage
- [ ] Step 2: Swap NuGet packages
- [ ] Step 3: Update namespaces
- [ ] Step 4: Migrate DbContext and configuration
- [ ] Step 5: Migrate entity configurations
- [ ] Step 6: Handle breaking API changes
- [ ] Step 7: Migrate migrations history
- [ ] Step 8: Validate
DbContext subclass, no .edmx files)DbContext subclasses and their DbSet<T> propertiesEntityTypeConfiguration<T> classes and OnModelCreating overridesDatabase.SetInitializer, DbModelBuilder, HasDatabaseGeneratedOption, Map(), MapToStoredProcedures()IConvention, Convention)Database.SqlQuery<T>(), Database.ExecuteSqlCommand()Configuration class generated by Enable-Migrations (typically in Migrations/Configuration.cs) — this will be removedRemove the EF6 package and add EF Core packages matching the target framework.
<!-- Remove -->
<PackageReference Include="EntityFramework" />
<!-- Add (choose the appropriate database provider) -->
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" />
Use the EF Core major version that matches the project's target framework (e.g., EF Core 8.x for .NET 8, EF Core 10.x for .NET 10). Use the managing-package-references skill to add these packages — it handles version determination, CPM detection, and NuGet feed lookup.
Replace all System.Data.Entity usings with Microsoft.EntityFrameworkCore. Note that System.Data.Entity.Validation and System.Data.Entity.Core.Objects are removed entirely in EF Core — replace with custom validation and DbContext APIs respectively.
DbContextOptions<T> instead of a connection string nameOnModelCreating parameter from DbModelBuilder to ModelBuilderDatabase initializers: EF Core does not support Database.SetInitializer or the MigrateDatabaseToLatestVersion initializer. Remove all initializer calls and convert seed logic to HasData() in OnModelCreating or a separate seed method called at startup. Also delete the EF6 Configuration class (typically Migrations/Configuration.cs) generated by Enable-Migrations — it has no equivalent in EF Core.
EntityTypeConfiguration<T> to IEntityTypeConfiguration<T> (add Configure(EntityTypeBuilder<T>) method, prefix all calls with builder.)HasRequired/HasOptional with HasOne + .IsRequired(), and WithRequired/WithOptional with WithOne + .IsRequired()WillCascadeOnDelete(false) with OnDelete(DeleteBehavior.Restrict)HasDatabaseGeneratedOption:DatabaseGeneratedOption.Identity → ValueGeneratedOnAdd() (database-generated IDs)DatabaseGeneratedOption.Computed → ValueGeneratedOnAddOrUpdate() (computed columns)DatabaseGeneratedOption.None → Check if you manually assign IDs before Add(). If yes, use ValueGeneratedNever(). If no (or using custom HiLo generator), use ValueGeneratedOnAdd() or EF Core's built-in UseHiLo() to avoid tracking conflicts from duplicate default IDsMap(m => m.ToTable("Name")) with ToTable("Name") directlyHasMany().WithMany() requires EF Core 5.0+. For join tables with payload columns, explicit join entity configuration is still requiredMapToStoredProcedures() is removed — use FromSql() or ExecuteSql()modelBuilder.Conventions.Remove<T>() is removed — override ConfigureConventions(ModelConfigurationBuilder) on the DbContext to add, remove, or replace conventionsDecimal property precision: EF6 automatically mapped decimal properties to decimal(18,2). EF Core requires explicit precision configuration to avoid silent data truncation:
// EF6 - automatic precision
public decimal Price { get; set; } // Became decimal(18,2) in database
// EF Core - must specify precision
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasPrecision(18, 2); // Or use [Column(TypeName = "decimal(18,2)")]
}
Register configurations: Replace modelBuilder.Configurations.Add(...) with modelBuilder.ApplyConfiguration(...) or use modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly) to apply all at once.
Raw SQL: Replace Database.SqlQuery<T>() with Set<T>().FromSql() and Database.ExecuteSqlCommand() with Database.ExecuteSql(). Use interpolated strings ($"...") for automatic parameterization — do not use string concatenation, which bypasses EF Core's parameterization and creates SQL injection vulnerabilities.
Lazy loading: EF Core disables lazy loading by default.
Microsoft.EntityFrameworkCore.Proxies, call UseLazyLoadingProxies().Include() for eager loadingComplex types: In EF Core 7 and earlier, [ComplexType] → OwnsOne(). EF Core 8+ reintroduces native support — use ComplexProperty() instead.
Validation: EF Core does not call IValidatableObject.Validate() on SaveChanges(). Add validation in the application layer or override SaveChanges() to call it explicitly.
Advise the user to apply all pending EF6 migrations to their database before starting the EF Core migration — this is a manual prerequisite. Once confirmed:
Migrations/ folder (contains EF6 DbMigration classes and the Configuration class generated by Enable-Migrations)dotnet ef migrations add InitialCreate
InitialCreate) and confirm it reflects their current schema — not new changes. If the migration contains unexpected schema alterations, applying it could cause data loss. Only proceed once the user confirms the migration is a clean baseline.dotnet ef database update
Alternatively, to avoid connecting to the database directly, generate an idempotent SQL script:
dotnet ef migrations script --idempotent --output mark-migration.sql
Advise the user to review the generated script and apply only the INSERT INTO __EFMigrationsHistory statement to their database. This registers the migration as applied without modifying the schema.
Compilation alone is not sufficient. Many EF6→EF Core differences only surface at runtime.
LLM responsibilities (automated):
System.Data.Entity namespaces, EntityFramework package reference, Migrations/Configuration.cs)User responsibilities (require database access and manual verification):
FromSql/ExecuteSql resultsPresent items 4–8 as a checklist for the user — the LLM cannot validate runtime database behavior.
EntityFramework NuGet package removed, EF Core packages addedSystem.Data.Entity namespaces replaced with Microsoft.EntityFrameworkCoreDbContext constructor accepts DbContextOptionsEntityTypeConfiguration<T> classes converted to IEntityTypeConfiguration<T>HasRequired → HasOne, etc.)FromSql/ExecuteSqlConfiguration class removed, seed data migratedTake microsoft/migrating-ef6-code-first-to-ef-core from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.