Usar servicios y manejar sus resultados en Xamarin

Continuemos discutiendo las diversas tareas para Xamarin que tenemos que enfrentar regularmente, ya que los artículos sobre este tema no aparecen con mucha frecuencia. Este artículo será más útil para los desarrolladores novatos, pero los más experimentados también pueden ser interesantes.





Entonces, ¿por qué tal tema? Habiendo trabajado en diferentes proyectos, me sorprendí pensando que en cada uno de ellos se utilizan enfoques completamente diferentes para llamar a servicios y manejar errores. En este artículo, decidí recopilar todo lo que tuve que enfrentar, mostrar qué opciones hay y pensar en los pros y los contras de cada uno.





Consideremos diferentes enfoques con un ejemplo simple, donde tendremos una solicitud de una lista de modelos estándar del backend y luego los convertiremos en una lista de modelos de vista para mostrar la colección. No consideraremos aquí la parte de la interfaz de usuario, nos limitaremos solo al trabajo de los servicios y al modelo de vista.





Entonces, nuestro modelo simple, que solicitaremos al backend:





public class ItemModel
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string CreatedDate { get; set; }
    public string ModifiedDate { get; set; }
}
      
      



Y el modelo de vista correspondiente, en el que solo necesitamos 2 campos del modelo para su posterior visualización:





public class ItemViewModel : ViewModel
{
    public ItemViewModel(ItemModel item)
    {
        Title = item.Title;
        Description = item.Description;
    }

    public string Title { get; }

    public string Description { get; }
}
      
      



, -:





public interface IDataService
{
    Task<IEnumerable<ItemModel>> LoadItemsAsync();
}
      
      



RequestService, :





public interface IRequestService
{
    Task<T> GetAsync<T>(string url);
}
      
      



- ItemViewModel. - ObservableCollection MvvmCross, AddRange()



, UI.





public class MainViewModel : ViewModel
{
    private readonly IDataService _dataService;

    public MainViewModel(IDataService dataService)
    {
        _dataService = dataService;
    }

    public MvxObservableCollection<ItemViewModel> Items { get; set; } = new();
}
      
      



- MainViewModel . -.





, :





public async Task Initialize()
{
    var result = await _dataService.LoadItemsAsync();
    var itemModels = result.ToList();
    if (itemModels.Any())
    {
        var items = new List<ItemViewModel>();
        itemModels.ForEach(m => items.Add(new ItemViewModel(m)));
        Items.AddRange(items);
    }
}
      
      



LoadItemsAsync()



- :





public Task<IEnumerable<ItemModel>> LoadItemsAsync()
{
    var url = "https://customapiservice/v1/items";
    return _requestService.GetAsync<IEnumerable<ItemModel>>(url);
}
      
      



, , , - 400- 500- . , crash. , , :





public async Task Initialize()
{
    try
    {
        var result = await _dataService.LoadItemsAsync();
        var itemModels = result.ToList();
        if (itemModels.Any())
        {
            var items = new List<ItemViewModel>();
            itemModels.ForEach(m => items.Add(new ItemViewModel(m)));
            Items.AddRange(items);
        }
    }
    catch (Exception e)
    {
        //      
    }
}
      
      



, . .





:





  • , , -





  • , ,





:





  • try-catch, -





  • , -





, - try-catch, . LoadItemsAsync()



:





public async Task<IEnumerable<ItemModel>> LoadItemsAsync()
{
    IEnumerable<ItemModel> result;
    try
    {
        var url = "https://customapiservice/v1/items";
        result = await _requestService.GetAsync<IEnumerable<ItemModel>>(url);
    }
    catch (Exception e)
    {
        result = new List<ItemModel>();
    }

    return result;
}
      
      



- , , - . , - . - null, , . , api null, .





:





  • - , breakpoints





  • try-catch, -





:





  • try-catch





  • , exception,





. , :





public class ServiceResult<TResult, TError>
{

    public ServiceResult(TResult result)
    {
        IsSuccessful = true;
        Result = result;
    }

    public ServiceResult(TError error)
    {
        IsSuccessful = false;
        Error = error;
    }

    public bool IsSuccessful { get; }
    public TResult Result { get; }
    public TError Error { get; }
}
      
      



LoadItemsAsync()



, :





public interface IDataService
{
    Task<ServiceResult<IEnumerable<ItemModel>, Exception>> LoadItemsAsync();
}
      
      



public async Task<ServiceResult<IEnumerable<ItemModel>, Exception>> LoadItemsAsync()
{
    try
    {
        var url = "https://customapiservice/v1/items";
        var result = await _requestService.GetAsync<IEnumerable<ItemModel>>(url);
        return new ServiceResult<IEnumerable<ItemModel>, Exception>(result);
    }
    catch (Exception e)
    {
        return new ServiceResult<IEnumerable<ItemModel>, Exception>(e);
    }
}
      
      



, - , - , try-catch:





public async Task Initialize()
{
    var result = await _dataService.LoadItemsAsync();
    if (result.IsSuccessful)
    {
        var itemModels = result.Result.ToList();
        if (itemModels.Any())
        {
            var items = new List<ItemViewModel>();
            itemModels.ForEach(m => items.Add(new ItemViewModel(m)));
            Items.AddRange(items);
        }
    }
    else
    {
        //      
    }   
}
      
      



:





  • , .





  • try-catch





  • - , , -





:





  • , , ServiceResult





  • -, - try-catch, ,





  • try-catch





OperationFactory FlexiMvvm. , Func OnSuccess OnError, , . DataService



:





public interface IDataService
{
    Task LoadItemsAsync(
        Func<IEnumerable<ItemModel>, Task> onSuccess = null,
        Func<Exception, Task> onError = null);
}
      
      



public async Task LoadItemsAsync(
    Func<IEnumerable<ItemModel>, Task> onSuccess = null,
    Func<Exception, Task> onError = null)
{
    try
    {
        var url = "https://customapiservice/v1/items";
        var result = await _requestService.GetAsync<IEnumerable<ItemModel>>(url);
        onSuccess?.Invoke(result);
    }
    catch (Exception e)
    {
        onError?.Invoke(e);
    }
}
      
      



- :





public async Task Initialize()
{
    await _dataService.LoadItemsAsync(HandleLoadSuccess, HandleLoadError);
}

private Task HandleLoadSuccess(IEnumerable<ItemModel> result)
{
    var itemModels = result.ToList();
    if (itemModels.Any())
    {
        var items = new List<ItemViewModel>();
        itemModels.ForEach(m => items.Add(new ItemViewModel(m)));
        Items.AddRange(items);
    }

    return Task.CompletedTask;
}

private Task HandleLoadError(Exception arg)
{
    //      
}
      
      



, - , Initialize()



, , .





, :





  • -









  • :





  • - OnSuccess OnError, , ,





  • try-catch





, OperationFactory. ServiceResult



. ServiceCall



, . 3 Func. - , - , - .





ExecuteAsync()



try-catch-finally. exception - ErrorHandler, try-catch, exception. - SuccessHandler, try-catch.





public class ServiceCall<TResult>
{
    private readonly Func<Task<TResult>> _callAction;

    public ServiceCall(Func<Task<TResult>> callAction)
    {
        _callAction = callAction;
    }

    public Func<TResult, Task> SuccessHandler { get; set; }

    public Func<Exception, Task> ErrorHandler { get; set; }

		public async Task ExecuteAsync()
    {
        TResult result = default;
        var isSuccess = false;

        try
        {
            result = await _callAction.Invoke();
            isSuccess = true;
        }
        catch (Exception e)
        {
            try
            {
                await ErrorHandler.Invoke(e);
            }
            catch (Exception)
            {
            }
        }
        finally
        {
            if (isSuccess)
            {
                try
                {
                    await SuccessHandler.Invoke(result);
                }
                catch (Exception)
                {
                }
            }
        }
    }
}
      
      



ServiceCallHandler



, , . , .





public interface IServiceCallHandler<TResult>
{
    IServiceCallHandler<TResult> OnSuccessAsync(Func<TResult, Task> handler);

    IServiceCallHandler<TResult> OnErrorAsync(Func<Exception, Task> handler);

    Task ExecuteAsync();
}
      
      



public class ServiceCallHandler<TResult> : IServiceCallHandler<TResult>
{
    private ServiceCall<TResult> _serviceCall;

    public ServiceCallHandler(ServiceCall<TResult> serviceCall)
    {
        _serviceCall = serviceCall;
    }

    public IServiceCallHandler<TResult> OnSuccessAsync(Func<TResult, Task> handler)
    {
        _serviceCall.SuccessHandler = handler;
        return this;
    }

    public IServiceCallHandler<TResult> OnErrorAsync(Func<Exception, Task> handler)
    {
        _serviceCall.ErrorHandler = handler;
        return this;
    }

    public Task ExecuteAsync() => _serviceCall.ExecuteAsync();
}
      
      







public interface IDataService
{
    IServiceCallHandler<IEnumerable<ItemModel>> LoadItems();
}
      
      



public IServiceCallHandler<IEnumerable<ItemModel>> LoadItems()
{
    var serviceCall = new ServiceCall<IEnumerable<ItemModel>>(LoadItemsAction);
    return new ServiceCallHandler<IEnumerable<ItemModel>>(serviceCall);
}

private Task<IEnumerable<ItemModel>> LoadItemsAction()
{
    var url = "https://customapiservice/v1/items";
    return _requestService.GetAsync<IEnumerable<ItemModel>>(url);
}
      
      



Initialize()



- , HandleLoadSuccess



HandleLoadError



.





public async Task Initialize()
{
    await _dataService
        .LoadItems()
        .OnSuccessAsync(HandleLoadSuccess)
        .OnErrorAsync(HandleLoadError)
        .ExecuteAsync();
}
      
      



:





  • , , , . OnSuccess OnError .





  • try-catch , -. ServiceCall.





, , :









  • , , IServiceCallHandler





  • ExecuteAsync()





, , . , . , .





Si también tiene métodos interesantes que no se mencionan en este artículo, compártalos en los comentarios, tal vez alguien aprenda algo útil para ellos.








All Articles