Przeglądając posty na blogach natrafiłem na bibliotekę, która pozwala nam walidować nasze modele/parametry w metodach/itp. Czyli pozwala nam zaimplementować guard w naszych aplikacjach 🙂 Ta biblioteka to Guard.
Co to jest guard
Jeśli nie spotkałeś się jeszcze z guard lub gardą 😀 to już tłumaczę czym jest. Jak sama nazwa wskazuje jest to strażnik. No i moim zdaniem nie ma co doszukiwać się jakiś głębszych funkcji. A więc guard sprawdza coś zanim gdzieś nas wpuści 😀 To sprawdzanie to najczęściej jakaś walidacja modelu, tego czy jesteś zalogowany, czy masz wystarczające role itp. itd. Lepszy opis znajdziesz np. tutaj
Przykłady
W poniższym przykładzie walidujemy nasz model w metodzie AddCar.
using Dawn;
public record Car
{
public string Model { get; set; }
public string Brand { get; set; }
public int Wheels { get; set; }
}
public record Truck : Car
{
public double Tonnage { get; set; }
}
public static void AddCar(Car car)
{
Guard.Argument(car.Model, nameof(car.Model)).NotNull().NotEmpty().MaxLength(50);
Guard.Argument(car.Wheels, nameof(car.Model)).Equal(4);
}
var car = new Car { Model = "Q5", Brand = "Audi", Wheels = 4 };
AddCar(car);
var truck = new Truck { Model = "Q5", Brand = "Audi", Wheels = 8 };
AddCar(truck);
Jeśli przekażemy truck do AddCar to dostaniemy błąd:
Error: System.ArgumentException: Model must be 4. (Parameter 'Model') at Dawn.Guard.Equal[T](ArgumentInfo`1& argument, T& other, IEqualityComparer`1 comparer, Func`3 message) at Submission#27.AddCar(Car car) at Submission#27.<>d__0.MoveNext() --- End of stack trace from previous location --- at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)
W tym przykładzie robimy to samo ale w konstruktorze. To czy to dobry czy zły pomysł to pozostawiam już Tobie. Moim zdaniem taki sobie, bo „uzależniamy” naszą klasę domenową od konkretnej biblioteki i mi to tak sobie się podoba. Ale mogę się mylić 🙂
using Dawn;
public class Currency
{
public string Code { get; set; }
public string Name { get; set; }
public Currency(string code, string name)
{
this.Code = Guard.Argument(code, nameof(code)).NotNull().NotEmpty().Length(3);
this.Name = Guard.Argument(name, nameof(name)).NotNull().NotEmpty().LengthInRange(1, 50);
}
}
var pln = new Currency("PLN", "Złoty");
var zl = new Currency("ZL", "Złoty");
Przy tworzeniu zl dostaniemy taki błąd:
Error: System.ArgumentException: code must consist of 3 characters. (Parameter 'code') at Dawn.Guard.Length(ArgumentInfo`1& argument, Int32 length, Func`3 message) at Submission#28.Currency..ctor(String code, String name) at Submission#28.<>d__0.MoveNext() --- End of stack trace from previous location --- at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)
Podsumowanie
Moim zdaniem Guard jest dość ciekawa ale też ostatni release był prawie 2 lata temu 🤷♂️ Ja w swoich projektach zwykle używam Fluent validation i pewnie w komercyjnych projektach zostanę przy niej. Ale jak dla mnie to brak jakiejkolwiek wstępnej konfiguracji w przypadku Guard jest super i w jakiś mniejszych aplikacjach być może jej użyję.
Repozytorium z przykładami znajdziesz tutaj.