SES-91 Utworzenie modelu zwrotek AP oraz zajmowanie się domyślnymi exceptionami #22
@ -74,6 +74,34 @@ namespace SessionCompanion.Extensions.EitherType
|
||||
return this.IsLeft ? leftFunc(this.Left) : rightFunc(this.Right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bazowa metoda dopasowania wzorów.
|
||||
/// Jeśli podana jest wartość lewa, to Match wykona lewą funkcję, w przeciwnym razie wykona funkcję prawą.
|
||||
/// </summary>
|
||||
/// <param name="leftFunc">Lewa funkcja </param>
|
||||
/// <param name="rightFunc">Prawa funkcja</param>
|
||||
public void Match(Action<TL> leftFunc, Action<TR> rightFunc)
|
||||
{
|
||||
if (leftFunc == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(leftFunc));
|
||||
}
|
||||
|
||||
if (rightFunc == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(rightFunc));
|
||||
}
|
||||
|
||||
if (this.IsLeft)
|
||||
{
|
||||
leftFunc(this.Left);
|
||||
}
|
||||
else
|
||||
{
|
||||
rightFunc(this.Right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Funkcja ustala czy uzyta została lewa zmienna, jesli tak to zwraca jej wartość inaczej zwraca defaultowy obiekt
|
||||
/// o typie takim jak lewa zmienna
|
||||
|
@ -0,0 +1,184 @@
|
||||
# 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
|
||||
```cs
|
||||
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:
|
||||
```cs
|
||||
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:
|
||||
```json
|
||||
{
|
||||
"left":null,
|
||||
"right":{
|
||||
"statusCode":400,
|
||||
"message":"Coś poszło nie tak"},
|
||||
"isLeft":false}
|
||||
```
|
||||
|
||||
Drugim sposobem jest zmiana działania serwisu:
|
||||
|
||||
```cs
|
||||
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 :
|
||||
```sh
|
||||
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:
|
||||
```cs
|
||||
UserViewModel user = new UserViewModel() { Id = 1, Nickname = "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.Nickname = "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.Nickname = "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.
|
||||
```cs
|
||||
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.
|
||||
```cs
|
||||
// ł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:
|
||||
```cs
|
||||
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:
|
||||
```cs
|
||||
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";
|
||||
}
|
||||
```
|
@ -5,8 +5,6 @@ using SessionCompanion.ViewModels.CharacterViewModels;
|
||||
|
||||
namespace SessionCompanion.Controllers
|
||||
{
|
||||
using SessionCompanion.Extensions.EitherType;
|
||||
|
||||
[Route("api/character")]
|
||||
[ApiController]
|
||||
public class CharacterController : Controller
|
||||
|
Loading…
Reference in New Issue
Block a user