FluentValidation เป็น .NET Library สำหรับสร้าง Validation Rules แบบ Strongly-typed Fluent Interface ใช้ Lambda Expressions ทำให้โค้ดอ่านง่าย รวม Validation Logic ไว้ที่เดียว (Separation of Concerns)
ทำไมต้องใช้ FluentValidation?
- แยก Validation Logic ออกจาก Domain Model (Clean Architecture)
- Fluent API: Chainable, Readable, Discoverable (IntelliSense)
- รองรับ Async, Conditional, Collection, Nested Validation
- Integrate กับ ASP.NET Core, MVC, WebAPI, Blazor, Minimal APIs ได้ง่าย
- Testing ง่าย (Validator เป็น Class ธรรมดา)
ติดตั้ง
dotnet add package FluentValidation
dotnet add package FluentValidation.AspNetCore // สำหรับ ASP.NET Core Integration
// หรือ
<PackageReference Include="FluentValidation" Version="11.9.0" />
ตัวอย่างพื้นฐาน
using FluentValidation;
using FluentValidation.Results;
using System;
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public decimal AccountBalance { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class PersonValidator : AbstractValidator<PersonModel>
{
public PersonValidator()
{
RuleFor(p => p.FirstName)
.Cascade(CascadeMode.StopOnFirstFailure) // หยุดที่ Error แรก
.NotEmpty().WithMessage("{PropertyName} ห้ามว่าง")
.Length(2, 50).WithMessage("{PropertyName} ความยาวต้อง 2-50 ตัวอักษร (ปัจจุบัน: {TotalLength})")
.Must(BeValidName).WithMessage("{PropertyName} ต้องเป็นตัวอักษรเท่านั้น");
RuleFor(p => p.LastName)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage("{PropertyName} ห้ามว่าง")
.Length(2, 50).WithMessage("{PropertyName} ความยาวต้อง 2-50 ตัวอักษร");
RuleFor(p => p.AccountBalance)
.GreaterThan(0).WithMessage("{PropertyName} ต้องมากกว่า 0");
RuleFor(p => p.DateOfBirth)
.LessThanOrEqualTo(DateTime.Today).WithMessage("{PropertyName} ต้องไม่เป็นอนาคต");
}
private bool BeValidName(string name)
{
if (string.IsNullOrWhiteSpace(name)) return false;
var cleaned = name.Replace(" ", "").Replace("-", "");
return cleaned.All(char.IsLetter);
}
}
static void Main()
{
var person = new PersonModel
{
FirstName = "", // Empty - Fail
LastName = "S", // Length 1 - Fail
AccountBalance = 100,
DateOfBirth = DateTime.Today
};
var validator = new PersonValidator();
ValidationResult result = validator.Validate(person);
if (!result.IsValid)
{
foreach (var error in result.Errors)
{
Console.WriteLine($"[{error.PropertyName}] {error.ErrorMessage}");
}
}
else
{
Console.WriteLine("Valid!");
}
}
ผลลัพธ์
[FirstName] FirstName ห้ามว่าง
[LastName] LastName ความยาวต้อง 2-50 ตัวอักษร (ปัจจุบัน: 1)
Placeholders ใน Error Message
| Placeholder | ค่า |
|---|---|
| {PropertyName} | ชื่อ Property (เช่น FirstName) |
| {PropertyValue} | ค่าที่ Validate |
| {TotalLength} | ความยาว String/Collection |
| {MinLength}, {MaxLength} | ค่าที่ตั้งใน Length/Min/Max |
| {ComparisonValue} | ค่าเปรียบเทียบ (GreaterThan, LessThan) |
Integration กับ ASP.NET Core
// Program.cs (.NET 6+)
builder.Services.AddControllers()
.AddFluentValidationAutoValidation() // Auto Validate + ModelState
.AddFluentValidationClientsideAdapters(); // Client-side (optional)
builder.Services.AddValidatorsFromAssemblyContaining<Program>(); // Scan Validators
สรุป
FluentValidation = Validation Library ที่ทรงพลัง อ่านง่าย ทดสอบง่าย รองรับ Scenario ซับซ้อน เหมาะสำหรับ Enterprise Applications