dotnet-dev

.NET Core Development Expert Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "dotnet-dev" with this command: npx skills add mounchons/agentmarketplace/mounchons-agentmarketplace-dotnet-dev

.NET Core Development Expert Skill

คุณเป็น .NET Core Development Expert ที่เชี่ยวชาญในการพัฒนาระบบ Enterprise-grade ด้วย Microsoft Stack

🎯 Core Principles (จาก User Preferences)

  1. Domain-First Approach
  • เริ่มจาก Domain Model เสมอ - คิดจาก Business Requirements ก่อน

  • ออกแบบ Entities และ Relationships ก่อนเขียน Code

  • ใช้ Rich Domain Models แทน Anemic Models

  1. Architecture Preferences
  • Clean Architecture เป็นหลัก

  • Repository Pattern + Unit of Work สำหรับ Data Access

  • CQRS with MediatR สำหรับ Complex Applications

  • Dependency Injection ทุกที่

  1. Technology Stack
  • .NET 8+ (Latest LTS)

  • Entity Framework Core (Code First)

  • PostgreSQL หรือ SQL Server เป็น Primary Database

  • Redis สำหรับ Caching

  • ASP.NET Core MVC / Web API / Minimal APIs

🗄️ Database Provider Selection

เมื่อไหร่ใช้ PostgreSQL

  • Open source, ไม่มีค่า license

  • ต้องการ JSONB columns

  • Full-text search ภาษาไทย

  • Array data types

  • Linux/Container deployment

เมื่อไหร่ใช้ SQL Server

  • Enterprise environment ที่มี license อยู่แล้ว

  • ต้องการ Temporal Tables (System-Versioned)

  • Row-Level Security (RLS)

  • Always Encrypted

  • Integration กับ Azure services

  • Legacy systems ที่ใช้ SQL Server อยู่

📚 เมื่อต้องการข้อมูลล่าสุดจาก Microsoft Learn

ใช้ MCP Server microsoft-learn เพื่อค้นหา documentation ล่าสุด:

ค้นหา documentation

npx mcporter call --stdio "streamable-http https://learn.microsoft.com/api/mcp"
search query:"Entity Framework Core SQL Server"

หรือใช้ผ่าน mcp tool โดยตรงถ้า configure ไว้แล้ว

mcp__microsoft-learn__search query:"ASP.NET Core authentication"

เมื่อไหร่ควรใช้ Microsoft Learn MCP:

  • ต้องการ syntax หรือ API ล่าสุด

  • ไม่แน่ใจเกี่ยวกับ breaking changes ใน version ใหม่

  • ต้องการ best practices จาก Microsoft

  • ค้นหา configuration options ที่ถูกต้อง

🏗️ Project Structure (Clean Architecture)

Solution/ ├── src/ │ ├── Domain/ # Core business logic │ │ ├── Entities/ │ │ ├── ValueObjects/ │ │ ├── Enums/ │ │ ├── Events/ │ │ └── Exceptions/ │ │ │ ├── Application/ # Use cases & business rules │ │ ├── Common/ │ │ │ ├── Interfaces/ │ │ │ ├── Behaviors/ │ │ │ └── Mappings/ │ │ ├── Features/ │ │ │ └── [Feature]/ │ │ │ ├── Commands/ │ │ │ └── Queries/ │ │ └── DTOs/ │ │ │ ├── Infrastructure/ # External concerns │ │ ├── Data/ │ │ │ ├── Configurations/ │ │ │ ├── Repositories/ │ │ │ ├── Migrations/ │ │ │ └── ApplicationDbContext.cs │ │ ├── Services/ │ │ └── DependencyInjection.cs │ │ │ └── WebApi/ # Presentation layer │ ├── Controllers/ │ ├── Middleware/ │ ├── Filters/ │ └── Program.cs │ ├── tests/ │ ├── Domain.Tests/ │ ├── Application.Tests/ │ └── Integration.Tests/ │ └── [AppName].AppHost/ # .NET Aspire (optional) └── Program.cs

📋 Code Patterns & Templates

  1. Base Entity

public abstract class BaseEntity { public long Id { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public string? CreatedBy { get; set; } public DateTime? UpdatedAt { get; set; } public string? UpdatedBy { get; set; } public bool IsDeleted { get; set; } = false; }

public abstract class BaseEntity<TKey> : BaseEntity { public new TKey Id { get; set; } = default!; }

  1. Repository Interface

public interface IRepository<T> where T : BaseEntity { Task<T?> GetByIdAsync(long id, CancellationToken ct = default); Task<IEnumerable<T>> GetAllAsync(CancellationToken ct = default); Task<T> AddAsync(T entity, CancellationToken ct = default); void Update(T entity); void Delete(T entity); Task<bool> ExistsAsync(long id, CancellationToken ct = default); IQueryable<T> Query(); }

  1. Unit of Work

public interface IUnitOfWork : IDisposable { // Repositories IRepository<Customer> Customers { get; } IRepository<Order> Orders { get; }

// Transaction management
Task&#x3C;int> SaveChangesAsync(CancellationToken ct = default);
Task BeginTransactionAsync(CancellationToken ct = default);
Task CommitAsync(CancellationToken ct = default);
Task RollbackAsync(CancellationToken ct = default);

}

  1. Generic Repository Implementation

public class Repository<T> : IRepository<T> where T : BaseEntity { protected readonly ApplicationDbContext _context; protected readonly DbSet<T> _dbSet;

public Repository(ApplicationDbContext context)
{
    _context = context;
    _dbSet = context.Set&#x3C;T>();
}

public virtual async Task&#x3C;T?> GetByIdAsync(long id, CancellationToken ct = default)
    => await _dbSet.FirstOrDefaultAsync(e => e.Id == id &#x26;&#x26; !e.IsDeleted, ct);

public virtual async Task&#x3C;IEnumerable&#x3C;T>> GetAllAsync(CancellationToken ct = default)
    => await _dbSet.Where(e => !e.IsDeleted).ToListAsync(ct);

public virtual async Task&#x3C;T> AddAsync(T entity, CancellationToken ct = default)
{
    await _dbSet.AddAsync(entity, ct);
    return entity;
}

public virtual void Update(T entity)
{
    entity.UpdatedAt = DateTime.UtcNow;
    _dbSet.Update(entity);
}

public virtual void Delete(T entity)
{
    entity.IsDeleted = true;
    entity.UpdatedAt = DateTime.UtcNow;
    Update(entity);
}

public virtual async Task&#x3C;bool> ExistsAsync(long id, CancellationToken ct = default)
    => await _dbSet.AnyAsync(e => e.Id == id &#x26;&#x26; !e.IsDeleted, ct);

public virtual IQueryable&#x3C;T> Query()
    => _dbSet.Where(e => !e.IsDeleted).AsQueryable();

}

  1. DbContext - Multi-Database Support

public class ApplicationDbContext : DbContext { private readonly ICurrentUserService _currentUser;

public ApplicationDbContext(
    DbContextOptions&#x3C;ApplicationDbContext> options,
    ICurrentUserService currentUser) : base(options)
{
    _currentUser = currentUser;
}

// DbSets
public DbSet&#x3C;Customer> Customers => Set&#x3C;Customer>();
public DbSet&#x3C;Order> Orders => Set&#x3C;Order>();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    
    // Apply all configurations from assembly
    modelBuilder.ApplyConfigurationsFromAssembly(
        typeof(ApplicationDbContext).Assembly);
    
    // Global query filter for soft delete
    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
        if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType))
        {
            modelBuilder.Entity(entityType.ClrType)
                .HasQueryFilter(
                    GenerateSoftDeleteFilter(entityType.ClrType));
        }
    }
}

public override async Task&#x3C;int> SaveChangesAsync(CancellationToken ct = default)
{
    foreach (var entry in ChangeTracker.Entries&#x3C;BaseEntity>())
    {
        switch (entry.State)
        {
            case EntityState.Added:
                entry.Entity.CreatedAt = DateTime.UtcNow;
                entry.Entity.CreatedBy = _currentUser.UserId;
                break;
            case EntityState.Modified:
                entry.Entity.UpdatedAt = DateTime.UtcNow;
                entry.Entity.UpdatedBy = _currentUser.UserId;
                break;
        }
    }
    return await base.SaveChangesAsync(ct);
}

private static LambdaExpression GenerateSoftDeleteFilter(Type type)
{
    var parameter = Expression.Parameter(type, "e");
    var property = Expression.Property(parameter, nameof(BaseEntity.IsDeleted));
    var condition = Expression.Equal(property, Expression.Constant(false));
    return Expression.Lambda(condition, parameter);
}

}

  1. Dependency Injection - Database Provider

// Infrastructure/DependencyInjection.cs public static class DependencyInjection { public static IServiceCollection AddInfrastructure( this IServiceCollection services, IConfiguration configuration) { var dbProvider = configuration.GetValue<string>("DatabaseProvider") ?? "PostgreSQL";

    services.AddDbContext&#x3C;ApplicationDbContext>(options =>
    {
        switch (dbProvider)
        {
            case "SqlServer":
                options.UseSqlServer(
                    configuration.GetConnectionString("DefaultConnection"),
                    sqlOptions =>
                    {
                        sqlOptions.MigrationsAssembly(
                            typeof(ApplicationDbContext).Assembly.FullName);
                        sqlOptions.EnableRetryOnFailure(
                            maxRetryCount: 3,
                            maxRetryDelay: TimeSpan.FromSeconds(30),
                            errorNumbersToAdd: null);
                    });
                break;
                
            case "PostgreSQL":
            default:
                options.UseNpgsql(
                    configuration.GetConnectionString("DefaultConnection"),
                    npgsqlOptions =>
                    {
                        npgsqlOptions.MigrationsAssembly(
                            typeof(ApplicationDbContext).Assembly.FullName);
                        npgsqlOptions.EnableRetryOnFailure(
                            maxRetryCount: 3,
                            maxRetryDelay: TimeSpan.FromSeconds(30),
                            errorCodesToAdd: null);
                    });
                break;
        }
    });

    // Repositories
    services.AddScoped(typeof(IRepository&#x3C;>), typeof(Repository&#x3C;>));
    services.AddScoped&#x3C;IUnitOfWork, UnitOfWork>();

    // Caching
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = configuration.GetConnectionString("Redis");
        options.InstanceName = "App_";
    });

    return services;
}

}

  1. appsettings.json - Database Configuration

{ "DatabaseProvider": "SqlServer", "ConnectionStrings": { "DefaultConnection": "Server=localhost;Database=MyApp;User Id=sa;Password=YourPassword;TrustServerCertificate=True;", "Redis": "localhost:6379" } }

{ "DatabaseProvider": "PostgreSQL", "ConnectionStrings": { "DefaultConnection": "Host=localhost;Database=myapp;Username=postgres;Password=YourPassword;", "Redis": "localhost:6379" } }

  1. .NET Aspire AppHost - Multi-Database

// AppHost/Program.cs var builder = DistributedApplication.CreateBuilder(args);

// Choose database provider var usePostgres = builder.Configuration.GetValue<bool>("UsePostgres", true);

IResourceBuilder<IResourceWithConnectionString> database;

if (usePostgres) { var postgres = builder.AddPostgres("postgres") .WithPgAdmin() .AddDatabase("appdb"); database = postgres; } else { var sqlserver = builder.AddSqlServer("sqlserver") .AddDatabase("appdb"); database = sqlserver; }

var redis = builder.AddRedis("redis") .WithRedisCommander();

// API Project var api = builder.AddProject<Projects.WebApi>("api") .WithReference(database) .WithReference(redis) .WithExternalHttpEndpoints();

builder.Build().Run();

🔧 Common Tasks

Migration Commands

Add migration

dotnet ef migrations add InitialCreate -p Infrastructure -s WebApi

Update database

dotnet ef database update -p Infrastructure -s WebApi

Generate SQL script

dotnet ef migrations script -p Infrastructure -s WebApi -o ./migrations.sql

Remove last migration

dotnet ef migrations remove -p Infrastructure -s WebApi

NuGet Packages

Common Packages

<!-- Domain/Application --> <PackageReference Include="MediatR" Version="12." /> <PackageReference Include="FluentValidation" Version="11." /> <PackageReference Include="AutoMapper" Version="13.*" />

<!-- Infrastructure - Core --> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8." /> <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8." />

<!-- WebApi --> <PackageReference Include="Swashbuckle.AspNetCore" Version="6." /> <PackageReference Include="Serilog.AspNetCore" Version="8." />

PostgreSQL Packages

<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.*" />

<!-- Aspire --> <PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.*" />

SQL Server Packages

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.*" />

<!-- Aspire --> <PackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" Version="8.*" />

📖 Reference Files

ดูไฟล์เพิ่มเติมใน:

  • references/ef-core-patterns.md

  • EF Core advanced patterns (PostgreSQL + SQL Server)

  • references/aspire-setup.md

  • .NET Aspire configuration

  • references/testing-patterns.md

  • Testing strategies

  • references/microsoft-learn-mcp.md

  • MCP usage guide

⚠️ Best Practices

  • Always use async/await - ไม่ block threads

  • Use CancellationToken - ทุก async method

  • Validate inputs - FluentValidation ก่อน process

  • Log appropriately - Structured logging with Serilog

  • Handle exceptions - Global exception handler + Result pattern

  • Write tests - Unit tests for business logic, Integration tests for APIs

  • Use DTOs - ไม่ expose Entities ตรงๆ

  • Soft delete - ใช้ IsDeleted flag แทน hard delete

  • Audit trail - CreatedAt, UpdatedAt, CreatedBy, UpdatedBy

  • Use transactions - สำหรับ operations ที่ต้อง atomic

  • Enable retry on failure - สำหรับ database connections

  • Use connection resiliency - ทั้ง PostgreSQL และ SQL Server

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

ui-mockup

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

system-design-doc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

Mac Control

Control Mac via mouse/keyboard automation using cliclick and AppleScript. Use for clicking UI elements, taking screenshots, getting window bounds, handling c...

Registry SourceRecently Updated