Walidacja złożonych struktur danych w FluentValidation
W świecie programowania, szczególnie kiedy mamy do czynienia z aplikacjami obsługującymi złożone dane, często spotykamy się z wyzwaniem zweryfikowania poprawności tych danych. FluentValidation
, dzięki swojej elastyczności, stanowi potężne narzędzie, które pozwala nam na eleganckie zarządzanie procesem walidacji, nie tylko dla prostych typów danych, ale również złożonych struktur.
Złożone struktury danych w kontekście FluentValidation
odnoszą się do tych części modelu danych, które same są obiektami. Nie jest to rzadkość — modele często składają się z innych modeli, tworząc warstwę złożoności, która może być trudna do zarządzania bez odpowiednich narzędzi. Na przykład, model Order
może zawierać listę obiektów Product
, a każdy Product
może mieć swoje własne, specyficzne reguły walidacji.
Rozważmy sytuację, w której musimy zweryfikować zamówienie, które zawiera listę produktów. Każdy produkt ma swoją cenę i ilość, a zamówienie zawiera informacje o kliencie i datę zamówienia.
public class Order
{
public Customer Customer { get; set; }
public DateTime OrderDate { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
Chcemy upewnić się, że każde zamówienie zawiera datę oraz listę produktów, a każdy produkt ma nazwę, cenę większą od zera i ilość.
Tworzymy walidator dla Order
oraz Product
, wykorzystując FluentValidation
do zapewnienia zgodności danych z naszymi oczekiwaniami.
public class OrderValidator : AbstractValidator<Order>
{
public OrderValidator()
{
RuleFor(order => order.OrderDate).NotEmpty();
RuleForEach(order => order.Products).SetValidator(new ProductValidator());
}
}
public class ProductValidator : AbstractValidator<Product>
{
public ProductValidator()
{
RuleFor(product => product.Name).NotEmpty();
RuleFor(product => product.Price).GreaterThan(0);
RuleFor(product => product.Quantity).GreaterThan(0);
}
}
W OrderValidator
, sprawdzamy, czy data zamówienia nie jest pusta i ustawiamy walidator dla każdego produktu w zamówieniu. ProductValidator
zajmuje się weryfikacją, czy każdy produkt spełnia określone kryteria — ma nazwę, cenę większą od zera i odpowiednią ilość. Wykorzystując metodę SetValidator
w OrderValidator
, wprowadzamy złożone reguły walidacji dla elementów kolekcji, co pozwala na zastosowanie specyficznych walidatorów dla indywidualnych składników zamówienia. Więcej informacji na temat walidacji kolekcji znajdziesz w dalszej części tekstu.
Za pomocą FluentValidation
do sprawdzania poprawności złożonych struktur danych, jesteśmy w stanie efektywnie zarządzać nawet najbardziej zawiłymi strukturami, jednocześnie utrzymując przejrzystość i łatwość konserwacji naszego kodu.
Walidacja kolekcji
Walidacja kolekcji w FluentValidation
jest kluczowym elementem zapewnienia poprawności danych w aplikacjach, które operują na złożonych strukturach danych. FluentValidation
dostarcza mechanizmów pozwalających na efektywną i elastyczną walidację elementów kolekcji.
Załóżmy, że mamy kolekcję obiektów, które również wymagają walidacji. Bezpośrednie podejście może polegać na iterowaniu przez kolekcję i walidowaniu każdego elementu indywidualnie. Jednak FluentValidation
oferuje wyrafinowane i wydajne rozwiązanie za pomocą metody RuleForEach()
. Ta metoda pozwala na zastosowanie określonych reguł walidacji do każdego elementu w kolekcji, co sprawia, że proces jest nie tylko bardziej efektywny, ale również bardziej zorganizowany i łatwiejszy w utrzymaniu.
Jest to szczególnie przydatne, gdy chcemy upewnić się, że wszystkie elementy kolekcji spełniają określone kryteria. Oto przykład zastosowania RuleForEach()
:
public class Customer
{
public List<Order> Orders { get; set; } = new List<Order>();
}
public class Order
{
public decimal TotalAmount { get; set; }
}
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleForEach(customer => customer.Orders).Must(order => order.TotalAmount >= 100)
.WithMessage("Each order should have a value of at least 100.");
}
}
W tym przykładzie, każde zamówienie w kolekcji Orders
klienta jest walidowane indywidualnie, aby upewnić się, że jego wartość TotalAmount
jest co najmniej 100.
ChildRules
jest kolejną zaawansowaną funkcją FluentValidation
, która umożliwia definiowanie zasad walidacji dla zagnieżdżonych obiektów. Dzięki temu, zamiast tworzyć oddzielne walidatory dla każdego zagnieżdżonego typu i ręcznie zarządzać ich wywołaniami, można skorzystać z zintegrowanego podejścia, które FluentValidation
oferuje poprzez ChildRules
. To podejście nie tylko upraszcza proces walidacji, ale także zapewnia, że wszystkie reguły walidacji dla danego modelu są zawarte w jednym miejscu, co znacząco ułatwia zarządzanie i utrzymanie kodu.
Załóżmy, że mamy klasę Customer
zawierającą kolekcję Addresses
, a każdy adres ma swoje własne reguły walidacji.
public class Customer
{
public List<Address> Addresses { get; set; } = new List<Address>();
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleForEach(customer => customer.Addresses).ChildRules(address =>
{
address.RuleFor(a => a.Street).NotEmpty().WithMessage("The street cannot be empty.");
address.RuleFor(a => a.City).NotEmpty().WithMessage("The city cannot be empty.");
address.RuleFor(a => a.PostalCode).Matches(@"^\d{2}-\d{3}$")
.WithMessage("The postal code must be in the format 00-000.");
});
}
}
W tym przykładzie każdy adres w kolekcji Addresses
klienta jest poddawany walidacji z wykorzystaniem ChildRules
. Dzięki temu każdy adres musi mieć określoną ulicę, miasto i kod pocztowy zgodny z określonym wzorcem. Użycie ChildRules
sprawia, że walidacja jest bardziej modularna i łatwiejsza w utrzymaniu, szczególnie gdy struktury danych są złożone i zawierają wiele poziomów zagnieżdżenia.
Podsumowując, w naszej serii poświęconej FluentValidation
, omówiliśmy dwa kluczowe mechanizmy umożliwiające efektywną walidację kolekcji: RuleForEach()
i ChildRules
. Obydwa te narzędzia są niezwykle przydatne w zapewnianiu integralności danych w aplikacjach obsługujących złożone struktury danych.
RuleForEach()
jest idealne do stosowania jednolitych zasad walidacji na każdym elemencie kolekcji. Jak pokazano w przykładzie z CustomerValidator
, ta metoda pozwala na łatwą i skuteczną walidację wszystkich elementów w kolekcji, gwarantując spełnienie określonych wymagań, takich jak minimalna wartość zamówienia.
Z drugiej strony, ChildRules
umożliwia zaawansowaną walidację zagnieżdżonych obiektów wewnątrz kolekcji. Przykład z CustomerValidator
ilustruje, jak można walidować każdy adres w kolekcji Addresses
, stosując indywidualne reguły dla różnych atrybutów adresu. To podejście nie tylko zwiększa precyzję i skuteczność walidacji, ale również sprawia, że kod jest bardziej zorganizowany i łatwiejszy w utrzymaniu.
Podsumowując, RuleForEach()
i ChildRules
w FluentValidation
oferują programistom potężne narzędzia do tworzenia czystych, modularnych i wydajnych walidacji dla aplikacji zarządzających złożonymi danymi. Ich zastosowanie znacząco poprawia jakość kodu i zapewnia solidną obronę przed nieprawidłowymi danymi, co jest kluczowe dla stabilności i niezawodności każdej aplikacji.
Do zobaczenia w następnych postach!