พื้นฐาน QueryHandler<TQuery, TResult>

By suparat · 08 Oct 2025


โอเคครับ 😎 เรามาไล่กัน ตั้งแต่พื้นฐานจนเข้าใจ QueryHandler<GetAppLogQuery, AppLogDto> ในสไตล์ CQRS + C# / .NET Core


1️⃣ พื้นฐาน QueryHandler<TQuery, TResult>

โดยทั่วไปใน CQRS เราจะแบ่งออกเป็น 2 ส่วนหลัก:

ส่วน ความหมาย
Query ใช้สำหรับ อ่านข้อมูล (Read)
Command ใช้สำหรับ เขียน/แก้ไขข้อมูล (Write)

QueryHandler<TQuery, TResult> เป็น class/interface ที่ทำหน้าที่:

“รับ Query แล้วคืนค่า TResult

โครงสร้างทั่วไป:

public interface IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
{
    Task<TResult> HandleAsync(TQuery query);
}
  • TQuery → query ที่ต้องการทำ

  • TResult → type ของข้อมูลที่จะคืน (เช่น DTO, List, primitive)


2️⃣ ตัวอย่าง Query: GetAppLogQuery

สมมติเราต้องการ query log ของแอป:

public class GetAppLogQuery : IQuery<AppLogDto>
{
    public int LogId { get; set; } // filter ตาม Id
}
  • Query นี้จะขอ log ตัวเดียว (AppLogDto)

  • Implement IQuery<AppLogDto> → บอกว่าผลลัพธ์คือ AppLogDto


3️⃣ DTO: AppLogDto

public class AppLogDto
{
    public int Id { get; set; }
    public string Message { get; set; }
    public string Level { get; set; } // Info, Error, Warning
    public DateTime CreatedAt { get; set; }
}

4️⃣ Query Handler: QueryHandler<GetAppLogQuery, AppLogDto>

นี่คือ class ที่ทำงานจริงกับ DB หรือ data source:

public class GetAppLogQueryHandler : IQueryHandler<GetAppLogQuery, AppLogDto>
{
    private readonly AppDbContext _dbContext;

    public GetAppLogQueryHandler(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<AppLogDto> HandleAsync(GetAppLogQuery query)
    {
        var log = await _dbContext.AppLogs
            .Where(x => x.Id == query.LogId)
            .Select(x => new AppLogDto
            {
                Id = x.Id,
                Message = x.Message,
                Level = x.Level,
                CreatedAt = x.CreatedAt
            })
            .FirstOrDefaultAsync();

        if (log == null)
            throw new KeyNotFoundException($"Log with Id {query.LogId} not found.");

        return log;
    }
}

อธิบายทีละขั้น:

  1. IQueryHandler<GetAppLogQuery, AppLogDto> → บอกว่า query นี้คืนค่าเป็น AppLogDto

  2. HandleAsync → method ที่รับ GetAppLogQuery และคืน AppLogDto

  3. ใช้ LINQ + EF Core query database

  4. map entity เป็น DTO (AppLogDto) ก่อน return

  5. ถ้าไม่เจอ log → throw exception


5️⃣ วิธีเรียกใช้งาน

สมมติมี Query Bus / Mediator:

var query = new GetAppLogQuery { LogId = 123 };
var log = await _queryBus.SendAsync(query);

Console.WriteLine($"{log.CreatedAt} [{log.Level}] {log.Message}");

6️⃣ สรุป Concept

สิ่งที่ทำ ความหมาย
GetAppLogQuery ตัว query request ที่บอกว่า “อยากได้ log ไหน”
AppLogDto DTO ที่ใช้ส่งออกข้อมูล log
QueryHandler<GetAppLogQuery, AppLogDto> class รับ query → query DB → map เป็น DTO → return
HandleAsync method หลักทำงานจริงกับ data source
CQRS Pattern แยก Read (Query) / Write (Command) → maintainable, testable

💡 Tip:

  • ถ้า query คืนหลายตัว → เปลี่ยน AppLogDto เป็น IEnumerable<AppLogDto>

  • Query Handler ทำหน้าที่ แค่ read ไม่ควรแก้ไข database


ถ้าคุณต้องการ ผมสามารถ วาด diagram flow ตั้งแต่ Controller → Query → QueryHandler → DbContext → AppLogDto ให้เห็นภาพชัด ๆ แบบ step by step ให้เลยครับ

คุณอยากให้ผมทำ diagram นั้นไหม?

QueryHandler
Web hosting by Somee.com