2013-01-21 2 views
8

Ich möchte einige Daten im Konstruktor eines ViewModel laden, aber aufgrund der Async-Natur von WinRT bin ich gezwungen, Async-Methoden zu verwenden. Leider kann ich keinen asynchronen Konstruktor haben, deshalb versuche ich die asynchrone Methode als synchrone Methode zu verwenden. Ich bin mir sicher, dass es eine viel bessere Möglichkeit gibt, Daten (async) auf die Anwendungslast zu laden, aber meine Gedanken sind im Moment leer.Asynchrone WinRT-Daten laden in Konstruktor

Ich bin entweder auf der Suche nach einer Möglichkeit, meine App mit der Linie der Gedanken, die ich gehe, zu beheben, oder um das dauerhaft mit einer angemesseneren Methode zu beheben.

Der Code ist sehr einfach (sogar das ViewModel fehlt), nur um das Problem zu demonstrieren, vor dem ich stehe.

public sealed partial class MainPage : Page 
{ 

    public string Data { get; set; } 

    public DataService _dataService { get; set; } 

    public MainPage() 
    { 
     this.InitializeComponent(); 

     _dataService = new DataService(); 
     var t = _dataService.GetData(); 

     Data = t.Result; 
    } 

    /// <summary> 
    /// Invoked when this page is about to be displayed in a Frame. 
    /// </summary> 
    /// <param name="e">Event data that describes how this page was reached. The Parameter 
    /// property is typically used to configure the page.</param> 
    protected override void OnNavigatedTo(NavigationEventArgs e) 
    { 
    } 


} 

public class DataService 
{ 
    public async Task<string> GetData() 
    { 
     //Force async 
     await Task.Delay(1); 

     return "Hello"; 
    } 
} 

Mit freundlichen Grüßen

Antwort

9

schrieb ich eine letzte Blog-Post über async in constructors.

Kurz gesagt, ziehe ich eine async Factory-Methode:

public sealed class MyViewModel : INotifyPropertyChanged 
{ 
    private readonly DataService _service; 

    private MyViewModel(DataService service) 
    { 
    _service = service; 
    } 

    private async Task InitializeAsync() 
    { 
    var result = await _service.GetData(); // async initialization 

    Data = result; // update data-bound properties with the results 
    } 

    // Data triggers INotifyPropertyChanged on write 
    public string Data { get { ... } set { ... } } 

    public static async Task<MyViewModel> CreateAsync() 
    { 
    var ret = new MyViewModel(); 
    await ret.InitializeAsync(); 
    return ret; 
    } 
} 
2

Erzwingen asynchroner Methoden synchron in der Regel führt zu Deadlocks laufen, so würde ich nicht empfehlen. Die Sache mit View-Modellen ist, dass sie in der Regel Änderungsbenachrichtigungen durch INotifyPropertyChangedPropertyChanged Ereignis unterstützen, so dass nicht alle Daten sofort verfügbar sein müssen. Wenn Ihre Daten nicht fest codiert sind, sollten Sie nicht erwarten, dass die Daten sofort angezeigt werden, und Sie möchten höchstwahrscheinlich eine Fortschrittsanzeige anzeigen, während die Daten geladen werden. Also ...

Rufen Sie in Ihrem Konstruktor eine asynchrone Initialisierungsmethode auf, ohne das Ergebnis zu erwarten (da Sie in einem Konstruktor nicht warten können) und in der Initialisierungsmethode, wenn alle Daten verfügbar sind view bindet an das PropertyChanged-Ereignis für diese Eigenschaften und blendet den Fortschrittsanzeiger durch Ändern der Ansichtsmodelleigenschaft aus, die seine Sichtbarkeit steuert.

+2

Behalten Sie die Fehlerbehandlung im Auge. Wenn die von der asynchronen Initialisierungsmethode zurückgegebene "Task" niemals beachtet wird, werden alle Ausnahmen stillschweigend verschluckt. Stellen Sie also sicher, dass Sie in Ihrem 'InitializeAsync' einen' try'/'catch' haben oder eine andere Methode' erwarten'. –

+0

Natürlich. Dies gilt unabhängig davon, ob sich der Aufruf in einem Initialisierer oder einer anderen Methode befindet, die zu einem fehlerhaften Status führen kann. –