Это правильный способ обеспечить загрузку данных в модель представления? - PullRequest
0 голосов
/ 22 мая 2018

Я создаю приложение Xamarin.Forms с использованием инфраструктуры Prism MVVM.У меня есть случай, когда мои view-модели могут получать полные объекты, которые будут использоваться в модели представления для обновления данных для представления списка или других элементов пользовательского интерфейса, или он получит только идентификаторы тех объектов, где я могу их использовать, и получить полныйобъекты из веб-службы, но это асинхронная операция, и к тому времени, когда представление загружено и представление списка готово для получения данных, эти объекты могут быть еще не установлены.Я написал модель представления таким образом, что, я думаю, это гарантирует, что просмотр списка не будет обновляться до тех пор, пока эти объекты не будут установлены.Я думал об использовании асинхронного метода void (запускать и забывать) для извлечения объектов, но это означало бы, что если к моменту загрузки списка просмотра веб-запрос все еще будет находиться в фоновом режиме, а затем метод обновления попытается получитьте же объекты снова вызывают повторяющиеся запросы.

//ViewModelBase implements INaviagtionAware and INotifyPropertyChanged
public class MainPageViewModel : ViewModelBase
{
    private string _object1Id;
    private string _object2Id;
    private Task _getObjectsFromIdsTask;
    private Object1 _object1;
    private Object2 _object2;
    private DelegateCommand _refreshListViewSourceCommand;
    private bool _isIsRefreshing;
    public Object1 Object1
    {
        get => _object1;
        set => SetProperty(ref _object1, value);
    }
    public Object2 Object2
    {
        get => _object2;
        set => SetProperty(ref _object2, value);
    }
    public DelegateCommand RefreshListViewSourceCommand => _refreshListViewSourceCommand ?? (_refreshListViewSourceCommand = new DelegateCommand(RefreshListViewSource, CanExecuteRefreshListViewSource).ObservesProperty(() => IsRefreshing));
    public bool IsRefreshing
    {
        get => _isIsRefreshing;
        set => SetProperty(ref _isIsRefreshing, value);
    }
    public MainPageViewModel ( INavigationService navigationService )
    : base ( navigationService )
    {
        Title = "Main Page";
    }
    //Called when the page is first loadded and on each pull-to-refresh.
    private async void RefreshListViewSource ()
    {
        IsRefreshing = true;
        try
        {
            //If this is truen then objects where set from either another viewmodel or the task has completed succesfully before this method was called from the view.
            if ( Object1 != null &&
                 Object2 != null )
            {
                //Populate the listview.
                IsRefreshing = false;
                return;
            }
            else if ( _getObjectsFromIdsTask != null )
            {
                switch ( _getObjectsFromIdsTask.Status )
                {
                    case TaskStatus.Created :
                    case TaskStatus.WaitingForActivation :
                    case TaskStatus.WaitingToRun :
                    case TaskStatus.Running :
                    case TaskStatus.WaitingForChildrenToComplete :
                        //If any of the above then we wait for its completion.
                        await _getObjectsFromIdsTask;
                        break;
                    case TaskStatus.Faulted :
                    case TaskStatus.Canceled :
                        //If an error occurs then we restart the task.
                        _getObjectsFromIdsTask = GetObjectsFromIds ();
                        await _getObjectsFromIdsTask;
                        break;
                    case TaskStatus.RanToCompletion :
                        //If completed then do nothing.
                        break;
                    default :
                        throw new ArgumentOutOfRangeException ();
                }
            }
            else
            {
                //If it is null then this method was called from the view before the task is set.
                //This branch should never be reached, should I delete it.
                _getObjectsFromIdsTask = GetObjectsFromIds ();
                await _getObjectsFromIdsTask;
            }

            //If we get to here then the objects must be set
            //Populate the listview.
        }
        catch ( Exception e )
        {
            Debug.WriteLine ( e );
        }

        IsRefreshing = false;
    }
    private bool CanExecuteRefreshListViewSource () => !IsRefreshing;
    //Gets the objects from a web service by thier ids.
    private async Task GetObjectsFromIds ()
    {
        //Uses _objectId1 and _objectId2 to get the objects. This just simulates delay.
        Task < Object1 > object1 = Task.Delay ( 100 ).ContinueWith ( task => new Object1 () );
        Task < Object2 > object2 = Task.Delay ( 100 ).ContinueWith ( task => new Object2 () );
        Object1 = await object1;
        Object2 = await object2;
    }
    //called when navigation to the page. I used OnNavigatingTo and not OnNavigatedTo to reduce the chance that the refresh method would be called befor the objects are set.
    public override void OnNavigatingTo ( NavigationParameters parameters )
    {
        base.OnNavigatingTo ( parameters );
        //If Ids are sent from another viewmodel then I have to get their objects from a web service. This would happen in case of deep linking from notification or a similar scenario.
        if ( parameters.ContainsKey ( "Object1Id" ) &&
             parameters.ContainsKey ( "Object2Id" ) )
        {
            _object1Id = parameters.GetValue < string > ( "Object1" );
            _object2Id = parameters.GetValue < string > ( "Object2" );
            _getObjectsFromIdsTask = GetObjectsFromIds ();
        }
        //If the full objects are sent, then I am setting the objects right here.
        else if ( parameters.ContainsKey ( "Object1" ) &&
                  parameters.ContainsKey ( "Object2" ) )
        {
            Object1 = parameters.GetValue < Object1 > ( "Object1" );
            Object2 = parameters.GetValue < Object2 > ( "Object2" );
        }
    }
}

Я что-то не так делаю?Есть ли более простой способ сделать это?

...