Przejdź do treści

Guard w .net

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.

Tutaj znajdziesz więcej dokumentacji do Guard.