2016-06-06 2 views
0

Я создаю MVVM Light WPF-приложение в Visual Studio 2015 с платформой Entity Framework 6. В приложении есть множество моделей просмотра, которые вызывают методы инициализации async. Вот вид модели пример:Вызов метода async из конструктора модели представления

public class MyViewModel : MyViewModelBase 
{ 
    public MyViewModel() 
    { 
     PopulateParameters(); 

     // Other code... 
    } 

    public ObservableCollection<ParametersViewModel> 
     Parameters { get; private set; } = 
      new ObservableCollection<ParametersViewModel>(); 

    private async void PopulateParameters() 
    { 
     var service = new MyDataService(); 
     Parameters.Clear(); 
     foreach(var parameter in await service.GetParameters()) 
      Parameters.Add(parameter);  
    } 
    // Other methods and properties 
} 

В MyDataService класс, у меня есть этот метод:

public async Task<ParametersViewModel> GetParameters() 
{ 
    using (var context = new MyEntities()) 
    { 
     var query = (from param in context.Parameters 
      select new ParametersViewModel 
      { 
       // Populate ParametersViewModel properties here... 
      } 
      ); 
     return await Task.Run(() => query); 
    } 
} 

Обратите внимание, что конструктор модели представления является Назвав async void метод PopulateParameters(). Это очень плохое программирование, и я бы хотел его изменить. Однако я не знаю, как это сделать. Я не могу вводить необходимые данные во все мои модели просмотра; некоторые из них должны выполнить свою собственную инициализацию, большая часть которых связана с вызовами методов async.

Как изменить приведенный выше код, чтобы соответствовать лучшим методам, когда методы асинхронизации вызывают из конструктора модели представления? Благодарю.

Update: Убедитесь, что вы свяжете на .Result значения, возвращаемого из NotifyTask.Create(). Понадобился время, чтобы понять это. Подробнее об этом здесь: https://msdn.microsoft.com/en-us/magazine/dn605875.aspx

+0

Если вы говорите о лучших практиках - что бы вы сделали, когда ваши PopulateParameters выдадут исключение? И это может и произойдет, потому что он говорит о удаленной службе (или базе данных). – Evk

+0

Почему бы не заставить 'PopulateParameters' правильно возвращать' Task' и вызывать его синхронно из конструктора с помощью '.Wait()'? Я пропустил что-то очевидное здесь? Одна вещь, которая кажется интуитивно очевидной, заключается в том, что конструктор никогда не должен вызывать код «асинхронный», который инициализирует объект, - либо сделать синхронизацию инициализации, либо заставить его произойти где-то, кроме конструктора. –

+0

@Evk, спасибо! Ты прав. Как получить исключение и обработать его изящно? Я хотел включить этот вопрос выше, но не сделал :) – Alex

ответ

3

У меня есть сообщение в блоге, описывающее несколько подходов к «async constructors».

В этом случае, похоже, что вы действительно хотите, это async data binding. Использование NotifyTask типа из моего Mvvm.Async library будет выглядеть следующим образом:

public NotifyTask<ObservableCollection<ParametersViewModel>> 
    Parameters { get; private set { /* with notify, such as RaisePropertyChanged() */ } } 

public MyViewModel() 
{ 
    Parameters = NotifyTask.Create(() => GetParametersAsync(), 
     new ObservableCollection<ParametersViewModel>()); 
    // Other code... 
} 

private async Task<ObservableCollection<ParametersViewModel>> GetParametersAsync() 
{ 
    var service = new MyDataService(); 
    var result = new ObservableCollection<ParametersViewModel>(); 
    foreach(var parameter in await service.GetParameters()) 
     result.Add(parameter); 
    return result; 
} 

NotifyTask<T> обертка предоставляет несколько данных-связываемые свойства, такие как Result, содержащих наблюдаемые коллекции, IsNotCompleted для показа индикаторов загрузки и IsFaulted/ErrorMessage для Data- (если вы хотите).

+0

Спасибо, @StephenCleary. Я видел ваш блог в прошлую пятницу и задавался вопросом, как реализовать примеры, которые у вас есть. Последующее наблюдение: как я могу улавливать исключения, когда db недоступен, поэтому мой вызов «service» вызывает взрыва? – Alex

+1

@Alex: 'NotifyTask ' предполагает, что любые исключения, выходящие из 'GetParametersAsync', будут обрабатываться посредством привязки данных (т. Е.' IsFaulted' и друзей). Если вы хотите обрабатывать их вручную, вы хотите поймать их в 'GetParametersAsync'. –

+0

Спасибо, @StephenCleary.Как вы связываетесь с 'IsFaulted' или' ErrorMessage'? Они находятся на объекте «Параметры»? Кроме того, ссылка API Docs выдает ошибку 500: http://dotnetapis.com/pkg/Nito.Mvvm.Async – Alex

Смежные вопросы