С# Обернуть вызовы различных методов класса в единый try-catch
Абстрактный пример. Есть клиент со следующим интерфейсом:
public interface IUserClient
{
Task<IUser> GetUserByIdAsync(int id);
Task<IUser> FindUserByFirstNameAndLastNameAsync(string firstName, string lastName);
Task<IUser> CreateUserAsync(string firstName, string lastName, string phone);
}
public interface IUser
{
int Id { get; }
string FirstName { get; set; }
string LastName { get; set; }
string Phone { get; set; }
}
Мне нужно написать обертку над этим клиентом, который будет пытаться перелогиниться, если метод клиента выбросит определенный Exception. Пока я реализовал это примерно так:
public class UserClientWrapper : IUserClient
{
private readonly IUserClient _userClient;
public UserClientWrapper(IUserClient userClient)
{
_userClient = userClient;
}
public async Task<IUser> GetUserByIdAsync(int id)
{
try
{
return await _userClient.GetUserByIdAsync(id);
}
catch (UnauthorizedAccessException)
{
// Пытаемся перелогиниться
try
{
return await _userClient.GetUserByIdAsync(id);
}
catch (UnauthorizedAccessException)
{
// Пишем в лог, что что-то не так, и пробрасываем выше этот exception
throw;
}
}
}
public async Task<IUser> FindUserByFirstNameAndLastNameAsync(string firstName, string lastName)
{
try
{
return await _userClient.FindUserByFirstNameAndLastNameAsync(firstName, lastName);
}
catch (UnauthorizedAccessException)
{
// Пытаемся перелогиниться
try
{
return await _userClient.FindUserByFirstNameAndLastNameAsync(firstName, lastName);
}
catch (UnauthorizedAccessException)
{
// Пишем в лог, что что-то не так, и пробрасываем выше этот exception
throw;
}
}
}
public async Task<IUser> CreateUserAsync(string firstName, string lastName, string phone)
{
// и тут тоже самое
}
}
Но вот смущает то, что я в каждом методе обязан дублировать код try-catch. Хотелось бы эту логику поместить в отдельный метод, но я не знаю, возможно ли это вообще. Поэтому возникает вопрос: как избежать дублирования кода в данном случае?
P.S. Важно! Методы клиента разные, с разными сигнатурами и возвращаемыми типами, но все они асинхронные.
Ответы (1 шт):
Нашел ответ на англоязычном StackOverflow. Вынес логику обработки исключений в метод, в который передаю делегат Func<Task<T>>
public class UserClientWrapper : IUserClient
{
private readonly IUserClient _userClient;
public UserClientWrapper(IUserClient userClient)
{
_userClient = userClient;
}
public Task<IUser> GetUserByIdAsync(int id)
{
return WrapClientCallAsync(() => _userClient.GetUserByIdAsync(id));
}
public Task<IUser> FindUserByFirstNameAndLastNameAsync(string firstName, string lastName)
{
return WrapClientCallAsync(() => _userClient.FindUserByFirstNameAndLastNameAsync(firstName, lastName));
}
public Task<IUser> CreateUserAsync(string firstName, string lastName, string phone)
{
return WrapClientCallAsync(() => _userClient.CreateUserAsync(firstName, lastName, phone));
}
private async Task<T> WrapClientCallAsync<T>(Func<Task<T>> function)
{
try
{
return await function();
}
catch (UnauthorizedAccessException)
{
// Пытаемся перелогиниться
try
{
return await function();
}
catch (UnauthorizedAccessException)
{
// Пишем в лог, что что-то не так, и пробрасываем выше этот exception
throw;
}
}
}
}