Как обрабатывать изменение универсального типа при перемещении вверх по стеку вызовов при использовании шаблона результаC#

Место общения программистов C#
Ответить
Anonymous
 Как обрабатывать изменение универсального типа при перемещении вверх по стеку вызовов при использовании шаблона результа

Сообщение Anonymous »

Я изучаю различные способы обработки ошибок в API-интерфейсах .NET REST. Я увидел упомянутый шаблон результатов и теперь пытаюсь реализовать его в личном проекте.
Однако я не могу не чувствовать, что что-то упускаю, поскольку его очень неудобно использовать.
Я попытаюсь объяснить на примере: здесь, в моем репозитории, я использую простой список в памяти, в котором хранятся мои пользователи:

Код: Выделить всё

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());
}
Если имя пользователя существует, я возвращаю результат с ошибкой и возвращаю ошибку. Если пользователь успешно добавлен в список, я хочу вернуть идентификатор пользователя вызывающему объекту, чтобы он мог позже получить пользователя по идентификатору.
Поэтому я заставляю свой результат принимать общий тип, который будет типом значения, которое он возвращает вызывающему объекту:

Код: Выделить всё

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);
}
Часть неудобства заключается в том, что на уровне обслуживания я хочу вернуть Result другого типа, где SignUpResponse является моим DTO.

Код: Выделить всё

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));
}
Чтобы обойти эту проблему, я создал следующее расширение карты результатов:

Код: Выделить всё

public static Result Map(this Result result, Func mapper)
{
return result.IsSuccess
? Result.Success(mapper(result.Value))
: Result.Failure(result.Error);
}
Что мне не нравится в этом подходе:
  • Функция Map скрывает тот факт, что она обрабатывает сценарии успеха и неудачи, поэтому код становится менее читаемым
  • В случае неудачи мне все равно приходится пропускать тип T, даже если он не используется, что кажется странным
  • Я могу себе представить, что по мере масштабирования это станет еще более неудобным, поскольку мне, возможно, придется продолжать картографировать в нескольких слоях.
Мой вопрос: если бы я воспользовался этим подходом, каковы были бы мысли людей о том, как обойти эти проблемы? Не уверен, что я неправильно реализовал шаблон, или это выявило у меня какое-то большее непонимание дизайна программного обеспечения, или это хорошо известный компромисс.
Будем очень признательны за любые советы о том, как использовать функциональный подход к обработке ошибок и делать это хорошо!

Подробнее здесь: https://stackoverflow.com/questions/798 ... -using-the
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»