session-companion/SessionCompanion/SessionCompanion.Extensions/EitherType/EitherDocumentation.md

4.9 KiB

Either

Jak używać

Either umożliwa nam zwrócenie z metody bądź nawet kontrolera, dwóch możliwych wyników. Dla ułatwienia zrozumienai poniżej podaję przykłady stosowania.

Jako typ zmiennej zwracanej z api: W tym przypadku wskazujemy, że nasze api zwróci albo CharacterViewModel albo ErrorResponse

public async Task<Either<CharacterViewModel, ErrorResponse>> Get(int id)

Aby zwrócić ten typ mamy dwie możliwości. Możemy wywołać metodę z serisu, która ma nam zwrócić CharacterViewModel. Następnie sprawdzić czy otrzymana wartość CharacerViewModel zawiera jakiekolwiek dane i odpowiednio, jeśli tak to zwracamy otrzymany viewmodel a jeśli nie tworzymy ErrorResponse i zwracamy wynik:

public async Task<Either<CharacterViewModel, ErrorResponse>> Get(int id)
        {
            Either<CharacterViewModel, ErrorResponse> result = await _service.Get(id);

            if (result.Left != null)
            {
                return result;
            }
            else
            {
                result = new ErrorResponse { StatusCode = 400, Message = "Coś poszło nie tak" };

                return result;
            }
        }

W przypadku jesli serwis nie znajdzie wartości otrzymamy:

{
"left":null,
"right":{
    "statusCode":400,
    "message":"Coś poszło nie tak"},
"isLeft":false}

Drugim sposobem jest zmiana działania serwisu:

public async Task<Either<CharacterViewModel, ErrorResponse>> GetCharacter(int id)
        {
            var repoResult = await this.Repository.Get(id);
            if (repoResult != null)
            {
                return Mapper.Map<CharacterViewModel>(repoResult);
            }
            else
            {
                return new ErrorResponse()
                           {
                               StatusCode = 404,
                               Message = "Coś poszło nie tak"
                           };
            }
        }

Wtedy kontroler wygląda tak :

public async Task<Either<CharacterViewModel, ErrorResponse>> Get(int id)
        {
            return await this._service.GetCharacter(id);
        }

Przypisanie nowej wartości

Jeśli z jakiegośpowodu będziemy chcieli zmienić wartość obiektu Either, musimy dokonać tego w taki oto sposób:

UserViewModel user = new UserViewModel() { Id = 1, Username = "Testowy", Password = "Secret" };
Either<UserViewModel, string> test = "asd";

// zmieniamy wartość Either
test = user;

// Warto zauważyć, że możemy zmienić wartość obiektu znajdujacego się wewnątrz
// którejś ze zmiennej o ile jest on publiczny
test.Left.Username = "test";

// Jednak jeśli dokonamy tego po zmianie wartości obietu na inny pomimo tego, że 
// dla kodu jest to ok, to nie zadziała to poprawnie, ponieważ wartość starego obiektu
// jest zmieniana na null(dla boola na false)
test = "asd";
test.Left.Username = "błąd";

LeftOrDefault() oraz RightOrDefault()

Zwracają one wartość wybranej zmiennej bądź jej wartość defaultową. Dla wiekszości obiektów jest to null, wyjatkiem jest tutaj bool, który zwróci nam false.

var test = (Either<bool, string>)true;

var a = test.LeftOrDefault(); // zwróci nam true
 var b = test.RightOrDefault(); // zwróci nam null

test = "asd";

var c = test.LeftOrDefault(); // zwróci nam false
var d = test.RightOrDefault(); // zwróci nam "asd"

Match

Funkcja ta pozwala nam na odpalenie odpowiedniej metody w zależności od tego jako typ zmiennej jest aktualnie przechowywany w obiekcie Either. Niestety ograniczeniem jest to, ze obie funkcje musza docelowo zwrócić ten sam typ.

// ładujemy userviewmodel do obiektu Either i wywoływamy matcha pdając dwie funkcje
test = user;
// zwwrócny zostanie string "Test1"
test.Match(this.Test1, this.Test2);

// ładujemy stringa do obiektu Either i wywoływamy matcha pdając dwie funkcje
test = "asd";
// zwwrócny zostanie string "Test2"
test.Match(this.Test1, this.Test2);

public string Test1(UserViewModel viewModel)
{
    return "Test1";
}

 public string Test2(string text)
{
    return "Test2";
}

Warto zauważyć, że możemy wykorzystać tutaj lambde by rozszerzyć nasze możliwości:

test = "asd";

test.Match(
    Test1,
    second =>
        {
            int z = Test2(second);
            // tutaj możemy robić już co nam się tylko podoba
            // pamiętać należy ze finalnie musimy coś zwrócić
            // chyba, że nasze obie funkcje są typu void
            return z.ToString();
});

public string Test1(UserViewModel viewModel)
{
    return "Test1";
}

 public int Test2(string text)
{
    return 12;
}

Możemy też podać jedna funkcję typu void a drugą typu innego np. string albo int:

y = "";
test = "asd";

test.Match(
    Test1,
    second =>
        {
            var x = this.Test2(second);
            //jednak nie ma możliwości zwrócenia wartości
            // możemy za to zwrócić wyniki do zewnętrznej zmiennej
            y = x;
});

public void Test1(UserViewModel viewModel)
{
    //...coś robi
}

 public string Test2(string text)
{
    return "Test2";
}