Claude-skill-registry aspnet-core-advanced
Master advanced ASP.NET Core development including Entity Framework Core, authentication, testing, and enterprise patterns for production applications.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/aspnet-core-advanced" ~/.claude/skills/majiayu000-claude-skill-registry-aspnet-core-advanced && rm -rf "$T"
manifest:
skills/data/aspnet-core-advanced/SKILL.mdsource content
ASP.NET Core Advanced Development
Skill Overview
Production-grade advanced skill for enterprise ASP.NET Core applications. Covers Entity Framework Core 8/9, authentication, advanced testing, caching, and design patterns with comprehensive observability.
Advanced Skills
Entity Framework Core 8/9
core_features: dbcontext: - Lifetime management (scoped) - Connection pooling - Query tracking behavior - Interceptors and events - Compiled models mapping: fluent_api: - Entity configuration - Relationships (HasOne, HasMany) - Complex types (.NET 8+) - JSON columns - Owned types conventions: - Table naming - Key naming - Property discovery queries: linq: - Projection (Select) - Filtering (Where) - Sorting (OrderBy) - Grouping (GroupBy) - Joins (Include, ThenInclude) raw_sql: - FromSqlRaw - FromSqlInterpolated - ExecuteSql performance: optimization: - No-tracking queries - Split queries (AsSplitQuery) - Compiled queries - Bulk operations - Connection resiliency monitoring: - Query logging - Execution plan analysis - Missing index detection new_in_ef9: - LINQ improvements - Better JSON column support - Improved migrations - ExecuteUpdate/Delete enhancements
Authentication & Authorization
authentication_schemes: jwt_bearer: setup: - Configure token validation - Set issuer and audience - Configure signing key best_practices: - Short token expiry - Refresh token rotation - Secure key storage cookie: setup: - Configure cookie options - Set secure and httpOnly - Configure SameSite policy use_cases: - Server-rendered apps - MVC applications oauth2_openid: providers: - Microsoft Identity - Google - GitHub - Custom OIDC flows: - Authorization code - Client credentials - Device code authorization: role_based: - [Authorize(Roles = "Admin")] - User.IsInRole("Admin") claims_based: - [Authorize(Policy = "RequireClaim")] - RequireClaim("permission", "read") policy_based: - Custom requirements - Authorization handlers - Resource-based resource_based: - IAuthorizationService - Document-level permissions
Testing Strategies
unit_testing: frameworks: - xUnit (recommended) - NUnit - MSTest patterns: - Arrange-Act-Assert (AAA) - Given-When-Then (BDD) mocking: - Moq - NSubstitute - FakeItEasy integration_testing: web_application_factory: - In-memory test server - Custom configuration - Service replacement database: - In-memory provider - TestContainers - Respawn for cleanup test_data: builders: - Fluent builders - Object mothers - AutoFixture fixtures: - Shared context - Disposable resources coverage: tools: - Coverlet - ReportGenerator targets: - 80% line coverage - 70% branch coverage
Caching Strategies
in_memory: IMemoryCache: - Local cache - Fast access - Limited to single instance best_for: - Session data - Configuration - Frequently accessed data distributed: IDistributedCache: providers: - Redis - SQL Server - NCache features: - Multi-instance support - Persistence - TTL policies hybrid_cache: # .NET 9 features: - Two-tier caching - Automatic stampede protection - Tag-based invalidation configuration: - L1 (memory) - L2 (distributed) output_caching: attributes: - [OutputCache(Duration = 60)] - [OutputCache(VaryByQuery = "id")] policies: - Custom cache policies - Cache profiles response_caching: headers: - Cache-Control - ETag - Vary
Performance Optimization
async_patterns: best_practices: - Async all the way - Avoid .Result and .Wait() - Use CancellationToken - ConfigureAwait(false) in libraries common_issues: - Deadlocks - Thread starvation - Excessive allocations memory_optimization: techniques: - Object pooling - Span<T> and Memory<T> - ArrayPool<T> - String interning tools: - dotnet-counters - dotnet-trace - Memory profilers database_optimization: query_level: - Projection (select only needed) - Pagination - Indexing - Query splitting connection_level: - Connection pooling - Connection resiliency - Read replicas
Code Examples
Entity Framework Core Configuration
// DbContext with production configuration public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Product> Products => Set<Product>(); public DbSet<Category> Categories => Set<Category>(); public DbSet<Order> Orders => Set<Order>(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly( typeof(ApplicationDbContext).Assembly); // Global query filters modelBuilder.Entity<Product>() .HasQueryFilter(p => !p.IsDeleted); // JSON column mapping (.NET 8+) modelBuilder.Entity<Order>() .OwnsOne(o => o.ShippingAddress, builder => { builder.ToJson(); }); } protected override void ConfigureConventions( ModelConfigurationBuilder configurationBuilder) { // Global conventions configurationBuilder.Properties<decimal>() .HavePrecision(18, 2); configurationBuilder.Properties<string>() .HaveMaxLength(500); } } // Entity configuration public class ProductConfiguration : IEntityTypeConfiguration<Product> { public void Configure(EntityTypeBuilder<Product> builder) { builder.ToTable("Products"); builder.HasKey(p => p.Id); builder.Property(p => p.Name) .HasMaxLength(200) .IsRequired(); builder.Property(p => p.Price) .HasPrecision(18, 2); builder.HasOne(p => p.Category) .WithMany(c => c.Products) .HasForeignKey(p => p.CategoryId) .OnDelete(DeleteBehavior.Restrict); builder.HasIndex(p => p.Sku) .IsUnique(); builder.HasIndex(p => new { p.CategoryId, p.Name }); } } // Registration with production settings builder.Services.AddDbContext<ApplicationDbContext>(options => { options.UseSqlServer( builder.Configuration.GetConnectionString("Default"), sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); sqlOptions.CommandTimeout(30); sqlOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); }); if (!builder.Environment.IsDevelopment()) { options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTrackingWithIdentityResolution); } });
JWT Authentication Setup
// Program.cs - JWT configuration builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)), ClockSkew = TimeSpan.FromMinutes(1) }; options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { if (context.Exception is SecurityTokenExpiredException) { context.Response.Headers.Append( "Token-Expired", "true"); } return Task.CompletedTask; } }; }); // Token generation service public class TokenService : ITokenService { private readonly IConfiguration _configuration; public TokenService(IConfiguration configuration) { _configuration = configuration; } public string GenerateToken(User user, IEnumerable<string> roles) { var claims = new List<Claim> { new(ClaimTypes.NameIdentifier, user.Id.ToString()), new(ClaimTypes.Email, user.Email), new(ClaimTypes.Name, user.UserName), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) }; claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); var key = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]!)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _configuration["Jwt:Issuer"], audience: _configuration["Jwt:Audience"], claims: claims, expires: DateTime.UtcNow.AddHours(1), signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(token); } }
Repository Pattern with Unit of Work
// Generic repository interface public interface IRepository<T> where T : Entity { Task<T?> GetByIdAsync(int id, CancellationToken ct = default); Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default); Task<IReadOnlyList<T>> GetAsync( Expression<Func<T, bool>> predicate, CancellationToken ct = default); Task<T> AddAsync(T entity, CancellationToken ct = default); void Update(T entity); void Delete(T entity); } // Generic repository implementation public class Repository<T> : IRepository<T> where T : Entity { protected readonly ApplicationDbContext _context; protected readonly DbSet<T> _dbSet; public Repository(ApplicationDbContext context) { _context = context; _dbSet = context.Set<T>(); } public virtual async Task<T?> GetByIdAsync(int id, CancellationToken ct = default) { return await _dbSet.FindAsync(new object[] { id }, ct); } public virtual async Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default) { return await _dbSet.ToListAsync(ct); } public virtual async Task<IReadOnlyList<T>> GetAsync( Expression<Func<T, bool>> predicate, CancellationToken ct = default) { return await _dbSet.Where(predicate).ToListAsync(ct); } public virtual async Task<T> AddAsync(T entity, CancellationToken ct = default) { await _dbSet.AddAsync(entity, ct); return entity; } public virtual void Update(T entity) { _dbSet.Attach(entity); _context.Entry(entity).State = EntityState.Modified; } public virtual void Delete(T entity) { if (_context.Entry(entity).State == EntityState.Detached) { _dbSet.Attach(entity); } _dbSet.Remove(entity); } } // Unit of Work public interface IUnitOfWork : IDisposable { IRepository<Product> Products { get; } IRepository<Category> Categories { get; } IRepository<Order> Orders { get; } Task<int> SaveChangesAsync(CancellationToken ct = default); } public class UnitOfWork : IUnitOfWork { private readonly ApplicationDbContext _context; private IRepository<Product>? _products; private IRepository<Category>? _categories; private IRepository<Order>? _orders; public UnitOfWork(ApplicationDbContext context) { _context = context; } public IRepository<Product> Products => _products ??= new Repository<Product>(_context); public IRepository<Category> Categories => _categories ??= new Repository<Category>(_context); public IRepository<Order> Orders => _orders ??= new Repository<Order>(_context); public async Task<int> SaveChangesAsync(CancellationToken ct = default) { return await _context.SaveChangesAsync(ct); } public void Dispose() { _context.Dispose(); } }
HybridCache Implementation (.NET 9)
// Registration builder.Services.AddHybridCache(options => { options.MaximumPayloadBytes = 1024 * 1024; // 1MB options.MaximumKeyLength = 512; options.DefaultEntryOptions = new HybridCacheEntryOptions { Expiration = TimeSpan.FromMinutes(5), LocalCacheExpiration = TimeSpan.FromMinutes(1) }; }); // Add Redis as L2 cache builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("Redis"); }); // Usage in service public class ProductService : IProductService { private readonly HybridCache _cache; private readonly ApplicationDbContext _context; private readonly ILogger<ProductService> _logger; public ProductService( HybridCache cache, ApplicationDbContext context, ILogger<ProductService> logger) { _cache = cache; _context = context; _logger = logger; } public async Task<ProductDto?> GetByIdAsync(int id, CancellationToken ct) { var cacheKey = $"product:{id}"; var product = await _cache.GetOrCreateAsync( cacheKey, async token => { _logger.LogDebug("Cache miss for product {ProductId}", id); return await _context.Products .AsNoTracking() .Where(p => p.Id == id) .Select(p => new ProductDto { Id = p.Id, Name = p.Name, Price = p.Price, CategoryName = p.Category.Name }) .FirstOrDefaultAsync(token); }, new HybridCacheEntryOptions { Expiration = TimeSpan.FromMinutes(10), LocalCacheExpiration = TimeSpan.FromMinutes(2) }, cancellationToken: ct); return product; } public async Task InvalidateCacheAsync(int productId, CancellationToken ct) { await _cache.RemoveAsync($"product:{productId}", ct); } }
Integration Testing with WebApplicationFactory
public class ProductsApiTests : IClassFixture<WebApplicationFactory<Program>>, IAsyncLifetime { private readonly WebApplicationFactory<Program> _factory; private readonly HttpClient _client; private AsyncServiceScope _scope; private ApplicationDbContext _dbContext = null!; public ProductsApiTests(WebApplicationFactory<Program> factory) { _factory = factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // Remove existing DbContext var descriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>)); if (descriptor != null) services.Remove(descriptor); // Add in-memory database services.AddDbContext<ApplicationDbContext>(options => { options.UseInMemoryDatabase("TestDb"); }); // Add test authentication services.AddAuthentication("Test") .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>( "Test", options => { }); }); }); _client = _factory.CreateClient(); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test"); } public async Task InitializeAsync() { _scope = _factory.Services.CreateAsyncScope(); _dbContext = _scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); await _dbContext.Database.EnsureCreatedAsync(); // Seed test data _dbContext.Products.AddRange( new Product { Id = 1, Name = "Test Product 1", Price = 10.00m }, new Product { Id = 2, Name = "Test Product 2", Price = 20.00m } ); await _dbContext.SaveChangesAsync(); } public async Task DisposeAsync() { await _dbContext.Database.EnsureDeletedAsync(); await _scope.DisposeAsync(); } [Fact] public async Task GetProducts_ReturnsAllProducts() { // Act var response = await _client.GetAsync("/api/products"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var products = await response.Content.ReadFromJsonAsync<List<ProductDto>>(); products.Should().HaveCount(2); } [Fact] public async Task GetProduct_WhenExists_ReturnsProduct() { // Act var response = await _client.GetAsync("/api/products/1"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var product = await response.Content.ReadFromJsonAsync<ProductDto>(); product!.Name.Should().Be("Test Product 1"); } [Fact] public async Task CreateProduct_WithValidData_ReturnsCreated() { // Arrange var request = new CreateProductRequest { Name = "New Product", Price = 99.99m }; // Act var response = await _client.PostAsJsonAsync("/api/products", request); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); response.Headers.Location.Should().NotBeNull(); } } // Test authentication handler public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public TestAuthHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { var claims = new[] { new Claim(ClaimTypes.NameIdentifier, "test-user-id"), new Claim(ClaimTypes.Email, "test@example.com"), new Claim(ClaimTypes.Role, "Admin") }; var identity = new ClaimsIdentity(claims, "Test"); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, "Test"); return Task.FromResult(AuthenticateResult.Success(ticket)); } }
Troubleshooting Guide
Common Issues
| Issue | Symptoms | Resolution |
|---|---|---|
| N+1 Query Problem | Excessive DB calls | Use or projection |
| Connection Pool Exhaustion | Timeouts | Use , check connection lifetime |
| Deadlocks | Request hangs | Use async, check transaction isolation |
| Token Validation Fails | 401 Unauthorized | Check clock skew, issuer, audience |
| Cache Stampede | All requests hit DB | Use HybridCache or distributed locking |
| Memory Leaks | Growing memory | Check IDisposable, DI lifetimes |
Debug Checklist
step_1_ef_core: - Enable query logging - Check execution plans - Verify indexes - Monitor connection count step_2_authentication: - Validate token format - Check signing key - Verify claims - Inspect authentication events step_3_performance: - Profile with Application Insights - Check async patterns - Review caching strategy - Analyze memory allocations step_4_testing: - Verify test isolation - Check service mocking - Review database cleanup - Inspect test authentication
Assessment Criteria
- Design and implement database schemas with EF Core
- Implement authentication and authorization
- Write comprehensive unit tests (80%+ coverage)
- Write integration tests with WebApplicationFactory
- Optimize database queries
- Implement caching strategies
- Use design patterns appropriately
- Handle errors gracefully
- Implement monitoring and logging
- Apply performance optimizations