Wbudowane walidatory FluentValidation

W tym poście skoncentruję się na wbudowanych walidatorów dostępnych w FluentValidation, które są niezbędne do efektywnej walidacji danych w aplikacjach .net. Każdy walidator ma swoje specyficzne zastosowania i zachowuje się inaczej w zależności od typu danych, na których jest stosowany. Zrozumienie tych różnic jest kluczowe do tworzenia skutecznych i niezawodnych reguł walidacyjnych. W tym poście zostaną opisane wbudowane walidatory. Aby lepiej zrozumieć działanie walidatorów dołączone zostały także przykłady.

Walidator NotEmpty

Walidator jest używany do sprawdzania, czy przekazana właściwość nie jest pusta. Jego zachowanie zmienia się w zależności od typu danych, na których jest stosowany. Dla łańcuchów znaków (string), NotEmpty upewnia się, że łańcuch nie jest pusty, co oznacza, że nie zawiera wyłącznie białych znaków, nie jest pustym ciągiem lub nie jest null. Jest to ważne w aplikacjach, gdzie wymagane jest podanie niepustych danych tekstowych, jak imię użytkownika czy adres e-mail. W przypadku typów numerycznych, NotEmpty sprawdza, czy wartość nie jest równa zeru, co jest przydatne przy weryfikacji wartości, gdzie zero może być traktowane jako niepoprawna lub brakująca wartość. Dla typu DateTime, ten walidator upewnia się, że data nie jest równa domyślnej wartości (DateTime.MinValue), co może być użyteczne przy sprawdzaniu, czy data została prawidłowo ustawiona. Dla kolekcji walidator czy zawiera jakiś element.

Załóżmy, że mamy klasę reprezentującą formularz zamówienia, gdzie chcemy upewnić się, że niektóre pola nie są puste:

public class OrderForm
{
    public string ProductId { get; set; }
    public string CustomerName { get; set; }
}

public class OrderFormValidator : AbstractValidator<OrderForm>
{
    public OrderFormValidator()
    {
        RuleFor(order => order.ProductId).NotEmpty()
            .WithMessage("Product ID must not be empty.");

        RuleFor(order => order.CustomerName).NotEmpty()
            .WithMessage("Customer name must not be empty.");
    }
}

W tym przykładzie walidator NotEmpty jest używany do sprawdzenia, czy ProductId (identyfikator produktu) i CustomerName (nazwa klienta) nie są puste. Oznacza to, że te pola nie mogą być ani null, ani składać się z pustego ciągu znaków. Jeśli któreś z tych pól jest puste, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o konieczności wypełnienia tych pól.

Walidator Empty

Walidator Empty w FluentValidation działa w przeciwny sposób do walidatora NotEmpty. Jego zadaniem jest sprawdzenie, czy dane pole lub właściwość jest "puste". W kontekście różnych typów danych "brak wartości" może być interpretowana na różne sposoby. Dla typów referencyjnych, jak np. ciągi znaków (string), puste oznacza null lub ciąg o długości zero (""), dla kolekcji — brak elementów, a dla typów wartościowych, jak np. DateTime lub typów numerycznych, "brak wartości" zwykle odnosi się do ich domyślnych wartości (default(T)), czyli np. DateTime.MinValue dla DateTime lub 0 dla typów numerycznych.

Użycie walidatora Empty jest szczególnie przydatne w scenariuszach, gdzie oczekuje się, aby pewne pola nie były ustawione lub wypełnione — na przykład, gdy chcesz upewnić się, że użytkownik świadomie pozostawił pewne opcjonalne pola bez wprowadzonych wartości.

Załóżmy, że mamy model User, który ma opcjonalne pole MiddleName, i chcemy zweryfikować, że to pole jest puste:

public class User
{
    public string MiddleName { get; set; }
}

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(user => user.MiddleName)
            .Empty().WithMessage("The middle name must be empty.");
    }
}

W tym przypadku walidacja zakończy się powodzeniem tylko wtedy, gdy MiddleName jest null lub jest pustym ciągiem znaków. Jeśli MiddleName zawiera jakikolwiek tekst, walidacja nie przejdzie.

Dla typu DateTime, używanie Empty jest mniej typowym scenariuszem, ponieważ zazwyczaj nie oczekuje się, aby data była "pusta" (czyli miała wartość DateTime.MinValue). Jednakże, jeśli taka sytuacja miałaby miejsce, walidator Empty sprawdziłby, czy data jest równa DateTime.MinValue.

Walidator NotNull

Walidator NotNull jest prosty, ale niezbędny. Sprawdza, czy dana wartość nie jest null. Jest to fundamentalna walidacja dla wszystkich typów danych, zapewniająca, że pole nie zostało pominięte lub nie zostało błędnie zainicjowane. Jest to szczególnie ważne w środowiskach, gdzie null może powodować wyjątki lub błędy w logice aplikacji.

Załóżmy, że mamy klasę reprezentującą informacje kontaktowe klienta, gdzie chcemy upewnić się, że niektóre kluczowe pola nie są puste:

public class CustomerContactInfo
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class CustomerContactInfoValidator : AbstractValidator<CustomerContactInfo>
{
    public CustomerContactInfoValidator()
    {
        RuleFor(customer => customer.Name).NotNull()
            .WithMessage("Name cannot be null.");

        RuleFor(customer => customer.Email).NotNull()
            .WithMessage("Email cannot be null.");
    }
}

W tym przykładzie walidator NotNull jest używany do sprawdzenia, czy Name i Email nie są null. Jeśli jakiekolwiek z tych pól jest null, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o konieczności wypełnienia tych pól.

Walidator Length

Ten walidator jest używany do kontrolowania długości łańcuchów znaków. Pozwala określić minimalną i maksymalną dopuszczalną długość łańcucha, co jest kluczowe w wielu kontekstach, na przykład przy walidacji haseł, gdzie zbyt krótkie lub zbyt długie hasło może być niebezpieczne, lub niepraktyczne.

Załóżmy, że mamy klasę reprezentującą recenzję produktu, gdzie chcemy upewnić się, że treść recenzji nie jest zbyt krótka ani zbyt długa:

public class ProductReview
{
    public string ReviewContent { get; set; }
}

public class ProductReviewValidator : AbstractValidator<ProductReview>
{
    public ProductReviewValidator()
    {
        RuleFor(review => review.ReviewContent).Length(50, 500)
            .WithMessage("Review content must be between 50 and 500 characters.");
    }
}

W tym przykładzie walidator Length jest używany do sprawdzenia, czy ReviewContent (treść recenzji) ma długość pomiędzy 50 a 500 znaków. Jeśli długość treści recenzji jest krótsza niż 50 znaków lub dłuższa niż 500 znaków, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany, że jego recenzja nie spełnia wymogów długości.

Walidator MaxLength

Walidator MaxLength jest używany do określenia maksymalnej dopuszczalnej długości łańcucha znaków. Jest to szczególnie ważne w kontekstach, gdzie dane wejściowe muszą być ograniczone do pewnej długości, na przykład w przypadku komentarzy użytkowników, opisów produktów czy wpisów na blogach. Zapewnia to, że dane wejściowe nie przekroczą określonego limitu, co może być ważne zarówno z punktu widzenia użyteczności, jak i wydajności systemu.

Załóżmy, że mamy klasę reprezentującą komentarz użytkownika na stronie internetowej, gdzie chcemy ograniczyć długość tego komentarza:

public class Comment
{
    public string Content { get; set; }
}

public class CommentValidator : AbstractValidator<Comment>
{
    public CommentValidator()
    {
        RuleFor(comment => comment.Content).MaxLength(200)
            .WithMessage("Comment must be no more than 200 characters long.");
    }
}

W tym przykładzie walidator MaxLength jest używany do sprawdzenia, czy Content (treść komentarza) nie przekracza 200 znaków. Jeśli długość komentarza jest dłuższa niż 200 znaków, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany, że jego komentarz jest zbyt długi.

Walidator MinLength

Przeciwieństwem MaxLength jest walidator MinLength, który sprawdza, czy długość łańcucha znaków jest co najmniej równa określonej minimalnej wartości. Jest to użyteczne w sytuacjach, gdzie dane wejściowe muszą spełniać minimalne wymagania długości, na przykład minimalna długość hasła w systemach bezpieczeństwa. Pomaga to zapewnić, że dane wejściowe są wystarczająco obszerne, aby spełniać określone kryteria.

Załóżmy, że mamy klasę reprezentującą użytkownika systemu, gdzie chcemy upewnić się, że podane hasło spełnia minimalną długość:

public class User
{
    public string Password { get; set; }
}

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(user => user.Password).MinLength(8)
            .WithMessage("Password must be at least 8 characters long.");
    }
}

W tym przykładzie walidator MinLength jest używany do sprawdzenia, czy Password (hasło) ma co najmniej 8 znaków. Jeśli długość hasła jest krótsza niż 8 znaków, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o konieczności wprowadzenia dłuższego hasła.

Walidator LessThan

Walidator LessThan jest stosowany do porównywania wartości, aby upewnić się, że dana wartość jest mniejsza niż określony punkt odniesienia. Jest to przydatne w wielu scenariuszach, takich jak ograniczanie maksymalnej wartości dla pewnych typów danych numerycznych, na przykład wieku, ceny, czy innych wartości liczbowych. Pozwala to na łatwe ustanowienie górnych limitów i jest nieocenione w kontrolowaniu zakresów danych.

Załóżmy, że mamy klasę reprezentującą informacje o wieku osoby, gdzie chcemy upewnić się, że osoba jest młodsza niż określony wiek:

public class Person
{
    public int Age { get; set; }
}

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(person => person.Age).LessThan(18)
            .WithMessage("Person must be younger than 18 years old.");
    }
}

W tym przykładzie walidator LessThan jest używany do sprawdzenia, czy Age (wiek osoby) jest mniejszy niż 18. Jeśli wiek osoby jest równy 18 lub większy, walidacja zakończy się niepowodzeniem, a użytkownik otrzyma odpowiedni komunikat o błędzie.

Walidator GreaterThan

Walidator GreaterThan działa odwrotnie niż LessThan, sprawdzając, czy dana wartość jest większa niż określony punkt odniesienia. Jest to niezbędne w sytuacjach, gdzie wymagane jest ustalenie dolnego limitu, na przykład minimalna kwota zamówienia, minimalny wiek czy inne wartości numeryczne. Ten walidator pozwala na ustanowienie dolnych granic dla danych, co jest ważne w wielu aplikacjach biznesowych i systemach zarządzania danymi.

Załóżmy, że mamy klasę reprezentującą szczegóły zamówienia, gdzie chcemy upewnić się, że ilość zamówionych produktów jest większa niż minimalna ilość dozwolona:

public class OrderDetails
{
    public int MinimumOrderQuantity { get; set; } = 5;
    public int OrderedQuantity { get; set; }
}

public class OrderDetailsValidator : AbstractValidator<OrderDetails>
{
    public OrderDetailsValidator()
    {
        RuleFor(order => order.OrderedQuantity).GreaterThan(order => order.MinimumOrderQuantity)
            .WithMessage("Ordered quantity must be greater than the minimum order quantity.");
    }
}

W tym przykładzie walidator GreaterThan jest używany do sprawdzenia, czy OrderedQuantity (ilość zamówionych produktów) jest większa niż MinimumOrderQuantity (minimalna ilość do zamówienia). Jeśli ilość zamówionych produktów jest mniejsza lub równa minimalnej ilości, walidacja zakończy się niepowodzeniem, a użytkownik otrzyma odpowiedni komunikat o błędzie.

Walidator GreaterThanOrEqual

Ten walidator jest używane do porównywania wartości. GreaterThanOrEqual sprawdza, czy dana wartość jest większa lub równa określonemu punktowi odniesienia. Jest on niezwykle przydatny przy walidacji zakresów, na przykład przy ustalaniu minimalnej i maksymalnej ceny produktu, dat granicznych w rezerwacjach, czy ograniczeniach wiekowych.

Załóżmy, że mamy klasę reprezentującą szczegóły produktu, gdzie chcemy zapewnić, że ilość produktu w magazynie jest większa lub równa minimalnej ilości wymaganej do utrzymania zapasów:

public class Product
{
    public int StockQuantity { get; set; }
}

public class ProductValidator : AbstractValidator<Product>
{
    public ProductValidator()
    {
        RuleFor(product => product.StockQuantity).GreaterThanOrEqual(10)
            .WithMessage("Stock quantity must be greater than or equal to 10.");
    }
}

W tym przykładzie walidator GreaterThanOrEqual jest używany do sprawdzenia, czy StockQuantity (ilość produktu w magazynie) jest większa lub równa 10. Jeśli ilość w magazynie jest mniejsza niż 10, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany, że ilość produktu w magazynie jest niewystarczająca.

LessThanOrEqual

Ten walidator jest używany do porównywania wartości. Sprawdza, czy wartość danego pola lub właściwości jest mniejsza, lub równa określonej wartości. Ten walidator jest przydatny w różnych scenariuszach, na przykład przy walidacji dat, limitów, wieku, itp., gdzie potrzebujemy upewnić się, że wartość nie przekracza pewnego maksimum.

Załóżmy, że tworzymy system rezerwacji i chcemy upewnić się, że data rezerwacji jest nie późniejsza niż aktualna data. Użyjemy walidatora LessThanOrEqual do sprawdzenia, czy data rezerwacji spełnia to kryterium:

using FluentValidation;
using System;

public class Reservation
{
    public DateTime ReservationDate { get; set; }
}

public class ReservationValidator : AbstractValidator<Reservation>
{
    public ReservationValidator()
    {
        RuleFor(reservation => reservation.ReservationDate)
            .LessThanOrEqualTo(DateTime.Now).WithMessage("Data rezerwacji musi być dzisiejszą datą lub wcześniejszą.");
    }
}

W tym przykładzie ReservationValidator używa LessThanOrEqualTo do porównania ReservationDate z DateTime.Now, czyli bieżącą datą. Jeśli data rezerwacji jest w przyszłości względem bieżącej daty, walidacja zakończy się niepowodzeniem, wyświetlając odpowiedni komunikat.

Walidator Email

Walidator Email służy do sprawdzania, czy dany ciąg znaków jest poprawnym adresem e-mail. Jest to kluczowe w wielu aplikacjach, gdzie wymagany jest kontakt e-mailowy z użytkownikiem. Ten walidator sprawdza, czy format adresu e-mail jest zgodny ze standardowymi konwencjami, co pomaga w eliminacji błędów wprowadzania danych i ułatwia komunikację.

Załóżmy, że mamy klasę reprezentującą dane kontaktowe użytkownika, gdzie chcemy sprawdzić, czy podany adres e-mail jest prawidłowy:

public class ContactInfo
{
    public string Email { get; set; }
}

public class ContactInfoValidator : AbstractValidator<ContactInfo>
{
    public ContactInfoValidator()
    {
        RuleFor(contact => contact.Email).EmailAddress()
            .WithMessage("Invalid email address.");
    }
}

W tym przykładzie walidator EmailAddress jest używany do sprawdzenia, czy Email jest prawidłowym adresem e-mail. Jeśli adres e-mail nie jest prawidłowy, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o błędzie.

Walidator wyrażeń regularnych

Umożliwia definiowanie własnych reguł walidacji za pomocą wyrażeń regularnych. Jest to niezwykle potężne narzędzie, które pozwala na dostosowanie walidacji do specyficznych potrzeb aplikacji. Może być używany do sprawdzania, czy dane wejściowe odpowiadają określonemu wzorcowi, na przykład kodowi SKU w sklepie internetowym czy formatowi numeru telefonu.

Załóżmy, że mamy klasę reprezentującą dane użytkownika, gdzie chcemy sprawdzić, czy numer telefonu użytkownika pasuje do określonego formatu:

public class User
{
    public string PhoneNumber { get; set; }
}

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        var phoneRegex = @"^\+48\d{9}$";
        RuleFor(user => user.PhoneNumber).Matches(phoneRegex)
            .WithMessage("Phone number must be in the format +48123456789.");
    }
}

W tym przykładzie walidator Matches jest używany do sprawdzenia, czy PhoneNumber (numer telefonu) pasuje do wzorca polskiego numeru telefonu komórkowego. Jeśli numer telefonu nie pasuje do tego wzorca, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o niewłaściwym formacie numeru telefonu.

Walidator NotEqual

Walidator NotEqual jest używany do zapewnienia, że dwa pola lub wartości nie są sobie równe. Jest to szczególnie przydatne w sytuacjach, gdzie dwie wartości nie powinny być identyczne, na przykład w przypadku, gdy hasło i jego potwierdzenie muszą być różne od nazwy użytkownika. Ten walidator pozwala określić, że dana wartość nie może być równa określonemu punktowi odniesienia, co pomaga w zapobieganiu konfliktom danych lub w niektórych przypadkach zwiększa bezpieczeństwo.

Załóżmy, że mamy klasę reprezentującą formularz rejestracji użytkownika, gdzie chcemy zapewnić, że nazwa użytkownika i hasło nie są takie same:

public class UserRegistration
{
    public string Username { get; set; }
    public string Password { get; set; }
}

public class UserRegistrationValidator : AbstractValidator<UserRegistration>
{
    public UserRegistrationValidator()
    {
        RuleFor(registration => registration.Password).NotEqual(registration => registration.Username)
            .WithMessage("The password cannot be the same as the username.");
    }
}

W tym przykładzie walidator NotEqual jest używany do sprawdzenia, czy Password (hasło) nie jest takie samo jak Username (nazwa użytkownika). Jeśli hasło i nazwa użytkownika są identyczne, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany, że musi wybrać inne hasło.

Walidator Equal

Przeciwieństwem NotEqual jest walidator Equal, który sprawdza, czy dwie wartości są sobie równe. Jest to niezbędne w wielu scenariuszach, na przykład podczas weryfikacji, czy dwukrotnie wprowadzone hasło jest takie samo. Może być również używany do zapewnienia spójności między różnymi polami danych, na przykład w sytuacjach, gdzie data rozpoczęcia i zakończenia wydarzenia powinna być taka sama, lub przy potwierdzaniu adresu e-mail. Dzięki temu walidatorowi można łatwo weryfikować zgodność danych wejściowych, co jest kluczowe w utrzymaniu integralności danych.

Załóżmy, że mamy klasę reprezentującą formularz zmiany hasła, gdzie użytkownik musi wprowadzić nowe hasło oraz potwierdzić je:

public class PasswordChangeForm
{
    public string NewPassword { get; set; }
    public string ConfirmPassword { get; set; }
}

public class PasswordChangeFormValidator : AbstractValidator<PasswordChangeForm>
{
    public PasswordChangeFormValidator()
    {
        RuleFor(form => form.ConfirmPassword).Equal(form => form.NewPassword)
            .WithMessage("The confirmation password does not match the new password.");
    }
}

W tym przykładzie walidator Equal jest używany do sprawdzenia, czy wartość ConfirmPassword (potwierdzenie hasła) jest równa NewPassword (nowe hasło). Jeżeli hasła nie są identyczne, walidacja zakończy się niepowodzeniem, a użytkownik otrzyma informację o niezgodności haseł.

Walidator Must

Walidator Must pozwala na tworzenie niestandardowych reguł walidacji za pomocą wyrażeń lambda lub metod delegowanych. Jest to elastyczne narzędzie, które umożliwia definiowanie precyzyjnych warunków walidacyjnych, które mogą nie być bezpośrednio dostępne przez inne wbudowane walidatory. Daje to programistom swobodę w tworzeniu specyficznych, kontekstowych reguł walidacji, które najlepiej pasują do ich unikalnych wymagań aplikacji.

Załóżmy, że mamy klasę reprezentującą dane użytkownika, gdzie chcemy zastosować niestandardową walidację dla adresu e-mail:

public class User
{
    public string Email { get; set; }
}

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(user => user.Email).Must(BeAValidEmail)
            .WithMessage("Invalid email address.");
    }

    private bool BeAValidEmail(string email)
    {
        return email.EndsWith("@example.com");
    }
}

W tym przykładzie, Must jest używany w połączeniu z metodą BeAValidEmail, która definiuje niestandardową logikę walidacji dla adresu e-mail. Jeśli adres e-mail nie spełnia kryteriów określonych w BeAValidEmail (w tym przypadku musi kończyć się na "@example.com"), walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o nieprawidłowym adresie e-mail.

Walidator CreditCard

To specjalizowany walidator służący do sprawdzania, czy dany ciąg znaków jest poprawnym numerem karty kredytowej. Wykorzystuje różne algorytmy do weryfikacji formatu i poprawności numerów kart, co jest niezbędne w aplikacjach finansowych i e-commerce, aby upewnić się, że podane informacje dotyczące karty są prawidłowe przed przetworzeniem transakcji.

Załóżmy, że mamy klasę reprezentującą szczegóły płatności, gdzie chcemy upewnić się, że podany numer karty kredytowej jest prawidłowy:

public class PaymentDetails
{
    public string CreditCardNumber { get; set; }
}

public class PaymentDetailsValidator : AbstractValidator<PaymentDetails>
{
    public PaymentDetailsValidator()
    {
        RuleFor(payment => payment.CreditCardNumber).CreditCard()
            .WithMessage("Invalid credit card number.");
    }
}

W tym przykładzie walidator CreditCard jest używany do sprawdzenia, czy CreditCardNumber (numer karty kredytowej) jest prawidłowym numerem karty. Walidator ten korzysta z algorytmów, takich jak algorytm Luhna, aby zweryfikować poprawność numeru karty. Jeśli numer karty jest niepoprawny, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o błędzie.

Walidator IsInEnum

Ten walidator służy do weryfikacji, czy dana wartość należy do określonego zestawu wartości enum (wyliczeniowych). Jest to przydatne w sytuacjach, gdzie dane wejściowe użytkownika lub wartości z zewnętrznych źródeł danych muszą być ograniczone do określonego zestawu predefiniowanych opcji. Umożliwia to zapewnienie spójności danych i zapobiega błędom wynikającym z nieoczekiwanych wartości.

Załóżmy, że mamy typ wyliczeniowy reprezentujący status zamówienia oraz klasę, która używa tego typu wyliczeniowego:

public enum OrderStatus
{
    Pending,
    Shipped,
    Delivered,
    Cancelled
}

public class Order
{
    public OrderStatus Status { get; set; }
}

public class OrderValidator : AbstractValidator<Order>
{
    public OrderValidator()
    {
        RuleFor(order => order.Status).IsInEnum()
            .WithMessage("Invalid order status.");
    }
}

W tym przykładzie, IsInEnum jest używany do sprawdzenia, czy Status (status zamówienia) w klasie Order odpowiada jednej z wartości zdefiniowanych w OrderStatus. Jeśli podana wartość Status nie odpowiada żadnej z wartości w OrderStatus, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany o nieprawidłowym statusie zamówienia.

Walidator IsEnumName

Ten walidator różni się od zwykłego Enum walidatora, ponieważ zamiast sprawdzać wartości numeryczne wyliczenia (enum), skupia się na weryfikacji nazw tych wartości. Jest to szczególnie przydatne, gdy oczekuje się, że dane wejściowe będą nazwami elementów enum, a nie ich wartościami liczbowymi. Dzięki temu można łatwo sprawdzić, czy podany ciąg znaków odpowiada jednej z nazw w zdefiniowanym zestawie enum, co jest przydatne w przypadkach, gdzie dane wejściowe są dostarczane w formie tekstowej, na przykład w plikach konfiguracyjnych czy interfejsach użytkownika.

Zdefiniujmy typ wyliczeniowy, następnie klasę, która będzie zawierać właściwość z nazwą koloru. Potem definiujemy walidator dla tej klasy:

public enum Color
{
    Red,
    Green,
    Blue
}

public class Product
{
    public string ColorName { get; set; }
}

public class ProductValidator : AbstractValidator<Product>
{
    public ProductValidator()
    {
        RuleFor(product => product.ColorName).IsEnumName(typeof(Color), caseSensitive: false)
            .WithMessage("Color name must be a valid name in the Color enum.");
    }
}

W tym przykładzie IsEnumName sprawdza, czy wartość właściwości ColorName w klasie Product jest prawidłową nazwą w typie wyliczeniowym Color. Parametr caseSensitive: false oznacza, że walidacja nie uwzględnia wielkości liter, co jest przydatne, gdy dane wejściowe mogą być wprowadzane w różnych formatach. Dzięki temu, jeśli ColorName będzie np. "red", "Red" czy "RED", walidator uzna to za prawidłową wartość.

Walidator ExclusiveBetween

Jest używany do określenia, że dana wartość musi znajdować się w określonym zakresie, ale wykluczając graniczne wartości. Na przykład, jeśli określimy zakres od 1 do 10, walidator zaakceptuje wartości od 2 do 9. Jest to przydatne w sytuacjach, gdzie potrzebne jest wyraźne ograniczenie wartości do środkowego zakresu, na przykład przy definiowaniu poziomów akceptowalnego ryzyka lub innych wartości, które nie powinny osiągać swoich skrajnych punktów.

Załóżmy, że mamy klasę reprezentującą informacje o grupie wiekowej dla pewnej aktywności, gdzie chcemy upewnić się, że wiek uczestnika mieści się w określonym zakresie, ale nie obejmuje granicznych wartości:

public class ActivityParticipant
{
    public int Age { get; set; }
}

public class ActivityParticipantValidator : AbstractValidator<ActivityParticipant>
{
    public ActivityParticipantValidator()
    {
        RuleFor(participant => participant.Age).ExclusiveBetween(18, 60)
            .WithMessage("Age must be more than 18 and less than 60.");
    }
}

W tym przykładzie walidator ExclusiveBetween jest używany do sprawdzenia, czy Age (wiek uczestnika) jest większy niż 18, ale mniejszy niż 60. Oznacza to, że akceptowane są wieku od 19 do 59 lat. Jeśli wiek uczestnika jest równy 18 lub 60, lub przekracza te wartości, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany, że jego wiek nie mieści się w dozwolonym zakresie.

Walidator InclusiveBetween

Walidator działa podobnie do ExclusiveBetween, ale w tym przypadku graniczne wartości są włączone do akceptowalnego zakresu. Na przykład, dla zakresu od 1 do 10, walidator zaakceptuje wszystkie wartości od 1 do 10 włącznie. Jest to użyteczne w większości przypadków, gdzie wymagane są ograniczenia wartości, ale graniczne wartości są nadal akceptowalne, jak na przykład w przypadku ograniczeń wiekowych czy budżetowych.

Załóżmy, że mamy klasę reprezentującą informacje o produkcie, gdzie chcemy upewnić się, że cena produktu mieści się w akceptowalnym zakresie cenowym:

public class Product
{
    public decimal Price { get; set; }
}

public class ProductValidator : AbstractValidator<Product>
{
    public ProductValidator()
    {
        RuleFor(product => product.Price).InclusiveBetween(10.00m, 100.00m)
            .WithMessage("Price must be between 10.00 and 100.00, inclusive.");
    }
}

W tym przykładzie walidator InclusiveBetween jest używany do sprawdzenia, czy Price (cena produktu) mieści się w przedziale od 10.00 do 100.00, włączając te wartości graniczne. Jeśli cena produktu jest niższa niż 10.00 lub wyższa niż 100.00, walidacja zakończy się niepowodzeniem, a użytkownik zostanie poinformowany, że cena nie mieści się w dozwolonym zakresie.

Walidator PrecisionScale

Jest używany do walidacji liczb zmiennoprzecinkowych, gdzie istotna jest nie tylko ich wartość, ale również precyzja, czyli liczba cyfr po przecinku. Pozwala określić maksymalną liczbę cyfr całkowitych oraz maksymalną liczbę cyfr po przecinku. Jest to kluczowe w aplikacjach finansowych i inżynieryjnych, gdzie dokładność wartości numerycznych ma istotne znaczenie, na przykład przy określaniu stawek cenowych, pomiarów inżynieryjnych lub obliczeń naukowych. Umożliwia to precyzyjne zarządzanie danymi, zapewniając, że liczby nie przekraczają określonej precyzji i skali, co jest kluczowe dla zapewnienia dokładności i spójności danych.

Załóżmy, że mamy klasę reprezentującą finansowe transakcje, gdzie chcemy precyzyjnie kontrolować format kwoty transakcji:

public class FinancialTransaction
{
    public decimal TransactionAmount { get; set; }
}

public class FinancialTransactionValidator : AbstractValidator<FinancialTransaction>
{
    public FinancialTransactionValidator()
    {
        RuleFor(transaction => transaction.TransactionAmount).PrecisionScale(2, 5)
            .WithMessage("Transaction amount must have no more than 2 decimal places and total digits must not exceed 5.");
    }
}

W tym przykładzie walidator ScalePrecision jest używany do sprawdzenia, czy TransactionAmount (kwota transakcji) ma nie więcej niż dwie cyfry po przecinku i łącznie nie przekracza 5 cyfr. Oznacza to, że maksymalna wartość, jaką może przyjąć TransactionAmount to 999.99. Jest to przydatne w systemach finansowych, gdzie precyzja i format kwot są kluczowe ze względów księgowych i raportowych. Zapewnia to, że kwoty transakcji są zawsze reprezentowane z odpowiednią precyzją, co jest ważne dla dokładności obliczeń finansowych i unikania błędów zaokrągleń.

Każdy z tych walidatorów oferuje specyficzne możliwości i jest przystosowany do konkretnych wymagań walidacyjnych. Umożliwiają one tworzenie bardziej szczegółowych i skutecznych reguł walidacji, co przekłada się na większą niezawodność i dokładność aplikacji. Wykorzystanie tych narzędzi w praktyce programistycznej pozwala na lepszą kontrolę i zarządzanie danymi wejściowymi, co jest niezwykle ważne w wielu aspektach nowoczesnego oprogramowania.

Do zobaczenia w następnych postach!