简体   繁体   中英

What is the best way to handle this two async methods that one dependent on the other?

I'm using Prism Library the latest version.

My list page view model - ActivityListPageViewModel.cs has something like this below which basically it's a list and when it clicked/selected then will navigate to the detail page.

public ActivityModel SelectedItem
    {
        get => _selectedActivity;
        set
        {
            SetProperty(ref _selectedActivity, value);
            if (_selectedActivity != null)
                ShowActivityDetails(_selectedActivity);

        }
    }

    async void ShowActivityDetails(ActivityModel selectedActivity)
    {
        var navigationParams = new NavigationParameters();
        navigationParams.Add("activity", selectedActivity);

        await _navigationService.NavigateAsync("ActivityDetailPage", navigationParams);
    }

In my detail page view model - ActivityDetailPageViewModel.cs

public class ActivityDetailPageViewModel : ViewModelBase, IActiveAware, INavigationAware
{
    private readonly IActivityApiService _activityApiService;

    private readonly ICategoryApiService _categoryApiService;

    private readonly INavigationService _navigationService;

    private readonly IPageDialogService _dialogService;

    public DelegateCommand DeleteCommand { get; private set; }

    public DelegateCommand SaveCommand { get; private set; }

    private ActivityModel _activity;

    public ActivityModel Activity
    {
        get { return this._activity; }
        set
        {
            SetProperty(ref this._activity, value);
        }
    }


    private ActivityModelValidator _activityValidator;

    private bool _isActive;

    public bool IsActive
    {
        get { return _isActive; }
        set
        {
            _isActive = value;
            SetProperty(ref _isActive, value, RaiseIsActiveChange);
        }
    }


    private List<CategoryModel> _categoryList;

    public List<CategoryModel> CategoryList
    {
        get => _categoryList;
        set => SetProperty(ref _categoryList, value);
    }

    private CategoryModel _selectedCategory;

    public event EventHandler IsActiveChanged;

    public CategoryModel SelectedCategory
    {
        get => _selectedCategory;
        set => SetProperty(ref _selectedCategory, value);
    }
    //public bool IsActive { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

    public ActivityDetailPageViewModel(CategoryApiService categoryApiService, ActivityApiService activityApiService, INavigationService navigationService, IPageDialogService dialogService) : base(navigationService)
    {
        _categoryApiService = categoryApiService;
        _activityApiService = activityApiService;
        _navigationService = navigationService;
        _dialogService = dialogService;

        _activityValidator = new ActivityModelValidator();

        Title = "Activity";

        DeleteCommand = new DelegateCommand(DeleteActivity);
        SaveCommand = new DelegateCommand(SaveActivity);
    }

    protected virtual void RaiseIsActiveChange()
    {
        IsActiveChanged?.Invoke(this, EventArgs.Empty);
    }

    public override void OnNavigatedTo(INavigationParameters parameters)
    {
        var activity = parameters["activity"] as ActivityModel;

        Activity = activity ?? new ActivityModel();

        Task.Run(async () => { await FetchCategories(); }).Wait();
        Task.Run(async () => { await FetchActivityDetail(); });
    }

    public async Task FetchActivityDetail()
    {
        if (Activity.Id > 0)
        {
            Activity = await _activityApiService.GetActivity(Activity.Id);
            SelectedCategory = CategoryList.FirstOrDefault(x => x.Id == Activity.CategoryId);
        }
    }

    public async Task FetchCategories()
    {
        CategoryResultModel categoryResult = await _categoryApiService.GetCategories();
        CategoryList = categoryResult.Results;
    }

    async void SaveActivity()
    {
        Activity.CategoryId = SelectedCategory.Id;
        Activity.CategoryName = SelectedCategory.Name;

        var validationResults = _activityValidator.Validate(Activity);

        if (validationResults.IsValid)
        {
            if (Activity.Id != 0)
            {
                bool isUserAccept = await _dialogService.DisplayAlertAsync("Activity", "Update the details", "OK", "Cancel");
                if (isUserAccept)
                {
                    var response = await _activityApiService.UpdateActivity(Activity.Id, Activity);
                    if (!response)
                    {
                        await _dialogService.DisplayAlertAsync("Activity", "Error.", "OK");
                    }
                    else
                    {
                        await _navigationService.NavigateAsync("NavigationPage/ActivityListPage");
                    }
                }
            }
            else
            {
                bool isUserAccept = await _dialogService.DisplayAlertAsync("Activity", "Add a new details", "OK", "Cancel");
                if (isUserAccept)
                {
                    var response = await _activityApiService.AddActivity(Activity);
                    if (!response)
                    {
                        await _dialogService.DisplayAlertAsync("Activity", "Error.", "OK");
                    }
                    else
                    {
                        await _navigationService.NavigateAsync("NavigationPage/ActivityListPage");
                    }
                }
            }
        }
        else
        {
            await _dialogService.DisplayAlertAsync("Activity", validationResults.Errors[0].ErrorMessage, "OK");
        }
    }


    async void DeleteActivity()
    {
        var alert = await _dialogService.DisplayAlertAsync("Activity", "Do you want to delete this item?", "Yes", "Cancel");
        if (alert)
        {
            var response = await _activityApiService.DeleteActivity(Activity.Id);
            if (!response)
            {
                await _dialogService.DisplayAlertAsync("Activity", "Error.", "OK");
            }
            else
            {
                await _navigationService.NavigateAsync("NavigationPage/ActivityListPage");
            }
        }
    }
}

I moved the two fetchings (FetchCategories and FetchActivityDetail ) originally from Constructor into OnNavigatedTo() as per recommend. Everything is running well. However my question though as the Fetch Categories needs to run first and complete then run FetchActivityDetail, is the following approach ok? I read somewhere that as long moving away from the Constructor the Wait is safe to run somehow.

Task.Run(async () => { await FetchCategories(); }).Wait();
Task.Run(async () => { await FetchActivityDetail(); });

Is there any better approach?

Thanks

The easiest solution is to convert the override to async , that way you can can just call the async methods as you normally would.

The advantage is the correct context will be passed to the the async methods, which would not happen if you offload with a Task.Run

public override async void OnNavigatedTo(INavigationParameters parameters)
{
    try
    {
        var activity = parameters["activity"] as ActivityModel;

        Activity = activity ?? new ActivityModel();

        await FetchCategories(); 
        await FetchActivityDetail();
     }
     catch(Exception ex)
     {
         // log or tell the user
     }
}

Note : Because this is an async void, you will need to be careful with your exceptions as they will not be observed, so best catch them

Task.Run(async () => { await FetchCategories(); await FetchActivityDetail();  });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM