Claude Skill

dotnet-entity-framework6

Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. Use when working in an EF6 codebase or planning a data layer migration.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download postpartum-genushyacinthus29-dotnet-skills-skills_dotnet-entity-framework6-bfa4ebd.zip · 7 KB
Part of postpartum-genushyacinthus29/dotnet-skills — 80 skills

Install

skills CLI npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills/tree/main/skills/dotnet-entity-framework6
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install postpartum-genushyacinthus29-dotnet-skills@llmmart
Git git clone https://github.com/Postpartum-genushyacinthus29/dotnet-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole postpartum-genushyacinthus29/dotnet-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Entity Framework 6

Trigger On

  • working in an EF6 codebase on .NET Framework or modern .NET
  • deciding whether to keep EF6, move to modern .NET runtime, or port to EF Core
  • reviewing EDMX, code-first, or legacy ASP.NET/WPF/WinForms data access
  • planning a data layer migration strategy

Workflow

  1. Audit current EF6 usage before planning any migration. Identify which features the codebase depends on:

    // Common EF6-specific patterns to inventory:
    // - EDMX designer models (check for *.edmx files)
    // - ObjectContext vs DbContext usage
    // - Lazy loading with virtual navigation properties
    // - Database.SqlQuery<T>() for raw SQL
    // - Stored procedure mappings in model
    // - Spatial types (DbGeography, DbGeometry)
    
  2. Decide runtime vs ORM migration separately:

    Path When to use
    Keep EF6 on .NET Framework Legacy app with no runtime pressure
    EF6 on modern .NET Runtime upgrade needed, ORM migration too risky
    EF6 → EF Core Clean data layer, no EDMX, minimal stored-procedure mapping
  3. For maintenance work — keep EF6 stable:

    • use repository + unit of work patterns to isolate data access (see references/patterns.md)
    • prefer DbContext over ObjectContext for new code
    • use AsNoTracking() for read-only queries
    • configure concurrency tokens with [ConcurrencyCheck] or IsRowVersion()
  4. For migration work — validate each slice:

    • map EF6 features to EF Core equivalents (see references/migration.md)
    • migrate one bounded context at a time, not the entire data layer
    • run integration tests against the real database provider, not InMemory
    • verify: dotnet ef migrations add succeeds, queries produce equivalent results, lazy loading behavior matches expectations
  5. Do not promise EF Core features to EF6 codebases — EF6 is stable and supported but not on the innovation path. Keep expectations realistic.

flowchart LR
  A["Audit EF6 usage"] --> B{"EDMX or complex mappings?"}
  B -->|Yes| C["High migration cost — consider keeping EF6"]
  B -->|No| D["Evaluate EF Core migration"]
  D --> E["Migrate one context at a time"]
  E --> F["Integration test against real DB"]
  C --> G["Modernize runtime only"]
  F --> H["Validate query equivalence"]
  G --> H

Deliver

  • realistic EF6 maintenance or migration guidance based on actual codebase audit
  • clear separation between runtime upgrade and ORM upgrade work
  • bounded migration slices with concrete validation checkpoints
  • reduced risk for legacy data access changes

Validate

  • EF6 feature inventory is complete before migration planning starts
  • migration assumptions are backed by real feature usage, not guesses
  • EF6-only features (EDMX, spatial types, ObjectContext patterns) are identified early
  • integration tests run against the real database provider, not mocks or InMemory
  • the proposed path avoids unnecessary churn in stable data access code

References

  • references/migration.md - decision framework, migration approaches, EF6-to-EF Core feature mapping, and common pitfalls
  • references/patterns.md - repository and unit of work patterns, query optimization, concurrency handling, auditing, and testing strategies for EF6 codebases
Files (dotnet-skills)
  • references
    • migration.md 5.9 KB
      # EF6 to EF Core Migration Guide
      
      ## Migration Decision Framework
      
      ### When to Stay on EF6
      
      - Application is stable and not actively evolving
      - Heavy use of EDMX designer models without code-first equivalents
      - Complex stored procedure mappings that drive business logic
      - Dependency on ObjectContext or ObjectStateManager APIs
      - No plans to move the host application to modern .NET
      
      ### When to Consider EF Core
      
      - Targeting .NET 6+ or planning a runtime migration
      - Need for cross-platform deployment (Linux, containers)
      - Desire for improved performance characteristics
      - Need for features only available in EF Core (e.g., compiled queries, split queries, interceptors)
      - Starting a new module or service that will coexist with EF6 code
      
      ### When to Skip EF Core Entirely
      
      - Moving to a different data access strategy (Dapper, raw ADO.NET, document stores)
      - Decomposing the monolith into microservices with dedicated data strategies
      - The data layer is being replaced by an external API or service
      
      ## Migration Approaches
      
      ### Parallel Coexistence
      
      Run EF6 and EF Core side by side in the same solution:
      
      1. Add EF Core packages alongside existing EF6 references
      2. Create a new DbContext for EF Core targeting the same database
      3. Migrate entity configurations incrementally
      4. Gradually shift new features and queries to EF Core
      5. Retire EF6 DbContext once all code paths are migrated
      
      Benefits:
      - No big-bang cutover
      - Validate behavior slice by slice
      - Rollback is straightforward
      
      Risks:
      - Two DbContexts means two change trackers; avoid crossing them in the same unit of work
      - Schema migrations need coordination
      
      ### Module-by-Module Migration
      
      Migrate entire bounded contexts or modules at once:
      
      1. Identify module boundaries in the existing codebase
      2. Extract the module's data access into a dedicated project
      3. Port that project to EF Core and modern .NET
      4. Integrate via API or shared database until full cutover
      
      Benefits:
      - Clean separation reduces cross-cutting risks
      - Easier to test in isolation
      
      Risks:
      - Requires clear module boundaries
      - May need temporary integration shims
      
      ### Big-Bang Rewrite
      
      Replace the entire data layer in a single release:
      
      1. Map all entities, configurations, and queries
      2. Port all migrations or regenerate schema
      3. Run extensive regression testing
      4. Deploy as a single release
      
      Benefits:
      - No ongoing dual maintenance
      
      Risks:
      - High risk of regressions
      - Long development cycle without production feedback
      - Rollback is difficult
      
      ## Feature Mapping
      
      ### Entity Configuration
      
      | EF6                             | EF Core                                      |
      |---------------------------------|----------------------------------------------|
      | `EntityTypeConfiguration<T>`    | `IEntityTypeConfiguration<T>` or Fluent API  |
      | `modelBuilder.Configurations.Add()` | `modelBuilder.ApplyConfigurationsFromAssembly()` |
      | EDMX designer                   | No equivalent; use code-first                |
      | Complex Types                   | Owned Types                                  |
      
      ### Lazy Loading
      
      | EF6                                  | EF Core                                      |
      |--------------------------------------|----------------------------------------------|
      | Enabled by default with virtual props | Opt-in via `UseLazyLoadingProxies()` or `ILazyLoader` |
      
      ### Change Tracking
      
      | EF6                        | EF Core                                      |
      |----------------------------|----------------------------------------------|
      | Snapshot by default        | Snapshot by default; change-tracking proxies optional |
      | `ObjectStateManager`       | `ChangeTracker`                              |
      
      ### Stored Procedures
      
      | EF6                                         | EF Core                                      |
      |---------------------------------------------|----------------------------------------------|
      | `MapToStoredProcedures()` for CUD           | Sproc mapping for CUD introduced in EF Core 7 |
      | Function imports in EDMX                    | `FromSql` or raw SQL                         |
      
      ### Migrations
      
      | EF6                           | EF Core                                      |
      |-------------------------------|----------------------------------------------|
      | `Add-Migration`, `Update-Database` | `Add-Migration`, `Update-Database` (similar commands) |
      | `__MigrationHistory` table    | `__EFMigrationsHistory` table                |
      
      ## Common Pitfalls
      
      ### Behavioral Differences
      
      - **Query translation**: EF Core has different LINQ translation behavior; some queries that worked in EF6 may throw or produce different SQL
      - **Cascade delete defaults**: EF Core defaults to cascade delete for required relationships; EF6 does not
      - **Shadow properties**: EF Core tracks FK values as shadow properties by default; EF6 requires explicit FK properties
      - **Global query filters**: EF Core supports them; EF6 does not
      
      ### Missing Features in EF Core
      
      Some EF6 features have no direct equivalent:
      
      - EDMX designer and visual model-first workflows
      - ObjectContext API (only DbContext is supported)
      - Automatic migrations (removed; use explicit migrations)
      - Entity SQL (ESQL) query language
      
      ### Provider Differences
      
      - Verify your database provider has EF Core support
      - Provider feature parity varies; some advanced database features may differ
      - Test against the real database, not just in-memory
      
      ## Testing Strategy
      
      1. **Characterization tests**: Before migration, write tests that capture current EF6 behavior
      2. **SQL diff comparisons**: Compare generated SQL between EF6 and EF Core for critical queries
      3. **Integration tests**: Run against a real database with realistic data volumes
      4. **Performance baselines**: Measure query performance before and after migration
      
      ## References
      
      - [Microsoft Docs: Porting from EF6 to EF Core](https://learn.microsoft.com/en-us/ef/efcore-and-ef6/porting/)
      - [Microsoft Docs: EF6 Overview](https://learn.microsoft.com/en-us/ef/ef6/)
      - [Microsoft Docs: EF Core Overview](https://learn.microsoft.com/en-us/ef/core/)
      
    • patterns.md 8.6 KB
      # EF6 Maintenance Patterns
      
      ## Repository and Unit of Work
      
      ### Basic Repository Pattern
      
      ```csharp
      public interface IRepository<T> where T : class
      {
          T GetById(int id);
          IQueryable<T> Query();
          void Add(T entity);
          void Update(T entity);
          void Delete(T entity);
      }
      
      public class EF6Repository<T> : IRepository<T> where T : class
      {
          private readonly DbContext _context;
          private readonly DbSet<T> _dbSet;
      
          public EF6Repository(DbContext context)
          {
              _context = context;
              _dbSet = context.Set<T>();
          }
      
          public T GetById(int id) => _dbSet.Find(id);
          public IQueryable<T> Query() => _dbSet;
          public void Add(T entity) => _dbSet.Add(entity);
          public void Update(T entity) => _context.Entry(entity).State = EntityState.Modified;
          public void Delete(T entity) => _dbSet.Remove(entity);
      }
      ```
      
      ### Unit of Work Pattern
      
      ```csharp
      public interface IUnitOfWork : IDisposable
      {
          IRepository<TEntity> Repository<TEntity>() where TEntity : class;
          int SaveChanges();
          Task<int> SaveChangesAsync();
      }
      
      public class EF6UnitOfWork : IUnitOfWork
      {
          private readonly DbContext _context;
          private readonly Dictionary<Type, object> _repositories = new();
      
          public EF6UnitOfWork(DbContext context)
          {
              _context = context;
          }
      
          public IRepository<TEntity> Repository<TEntity>() where TEntity : class
          {
              if (!_repositories.ContainsKey(typeof(TEntity)))
              {
                  _repositories[typeof(TEntity)] = new EF6Repository<TEntity>(_context);
              }
              return (IRepository<TEntity>)_repositories[typeof(TEntity)];
          }
      
          public int SaveChanges() => _context.SaveChanges();
          public Task<int> SaveChangesAsync() => _context.SaveChangesAsync();
      
          public void Dispose() => _context.Dispose();
      }
      ```
      
      ## Connection and Context Management
      
      ### Scoped Context Lifetime
      
      Always scope DbContext to a logical unit of work:
      
      ```csharp
      // ASP.NET MVC: use per-request lifetime
      public class OrderController : Controller
      {
          private readonly MyDbContext _context;
      
          public OrderController(MyDbContext context)
          {
              _context = context;
          }
      }
      
      // Non-web: use explicit using blocks
      using (var context = new MyDbContext())
      {
          var orders = context.Orders.Where(o => o.Status == "Pending").ToList();
          // process orders
          context.SaveChanges();
      }
      ```
      
      ### Avoiding Long-Lived Contexts
      
      Do not:
      - Keep a DbContext alive across multiple HTTP requests
      - Share a DbContext across threads
      - Cache a DbContext in a static field
      
      ### Connection Resiliency
      
      Configure retry logic for transient failures:
      
      ```csharp
      public class MyDbConfiguration : DbConfiguration
      {
          public MyDbConfiguration()
          {
              SetExecutionStrategy("System.Data.SqlClient",
                  () => new SqlAzureExecutionStrategy(5, TimeSpan.FromSeconds(10)));
          }
      }
      ```
      
      ## Query Optimization
      
      ### Eager Loading
      
      Use `Include` to avoid N+1 queries:
      
      ```csharp
      var orders = context.Orders
          .Include(o => o.Customer)
          .Include(o => o.OrderItems.Select(oi => oi.Product))
          .Where(o => o.OrderDate >= startDate)
          .ToList();
      ```
      
      ### Projection
      
      Select only needed columns:
      
      ```csharp
      var orderSummaries = context.Orders
          .Where(o => o.Status == "Shipped")
          .Select(o => new OrderSummaryDto
          {
              OrderId = o.Id,
              CustomerName = o.Customer.Name,
              TotalAmount = o.OrderItems.Sum(oi => oi.Quantity * oi.UnitPrice)
          })
          .ToList();
      ```
      
      ### No-Tracking Queries
      
      Use `AsNoTracking()` for read-only scenarios:
      
      ```csharp
      var products = context.Products
          .AsNoTracking()
          .Where(p => p.IsActive)
          .ToList();
      ```
      
      ### Compiled Queries (LINQ to Entities)
      
      For frequently executed queries:
      
      ```csharp
      private static readonly Func<MyDbContext, int, Customer> GetCustomerById =
          CompiledQuery.Compile<MyDbContext, int, Customer>(
              (ctx, id) => ctx.Customers.FirstOrDefault(c => c.Id == id));
      
      // Usage
      var customer = GetCustomerById(_context, customerId);
      ```
      
      ## Handling Concurrency
      
      ### Optimistic Concurrency with RowVersion
      
      ```csharp
      public class Order
      {
          public int Id { get; set; }
          public string Status { get; set; }
      
          [Timestamp]
          public byte[] RowVersion { get; set; }
      }
      
      // Handle concurrency conflict
      try
      {
          context.SaveChanges();
      }
      catch (DbUpdateConcurrencyException ex)
      {
          var entry = ex.Entries.Single();
          var databaseValues = entry.GetDatabaseValues();
          if (databaseValues == null)
          {
              // Entity was deleted
          }
          else
          {
              // Resolve conflict: client wins, database wins, or merge
              entry.OriginalValues.SetValues(databaseValues);
          }
      }
      ```
      
      ## Auditing and Interception
      
      ### SaveChanges Override for Auditing
      
      ```csharp
      public class AuditableDbContext : DbContext
      {
          public override int SaveChanges()
          {
              var entries = ChangeTracker.Entries()
                  .Where(e => e.Entity is IAuditable &&
                             (e.State == EntityState.Added || e.State == EntityState.Modified));
      
              foreach (var entry in entries)
              {
                  var auditable = (IAuditable)entry.Entity;
                  auditable.ModifiedDate = DateTime.UtcNow;
                  auditable.ModifiedBy = GetCurrentUser();
      
                  if (entry.State == EntityState.Added)
                  {
                      auditable.CreatedDate = DateTime.UtcNow;
                      auditable.CreatedBy = GetCurrentUser();
                  }
              }
      
              return base.SaveChanges();
          }
      }
      ```
      
      ### Command Interception for Logging
      
      ```csharp
      public class LoggingInterceptor : IDbCommandInterceptor
      {
          public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> context)
          {
              LogCommand(command);
          }
      
          public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> context)
          {
              LogCommand(command);
          }
      
          public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> context)
          {
              LogCommand(command);
          }
      
          private void LogCommand(DbCommand command)
          {
              Debug.WriteLine($"SQL: {command.CommandText}");
          }
      
          // Implement other interface members...
      }
      
      // Registration
      DbInterception.Add(new LoggingInterceptor());
      ```
      
      ## Stored Procedure Integration
      
      ### Mapping CUD Operations
      
      ```csharp
      modelBuilder.Entity<Order>()
          .MapToStoredProcedures(s =>
              s.Insert(i => i.HasName("usp_InsertOrder"))
               .Update(u => u.HasName("usp_UpdateOrder"))
               .Delete(d => d.HasName("usp_DeleteOrder")));
      ```
      
      ### Calling Stored Procedures Directly
      
      ```csharp
      // Return entities
      var orders = context.Database.SqlQuery<Order>(
          "EXEC GetOrdersByCustomer @customerId",
          new SqlParameter("@customerId", customerId)).ToList();
      
      // Non-query
      context.Database.ExecuteSqlCommand(
          "EXEC ArchiveOldOrders @cutoffDate",
          new SqlParameter("@cutoffDate", cutoffDate));
      ```
      
      ## Testing Strategies
      
      ### Integration Testing with LocalDB
      
      ```csharp
      [TestClass]
      public class OrderRepositoryTests
      {
          private MyDbContext _context;
      
          [TestInitialize]
          public void Setup()
          {
              var connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=TestDb;Integrated Security=True";
              _context = new MyDbContext(connectionString);
              _context.Database.CreateIfNotExists();
          }
      
          [TestCleanup]
          public void Cleanup()
          {
              _context.Database.Delete();
              _context.Dispose();
          }
      
          [TestMethod]
          public void CanAddOrder()
          {
              var order = new Order { Status = "New" };
              _context.Orders.Add(order);
              _context.SaveChanges();
      
              Assert.IsTrue(order.Id > 0);
          }
      }
      ```
      
      ### Mocking with Interfaces
      
      Wrap DbContext behind an interface for unit testing:
      
      ```csharp
      public interface IMyDbContext
      {
          IDbSet<Order> Orders { get; }
          int SaveChanges();
      }
      
      // In tests, mock IMyDbContext
      var mockContext = new Mock<IMyDbContext>();
      var mockOrders = new Mock<IDbSet<Order>>();
      mockContext.Setup(c => c.Orders).Returns(mockOrders.Object);
      ```
      
      ## Performance Monitoring
      
      ### Database Logging
      
      ```csharp
      context.Database.Log = sql => Debug.WriteLine(sql);
      ```
      
      ### Identifying Slow Queries
      
      Use SQL Server Profiler, Extended Events, or Query Store alongside EF6 logging to correlate slow queries with application code paths.
      
      ## Common Anti-Patterns to Avoid
      
      1. **Lazy loading in loops**: Causes N+1 queries; use eager loading or projection
      2. **Tracking entities unnecessarily**: Use `AsNoTracking()` for read-only queries
      3. **Returning IQueryable from repositories**: Leaks query composition outside the data layer
      4. **Ignoring connection management**: Always dispose DbContext properly
      5. **Mixing ObjectContext and DbContext**: Pick one API and stick with it
      6. **Skipping concurrency handling**: Add RowVersion for entities with concurrent updates
      
  • SKILL.md 3.7 KB
    ---
    name: dotnet-entity-framework6
    version: "1.0.1"
    category: "Data, Distributed, and AI"
    description: "Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. Use when working in an EF6 codebase or planning a data layer migration."
    compatibility: "Requires EF6 or a transition plan from EF6 to EF Core or modern .NET."
    ---
    
    # Entity Framework 6
    
    ## Trigger On
    
    - working in an EF6 codebase on .NET Framework or modern .NET
    - deciding whether to keep EF6, move to modern .NET runtime, or port to EF Core
    - reviewing EDMX, code-first, or legacy ASP.NET/WPF/WinForms data access
    - planning a data layer migration strategy
    
    ## Workflow
    
    1. **Audit current EF6 usage** before planning any migration. Identify which features the codebase depends on:
       ```csharp
       // Common EF6-specific patterns to inventory:
       // - EDMX designer models (check for *.edmx files)
       // - ObjectContext vs DbContext usage
       // - Lazy loading with virtual navigation properties
       // - Database.SqlQuery<T>() for raw SQL
       // - Stored procedure mappings in model
       // - Spatial types (DbGeography, DbGeometry)
       ```
    2. **Decide runtime vs ORM migration separately:**
    
       | Path | When to use |
       |------|-------------|
       | Keep EF6 on .NET Framework | Legacy app with no runtime pressure |
       | EF6 on modern .NET | Runtime upgrade needed, ORM migration too risky |
       | EF6 → EF Core | Clean data layer, no EDMX, minimal stored-procedure mapping |
    
    3. **For maintenance work** — keep EF6 stable:
       - use repository + unit of work patterns to isolate data access (see references/patterns.md)
       - prefer `DbContext` over `ObjectContext` for new code
       - use `AsNoTracking()` for read-only queries
       - configure concurrency tokens with `[ConcurrencyCheck]` or `IsRowVersion()`
    4. **For migration work** — validate each slice:
       - map EF6 features to EF Core equivalents (see references/migration.md)
       - migrate one bounded context at a time, not the entire data layer
       - run integration tests against the real database provider, not InMemory
       - verify: `dotnet ef migrations add` succeeds, queries produce equivalent results, lazy loading behavior matches expectations
    5. **Do not promise EF Core features to EF6 codebases** — EF6 is stable and supported but not on the innovation path. Keep expectations realistic.
    
    ```mermaid
    flowchart LR
      A["Audit EF6 usage"] --> B{"EDMX or complex mappings?"}
      B -->|Yes| C["High migration cost — consider keeping EF6"]
      B -->|No| D["Evaluate EF Core migration"]
      D --> E["Migrate one context at a time"]
      E --> F["Integration test against real DB"]
      C --> G["Modernize runtime only"]
      F --> H["Validate query equivalence"]
      G --> H
    ```
    
    ## Deliver
    
    - realistic EF6 maintenance or migration guidance based on actual codebase audit
    - clear separation between runtime upgrade and ORM upgrade work
    - bounded migration slices with concrete validation checkpoints
    - reduced risk for legacy data access changes
    
    ## Validate
    
    - EF6 feature inventory is complete before migration planning starts
    - migration assumptions are backed by real feature usage, not guesses
    - EF6-only features (EDMX, spatial types, ObjectContext patterns) are identified early
    - integration tests run against the real database provider, not mocks or InMemory
    - the proposed path avoids unnecessary churn in stable data access code
    
    ## References
    
    - references/migration.md - decision framework, migration approaches, EF6-to-EF Core feature mapping, and common pitfalls
    - references/patterns.md - repository and unit of work patterns, query optimization, concurrency handling, auditing, and testing strategies for EF6 codebases
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related