Umgang mit generischen Typänderungen beim Aufsteigen im Aufrufstapel bei Verwendung des ErgebnismustersC#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Umgang mit generischen Typänderungen beim Aufsteigen im Aufrufstapel bei Verwendung des Ergebnismusters

Post by Anonymous »

Ich lerne verschiedene Möglichkeiten zur Fehlerbehandlung in .NET-REST-APIs kennen. Ich habe gesehen, dass das Ergebnismuster erwähnt wurde, und versuche jetzt, es in einem persönlichen Projekt zu implementieren.
Allerdings habe ich das Gefühl, dass mir etwas fehlt, da es sich sehr umständlich anfühlt, es zu verwenden.
Ich versuche es anhand eines Beispiels zu erklären: Hier in meinem Repository verwende ich eine einfache In-Memory-Liste, die meine Benutzer speichert:

Code: Select all

public ResultAdd(User user)
{
var userExists = CheckIfUserNameExists(user.GetUserName());

if (userExists)
{
var error = UserErrors.UserAlreadyExistsError(user.GetUserName());
return Result.Failure(error);
}

_users.Add(user);

return Result.Success(user.GetId());
}
Wenn der Benutzername existiert, dann gebe ich ein Fehlerergebnis zurück und einen Fehler zurück. Wenn ein Benutzer erfolgreich zur Liste hinzugefügt wurde, möchte ich die Benutzer-ID an den Anrufer zurückgeben, damit er den Benutzer später anhand der ID abrufen kann.
Daher lasse ich mein Ergebnis einen generischen Typ akzeptieren, der der Typ des Werts ist, den es an den Anrufer zurückgibt:

Code: Select all

public class Result
{
private Result(T value, bool isSuccess, Error error)
{
if(isSuccess && error != Error.None || !isSuccess && error == Error.None)
{
throw new ArgumentException("Invalid error", nameof(error));
}

IsSuccess = isSuccess;
Value = value;
Error = error;
}

public T Value { get;  }
public bool IsSuccess { get; }
public Error Error { get; }

public static Result Success(T value) => new(value, true, Error.None);
public static Result Failure(Error error) => new(default!, false, error);
}
Ein Teil der Peinlichkeit besteht darin, dass ich in der Serviceschicht einen anderen Typ Result zurückgeben möchte, wobei SignUpResponse mein DTO ist.

Code: Select all

public Result SignUp(SignUpRequest request)
{
var user = new User(
request.FirstName,
request.LastName,
request.UserName,
request.Password
);

var idResult = _userRepository.Add(user);

return idResult.Map(id => new SignUpResponse(id));
}
Um dieses Problem zu umgehen, habe ich diese Kartenerweiterung von result erstellt:

Code: Select all

public static Result Map(this Result result, Func mapper)
{
return result.IsSuccess
? Result.Success(mapper(result.Value))
: Result.Failure(result.Error);
}
Dinge, die mir an diesem Ansatz nicht gefallen:
  • Die Map-Funktion verbirgt die Tatsache, dass sie sowohl das Erfolgs-- als auch das Fehlerszenario behandelt, sodass der Code weniger lesbar ist
  • Bei Fehlerergebnissen muss ich immer noch auf Typ T verzichten, auch wenn er nicht verwendet wird, was sich komisch anfühlt
  • Ich kann mir vorstellen, dass es mit der Skalierung noch umständlicher wird, da ich die Kartierung möglicherweise in mehreren Ebenen fortsetzen muss
Meine Frage ist: Wenn ich diesen Ansatz wählen würde, was denken die Leute darüber, diese Dinge zu umgehen? Ich bin mir nicht sicher, ob ich das Muster falsch implementiert habe, ob dadurch ein größeres Missverständnis über das Software-Design ans Licht gekommen ist oder ob dies ein bekannter Kompromiss ist.
Jeder Tipp, wie man einen funktionalen Ansatz zur Fehlerbehandlung verwendet und es gut macht, wäre sehr dankbar!

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post