โอเคครับ 😎 เรามาไล่กัน ตั้งแต่พื้นฐานจนเข้าใจ 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;
}
}
อธิบายทีละขั้น:
-
IQueryHandler<GetAppLogQuery, AppLogDto>
→ บอกว่า query นี้คืนค่าเป็นAppLogDto
-
HandleAsync
→ method ที่รับGetAppLogQuery
และคืนAppLogDto
-
ใช้ LINQ + EF Core query database
-
map entity เป็น DTO (
AppLogDto
) ก่อน return -
ถ้าไม่เจอ 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 นั้นไหม?