microsoft/migrating-linq-to-sql-to-ef-core
> Migrates LINQ to SQL (System.Data.Linq) data access layer to Entity Framework Core during .NET Framework to modern .NET upgrades. Covers DataContext to DbContext conversion, DBML entity mapping to EF Core model configuration, stored procedure migration, query translation differences, and concurrency handling changes. Use when assessment detects LINQ to SQL usage or when upgrading projects referencing System.Data.Linq. Triggers for "LINQ to SQL", "System.Data.Linq", "DataContext migration", "DBML to EF Core", "linq2sql", "migrate LINQ to SQL".
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill migrating-linq-to-sql-to-ef-core
Migrates .NET Framework projects from LINQ to SQL (System.Data.Linq) to Entity Framework Core. LINQ to SQL is not available in .NET 6+ — this is a migration blocker, not an optional improvement.
Reference files for detailed patterns:
ref/entity-mapping-conversion.md — DBML/attribute mapping to EF Coreref/datacontext-to-dbcontext.md — DataContext lifecycle and API migrationref/query-translation-gotchas.md — Query behavior differences (critical for runtime correctness)ref/stored-procedure-migration.md — SP/function mappingref/concurrency-and-change-tracking.md — UpdateCheck/conflict resolutionref/relationship-migration.md — EntitySet/EntityRef to navigation propertiesVerify LINQ to SQL usage before proceeding:
System.Data.Linq assembly reference in the project file.dbml files in the project directoryusing System.Data.Linq in code filesIf none found, skip this skill.
Migration Progress:
- [ ] Step 1: Assess LINQ to SQL usage scope
- [ ] Step 2: Set up EF Core infrastructure
- [ ] Step 3: Scaffold or convert entity model
- [ ] Step 4: Migrate DataContext to DbContext
- [ ] Step 5: Migrate stored procedures and functions
- [ ] Step 6: Fix relationship loading patterns
- [ ] Step 7: Migrate concurrency handling
- [ ] Step 8: Fix query translation issues
- [ ] Step 9: Validate
- [ ] Step 10: Remove LINQ to SQL artifacts
Before making changes, inventory the full scope. This determines effort and identifies blockers.
Find and count:
.dbml files and their entity/SP counts (parse the XML): DataContext or : System.Data.Linq.DataContext)new *DataContext()[Function( attribute usage)IMultipleResults usage (blocker — no EF Core equivalent; see ref/stored-procedure-migration.md)EntitySet<T> and EntityRef<T> usage countsUpdateCheck.WhenChanged usage (requires concurrency strategy decision)Flag these blockers early:
IMultipleResults → requires ADO.NET fallback or SP redesignUpdateCheck.WhenChanged → no direct EF Core equivalent (see ref/concurrency-and-change-tracking.md)Microsoft.EntityFrameworkCore.SqlServer (or appropriate provider)Microsoft.EntityFrameworkCore.Design (for tooling)CRITICAL: Create new files — do NOT rewrite the DBML-generated *.designer.cs file in place.
The *.designer.cs file (e.g., CompanyDB.designer.cs) was auto-generated by the LINQ to SQL O/R Designer. It is full of LINQ to SQL plumbing (INotifyPropertyChanging, EntitySet<T>, EntityRef<T>, OnXChanging/OnXChanged partial methods). Do not attempt to "convert" this file into EF Core entities — it will retain confusing artifacts and the .designer.cs name implies auto-generation.
Instead, create new clean files:
MyDbContext.cs (or {OriginalDataContextName}DbContext.cs). Define the DbContext subclass with DbSet<T> properties here.Entities/Customer.cs, Entities/Order.cs) or a single Entities.cs file for small models. Write clean POCO classes with EF Core data annotations or Fluent API configuration..designer.cs file), migrate that business logic into the new entity files.Recommended approach: Scaffold first, then reconcile.
dotnet ef dbcontext scaffold "ConnectionString" Microsoft.EntityFrameworkCore.SqlServer -o Entities to generate baseline entities from the existing database into a new folder.dbml XML to extract table/column definitions, then translate to EF Core attributesSee ref/entity-mapping-conversion.md for the complete attribute mapping table and DBML conversion strategy.
Important: During conversion, both LINQ to SQL and EF Core use [Table] and [Column] attributes from different namespaces. Use fully-qualified names or manage using directives carefully to avoid ambiguous reference errors.
Replace all DataContext usage with the new DbContext class created in Step 3. This is not just a rename — the lifecycle model differs fundamentally.
Update all consuming code to reference the new DbContext class name and the new entity types:
Table<T> properties → DbSet<T> propertiesSubmitChanges() → SaveChanges() / SaveChangesAsync()GetChangeSet() → ChangeTracker.Entries()ExecuteCommand() → Database.ExecuteSqlRaw()ExecuteQuery<T>() → FromSqlRaw() / SqlQueryRaw<T>()DataContext.Log → ILoggerFactory / LogTo() on DbContextOptionsBuilderObjectTrackingEnabled = false → AsNoTracking() queriesnew MyDataContext(conn)) → DI-injected DbContextCritical: If DataContext was used in using blocks and you migrate to DI-managed DbContext (scoped lifetime), remove the using blocks — the DI container manages disposal. Keeping both causes premature disposal.
See ref/datacontext-to-dbcontext.md for detailed API mappings and lifecycle patterns.
LINQ to SQL maps SPs as methods on DataContext with [Function] attributes. EF Core has no attribute-based SP mapping.
For each stored procedure:
FromSqlRaw("EXEC sp_name @p0, @p1", params)Database.ExecuteSqlRaw()SqlQueryRaw<T>() (EF Core 8+) with [Keyless] result typesSqlParameter objects directlyHasDbFunction() in Fluent APISee ref/stored-procedure-migration.md for complete patterns and the IMultipleResults blocker.
LINQ to SQL lazy-loads via EntitySet<T> / EntityRef<T> by default. EF Core does NOT lazy-load by default.
This is the highest-risk area for subtle runtime bugs:
EntitySet<T> → ICollection<T>EntityRef<T> → standard navigation propertyInclude() / ThenInclude() callsDataLoadOptions.LoadWith<T>() → Include()DataLoadOptions.AssociateWith<T>() → filtered includesCode that worked before (order.Customer.Name) will return null / throw NullReferenceException without explicit loading.
See ref/relationship-migration.md for detailed patterns.
If the project uses UpdateCheck attributes on columns, a concurrency strategy decision is needed.
Decision tree:
rowversion/timestamp column → use [Timestamp] attribute (straightforward)UpdateCheck.Always on all columns → add a rowversion column (recommended) or use [ConcurrencyCheck]UpdateCheck.WhenChanged → no direct equivalent — recommend adding rowversion columnUpdateCheck.Never → those columns simply don't get [ConcurrencyCheck]Also migrate conflict resolution: ChangeConflictException → DbUpdateConcurrencyException (different resolution API).
See ref/concurrency-and-change-tracking.md for the complete decision tree and API mappings.
EF Core is stricter than LINQ to SQL about query translation. The #1 source of runtime breaks:
.AsEnumerable() before client-side operations.OrderByCompiledQuery.Compile() calls — EF Core handles compilation automaticallySee ref/query-translation-gotchas.md for the complete list.
System.Data.Linq references in codeDataContext, EntitySet, EntityRef type usageOnly proceed to Step 10 after validation passes. The old .dbml and .designer.cs files are useful as a reference if you need to fix issues found during validation.
MANDATORY — do not skip this step. LINQ to SQL artifacts serve no purpose after migration and will confuse future developers.
Delete these files (verify each exists before deleting):
*.dbml files — the LINQ to SQL O/R Designer model definition (XML). Not used by EF Core.*.dbml.layout files — Visual Studio designer UI layout. Not used by EF Core.*.designer.cs files — the auto-generated DataContext and entity classes. These must have been replaced by new clean files in Step 3. Do NOT delete WinForms/WPF *.Designer.cs files — only the one that was generated from the .dbml (typically named {DbmlName}.designer.cs and containing DataContext inheritance).Then clean up remaining references:
System.Data.Linq assembly reference from the project file (.csproj)using System.Data.Linq and using System.Data.Linq.Mapping statements from all code files<None Include="*.dbml"> or <Compile Include="*.designer.cs"> entries with <DependentUpon>*.dbml</DependentUpon> or <Generator>MSLinqToSQLGenerator</Generator> from the project file (SDK-style projects handle this automatically; old-style .csproj may need manual cleanup)How to identify the DBML-generated designer file: It is the .designer.cs file that has a <DependentUpon>SomeName.dbml</DependentUpon> entry in the project file, or that contains a class inheriting from System.Data.Linq.DataContext. Do NOT confuse it with WinForms Form1.Designer.cs files — those are unrelated.
Final check: After cleanup, confirm zero *.dbml, *.dbml.layout files remain and no System.Data.Linq references exist anywhere in the project.
Do NOT apply this skill to:
System.Linq (LINQ to Objects) — works in modern .NETSystem.Xml.Linq (LINQ to XML) — works in modern .NETSystem.Data.Entity (Entity Framework 6) — separate migration skillLinqToDB (linq2db) — third-party library, different migration path*.designer.cs)*.dbml, *.dbml.layout, DBML-generated *.designer.csSystem.Data.Linq references removed from project file and codeTake microsoft/migrating-linq-to-sql-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.