SignalR Core + EF Core, как правильно обработать ошикби базы?

Допустим, что есть Real-time приложение, которое обращается к базе для CRUD операция из хаба, но в какой-то момент база падает, либо случается какая-то иная ошибка, как правильно осуществлять CRUD операции, чтобы на клиенте не сыпались Exception'ы, что "На сервере что-то не так"?
Оборачивать каждое обращение к базе в try-catch? Слишком напряжно, ведь методов хабов, как и самих хабов, может быть много, одинакового кода тоже будет много.
Пока придумал такой вариант:

  1. Общий класс, для отдачи ответов от методов хаба:
public class UResponse<T>
{
    /// <summary>
    /// Код ответа сервера.
    /// </summary>
    public UResponseCode ResponseCode { get; }
    
    /// <summary>
    /// Результат запроса.
    /// </summary>
    public T Response { get; set; }

    /// <summary>
    /// Конструктор.
    /// </summary>
    public UResponse(UResponseCode responseCode, T response)
    {
        ResponseCode = responseCode;
        Response = response;
    }

    public static implicit operator bool(UResponse response) => response?.ResponseCode == UResponseCode.Ok;
}

public enum UResponseCode
{
    /// <summary>
    /// Всё ок.
    /// </summary>
    Ok,

    /// <summary>
    /// Ошибка сервера.
    /// </summary>
    ServerError,

    /// <summary>
    /// Ошибка базы данных.
    /// </summary>
    DbError
}
  1. Метод-расширение для DbContext'a:
public static async Task<UResponse<TResponse>> ContextSafeFunc<TResponse>(this DbContext context, Func<Task<TResponse>> func, TResponse defaultValue = default)
{
    try
    {
        var res = await func().ConfigureAwait(false);

        return new UResponse<TResponse>(res, UResponseCode.Ok);
    }
    catch
    {
        return new UResponse<TResponse>(defaultValue, UResponseCode.DbError);
    }
}
  1. Работа с базой данных через данное расширение в методе хаба:
public Task<UResponse<IEnumerable<GetOrderDto>>> GetOrdersAsync(int offset)
{
    return context.ContextSafeFunc(async () =>
    {
        var res = await context.Orders.AsNoTracking()
                                      .OrderByDescending(x => x.Id)
                                      .Skip(offset)
                                      .Take(20)
                                      .ToListAsync()
                                      .ConfigureAwait(false);

        return mapper.Map<IEnumerable<GetOrderDto>>(res);
    });
}

На сколько данный метод является костылем? Как его улучшить, чтобы при возникшей ошибке на клиенте ничего не ломалось, не вызывались Exception'ы, а только отдавался унифицированный ответ с кодами ошибок? (Т.е. при успешном запросе к базе данных ответ был => UResponseCode = UResponseCode.Ok, Response = результат запроса к базе; при неудачном запросе ответ был => UResponseCode = UResponseCode.DbError, Response = defaultValue (обычно default(T)))


Ответы (0 шт):