このトピックに関する記事はあまり頻繁に表示されないため、定期的に直面する必要のあるXamarinのさまざまなタスクについて引き続き説明します。この記事は初心者の開発者にとってより役立つでしょうが、より経験豊富なものも興味深いかもしれません。
では、なぜそのようなトピックなのですか?さまざまなプロジェクトに取り組んできたので、それぞれのプロジェクトで、サービスの呼び出しとエラーの処理にまったく異なるアプローチが使用されていると思いました。この記事では、私が直面しなければならなかったすべてを収集し、そこにあるオプションを示し、それぞれの長所と短所について考えることにしました。
簡単な例でさまざまなアプローチを考えてみましょう。バックエンドから標準モデルのリストを要求し、それらをビューモデルのリストに変換してコレクションを表示します。ここではUIの部分については考慮せず、サービスの作業とビューモデルのみに限定します。
したがって、バックエンドから要求する単純なモデルは次のとおりです。
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; }
}
そして、対応するビューモデル。このモデルでは、後続の表示のためにモデルから2つのフィールドのみが必要です。
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()
, , . , . , .
この記事に記載されていない興味深い方法もある場合は、コメントで共有してください。おそらく誰かが自分に役立つ何かを学ぶでしょう。