# 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".

- Skill: `microsoft/migrating-linq-to-sql-to-ef-core` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add microsoft/migrating-linq-to-sql-to-ef-core`
- Raw SKILL.md: https://api.skillmd.com/api/skills/microsoft/migrating-linq-to-sql-to-ef-core/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: Microsoft (https://skillmd.com/u/microsoft)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/microsoft/migrating-linq-to-sql-to-ef-core

---


# LINQ to SQL to Entity Framework Core Migration

## Overview

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 Core
- `ref/datacontext-to-dbcontext.md` — DataContext lifecycle and API migration
- `ref/query-translation-gotchas.md` — Query behavior differences (critical for runtime correctness)
- `ref/stored-procedure-migration.md` — SP/function mapping
- `ref/concurrency-and-change-tracking.md` — UpdateCheck/conflict resolution
- `ref/relationship-migration.md` — EntitySet/EntityRef to navigation properties

## Prerequisites

Verify LINQ to SQL usage before proceeding:
1. Check for `System.Data.Linq` assembly reference in the project file
2. Search for `.dbml` files in the project directory
3. Search for `using System.Data.Linq` in code files

If none found, skip this skill.

## Workflow

```
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
```

### Step 1: Assess LINQ to SQL Usage Scope

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 classes (search for `: DataContext` or `: System.Data.Linq.DataContext`)
- DataContext instantiation points (`new *DataContext(`)
- Stored procedure mappings (`[Function(` attribute usage)
- `IMultipleResults` usage (**blocker** — no EF Core equivalent; see `ref/stored-procedure-migration.md`)
- `EntitySet<T>` and `EntityRef<T>` usage counts
- `UpdateCheck.WhenChanged` usage (requires concurrency strategy decision)
- Partial class extensions of generated entities (business logic to preserve)

Flag these blockers early:
- `IMultipleResults` → requires ADO.NET fallback or SP redesign
- `UpdateCheck.WhenChanged` → no direct EF Core equivalent (see `ref/concurrency-and-change-tracking.md`)

### Step 2: Set Up EF Core Infrastructure

1. Add NuGet packages:
   - `Microsoft.EntityFrameworkCore.SqlServer` (or appropriate provider)
   - `Microsoft.EntityFrameworkCore.Design` (for tooling)
2. Ensure the project targets .NET 6+ (should already be the case in an upgrade scenario)

### Step 3: Create New EF Core Entity Model

**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:**

1. **New DbContext file** — e.g., `MyDbContext.cs` (or `{OriginalDataContextName}DbContext.cs`). Define the `DbContext` subclass with `DbSet<T>` properties here.
2. **New entity class files** — create one file per entity class (e.g., `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.
3. **Preserve partial class logic** — if the project has partial classes extending DBML-generated entities (separate from the `.designer.cs` file), migrate that business logic into the new entity files.

**Recommended approach: Scaffold first, then reconcile.**

1. Use `dotnet ef dbcontext scaffold "ConnectionString" Microsoft.EntityFrameworkCore.SqlServer -o Entities` to generate baseline entities from the existing database into a new folder
2. Compare scaffolded entities with DBML-defined entities:
   - Reconcile custom C# names from DBML (the DBML may rename entities/properties differently from the database)
   - Preserve custom partial class logic from existing code
3. Apply Fluent API configuration for non-convention mappings
4. If scaffolding is not possible (no database access), manually create entity POCOs by reading the `.dbml` XML to extract table/column definitions, then translate to EF Core attributes

See `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.

### Step 4: Migrate DataContext to DbContext

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>` properties
- `SubmitChanges()` → `SaveChanges()` / `SaveChangesAsync()`
- `GetChangeSet()` → `ChangeTracker.Entries()`
- `ExecuteCommand()` → `Database.ExecuteSqlRaw()`
- `ExecuteQuery<T>()` → `FromSqlRaw()` / `SqlQueryRaw<T>()`
- `DataContext.Log` → `ILoggerFactory` / `LogTo()` on `DbContextOptionsBuilder`
- `ObjectTrackingEnabled = false` → `AsNoTracking()` queries
- Direct instantiation (`new MyDataContext(conn)`) → DI-injected DbContext

**Critical:** 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.

### Step 5: Migrate Stored Procedures and Functions

LINQ to SQL maps SPs as methods on DataContext with `[Function]` attributes. EF Core has no attribute-based SP mapping.

For each stored procedure:
- SPs returning entity types → `FromSqlRaw("EXEC sp_name @p0, @p1", params)`
- SPs with no return → `Database.ExecuteSqlRaw()`
- SPs with non-entity results → `SqlQueryRaw<T>()` (EF Core 8+) with `[Keyless]` result types
- SPs with output parameters → use `SqlParameter` objects directly
- UDFs → `HasDbFunction()` in Fluent API

See `ref/stored-procedure-migration.md` for complete patterns and the `IMultipleResults` blocker.

### Step 6: Fix Relationship Loading Patterns

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:
1. Replace `EntitySet<T>` → `ICollection<T>`
2. Replace `EntityRef<T>` → standard navigation property
3. **Find all navigation property access patterns** and add `Include()` / `ThenInclude()` calls
4. Convert `DataLoadOptions.LoadWith<T>()` → `Include()`
5. Convert `DataLoadOptions.AssociateWith<T>()` → filtered includes

Code that worked before (`order.Customer.Name`) will return `null` / throw `NullReferenceException` without explicit loading.

See `ref/relationship-migration.md` for detailed patterns.

### Step 7: Migrate Concurrency Handling

If the project uses `UpdateCheck` attributes on columns, a concurrency strategy decision is needed.

Decision tree:
1. Project uses `rowversion`/`timestamp` column → use `[Timestamp]` attribute (straightforward)
2. Project uses `UpdateCheck.Always` on all columns → add a `rowversion` column (recommended) or use `[ConcurrencyCheck]`
3. Project uses `UpdateCheck.WhenChanged` → **no direct equivalent** — recommend adding `rowversion` column
4. `UpdateCheck.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.

### Step 8: Fix Query Translation Issues

EF Core is stricter than LINQ to SQL about query translation. The #1 source of runtime breaks:

- **Client-side evaluation**: LINQ to SQL silently evaluated untranslatable expressions client-side. EF Core throws. Add `.AsEnumerable()` before client-side operations.
- **GroupBy**: Complex GroupBy with materialization fails in EF Core
- **Take/Skip without OrderBy**: EF Core requires explicit `OrderBy`
- **String comparison**: Case-sensitivity may differ by provider
- **CompiledQuery**: Remove `CompiledQuery.Compile()` calls — EF Core handles compilation automatically

See `ref/query-translation-gotchas.md` for the complete list.

### Step 9: Validate

1. Build all affected projects — zero errors required
2. Search for remaining `System.Data.Linq` references in code
3. Search for remaining `DataContext`, `EntitySet`, `EntityRef` type usage
4. Verify navigation property loading works (test queries that traverse relationships)
5. Verify stored procedure calls work
6. Verify concurrency conflict handling works

**Only 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.

### Step 10: Remove LINQ to SQL Artifacts

**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):
1. **`*.dbml`** files — the LINQ to SQL O/R Designer model definition (XML). Not used by EF Core.
2. **`*.dbml.layout`** files — Visual Studio designer UI layout. Not used by EF Core.
3. **DBML-generated `*.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:
4. Remove `System.Data.Linq` assembly reference from the project file (`.csproj`)
5. Remove `using System.Data.Linq` and `using System.Data.Linq.Mapping` statements from all code files
6. Remove any `<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.

## Disambiguation

Do NOT apply this skill to:
- `System.Linq` (LINQ to Objects) — works in modern .NET
- `System.Xml.Linq` (LINQ to XML) — works in modern .NET
- `System.Data.Entity` (Entity Framework 6) — separate migration skill
- `LinqToDB` (linq2db) — third-party library, different migration path

## Success Criteria

- **New clean files created** for DbContext and entity classes (not rewritten in the old `*.designer.cs`)
- **All LINQ to SQL artifacts deleted**: `*.dbml`, `*.dbml.layout`, DBML-generated `*.designer.cs`
- All `System.Data.Linq` references removed from project file and code
- EF Core DbContext replaces all DataContext usage
- All stored procedures migrated to EF Core patterns
- Navigation property loading is explicit (no silent null references)
- Concurrency strategy decided and implemented
- Project builds and queries execute correctly

