From eb4e2387a104bd725bd3f028f0f35dc8e4da44e0 Mon Sep 17 00:00:00 2001 From: "gu.martinm@gmail.com" Date: Fri, 15 Aug 2014 23:25:10 +0200 Subject: [PATCH] WeatherInformation WP8 Using ConfigureAwait(false) for async methods. Updates in database. --- .../WeatherInformation/App.xaml.cs | 146 --------------------- .../WeatherInformation/MainPage.xaml.cs | 68 +++++----- .../WeatherInformation/MapPage.xaml.cs | 4 +- .../WeatherInformation/Model/Location.cs | 16 +-- .../Model/Services/CustomHTTPClient.cs | 2 +- 5 files changed, 49 insertions(+), 187 deletions(-) diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs b/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs index 592dcc6..b10cfec 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs @@ -98,110 +98,6 @@ namespace WeatherInformation } } - public bool IsStoredDataFresh() - { - // Check to see if the data is fresh. - // This example uses 30 seconds as the valid time window to make it easy to test. - // Real apps will use a larger window. - - // Check the time elapsed since data was last saved to isolated storage. - TimeSpan TimeSinceLastSave = TimeSpan.FromSeconds(0); - if (IsolatedStorageSettings.ApplicationSettings.Contains("DataLastSavedTime")) - { - DateTime dataLastSaveTime = (DateTime)IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"]; - TimeSinceLastSave = DateTime.Now - dataLastSaveTime; - } - - if (TimeSinceLastSave.TotalSeconds < 30) - { - return true; - } - - return false; - } - - public WeatherData GetIsolatedStoredData() - { - string JSONRemoteCurrentWeather = null; - string JSONRemoteForecastWeather = null; - string city = null; - string country = null; - - using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (isoStore.FileExists("JSONRemoteCurrentWeatherFile.txt") && - isoStore.FileExists("JSONRemoteForecastWeatherFile.txt") && - isoStore.FileExists("CityFile.txt") && - isoStore.FileExists("CountryFile.txt")) - { - using (IsolatedStorageFileStream file = isoStore.OpenFile("JSONRemoteCurrentWeatherFile.txt", FileMode.Open)) - using (StreamReader sr = new StreamReader(file)) - { - // This method loads the data from isolated storage, if it is available. - JSONRemoteCurrentWeather = sr.ReadLine(); - } - - using (IsolatedStorageFileStream file = isoStore.OpenFile("CityFile.txt", FileMode.Open)) - using (StreamReader sr = new StreamReader(file)) - { - // This method loads the data from isolated storage, if it is available. - city = sr.ReadLine(); - } - - using (IsolatedStorageFileStream file = isoStore.OpenFile("CountryFile.txt", FileMode.Open)) - using (StreamReader sr = new StreamReader(file)) - { - // This method loads the data from isolated storage, if it is available. - country = sr.ReadLine(); - } - } - } - - if (!string.IsNullOrEmpty(JSONRemoteCurrentWeather) && !string.IsNullOrEmpty(JSONRemoteForecastWeather)) - { - // TODO: I am repeating this code 2 times. What could I do to improve it? - var parser = new ServiceParser(new JsonParser()); - var weatherData = parser.WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); - weatherData.City = city ?? ""; - weatherData.Country = country ?? ""; - - return weatherData; - } - - return null; - } - - // no way of moving temporary file atomically with IsolatedStorageFile. LINUX OWNS MICROSOFT. - private void SaveDataToIsolatedStorage(string fileName, string value) - { - string pathToTemporaryFile = CreateTemporaryFile(fileName); - - SaveDataToTemporaryFile(pathToTemporaryFile, value); - - using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) - { - // NO FUCKING WAY, MoveFile throws exception if destination file exists!!!! - // And documentation doesn't say anything... ROFL - // Linux OWNS Microsoft... - // If there are corrupted files do not blame me.... Unbelievable.... - if (isoStore.FileExists(fileName)) - { - isoStore.DeleteFile(fileName); - } - isoStore.MoveFile(pathToTemporaryFile, fileName); - } - } - - private string CreateTemporaryFile(string fileName) - { - using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) - { - isoStore.CreateDirectory("tmp"); - } - - return Path.Combine("tmp", fileName); - } - private void SaveDataToTemporaryFile(string pathToTemporaryFile, string value) { using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) @@ -270,9 +166,6 @@ namespace WeatherInformation // Código para ejecutar cuando la aplicación se desactiva (se envía a segundo plano) // Este código no se ejecutará cuando la aplicación se cierre - // TODO: no siempre pasa por aqui cuando se lanza una nueva ventana de la misma aplicación. - // Y POR ESO HAY PROBLEMAS CON GetDataAsync PORQUE ESTO NO TIENE NADA DE NADA Y DEVUELVE NULL - // Y SIEMPRE HACE UNA PETICIÓN AL SERVIDOR :( private void Application_Deactivated(object sender, DeactivatedEventArgs e) { // If there is data in the application member variable... @@ -282,33 +175,17 @@ namespace WeatherInformation if (!string.IsNullOrEmpty(weatherData.JSONRemoteCurrent) && !string.IsNullOrEmpty(weatherData.JSONRemoteForecast)) { - // TODO: too many files? Remember there is a time limit for running this method!!! :/ - // Store it in the State dictionary. PhoneApplicationService.Current.State["JSONRemoteForecastWeather"] = weatherData.JSONRemoteForecast; - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("JSONRemoteForecastWeatherFile.txt", weatherData.JSONRemoteForecast); - // Store it in the State dictionary. PhoneApplicationService.Current.State["JSONRemoteCurrentWeather"] = weatherData.JSONRemoteCurrent; - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("JSONRemoteCurrentWeatherFile.txt", weatherData.JSONRemoteCurrent); - // Store it in the State dictionary. PhoneApplicationService.Current.State["City"] = weatherData.City; - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("CityFile.txt", weatherData.City); - // Store it in the State dictionary. PhoneApplicationService.Current.State["Country"] = weatherData.Country; - - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("CountryFile.txt", weatherData.Country); - - IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"] = DateTime.Now; } } } @@ -319,29 +196,6 @@ namespace WeatherInformation { // Asegurarse de que el estado de la aplicación requerida persiste aquí. // The application will not be tombstoned, so save only to isolated storage. - var weatherData = ApplicationDataObject; - if (weatherData != null) - { - if (!string.IsNullOrEmpty(weatherData.JSONRemoteForecast) && - !string.IsNullOrEmpty(weatherData.JSONRemoteCurrent)) - { - // TODO: too many files? Remember there is a time limit for running this method!!! :/ - - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("JSONRemoteForecastWeatherFile.txt", weatherData.JSONRemoteForecast); - - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("JSONRemoteCurrentWeatherFile.txt", weatherData.JSONRemoteCurrent); - - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("CityFile.txt", weatherData.City); - - // Also store it in isolated storage, in case the application is never reactivated. - SaveDataToIsolatedStorage("CountryFile.txt", weatherData.Country); - - IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"] = DateTime.Now; - } - } } // Código para ejecutar si hay un error de navegación diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs b/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs index b133b16..897a531 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs @@ -76,20 +76,25 @@ namespace WeatherInformation // set the page's data object from the application member variable. // TODO: I am setting and getting ApplicationDataObject from different threads!!!! What if I do not see its last value? Do I need synchronization? :/ WeatherData weatherData = (Application.Current as WeatherInformation.App).ApplicationDataObject; - if (weatherData != null && - !locationItem.IsNewLocation && - // TODO: NO ESTOY USANDO GetIsolatedStoredData!!!!!! :( - (Application.Current as WeatherInformation.App).IsStoredDataFresh()) + if (!IsDataFresh(locationItem.LastRemoteDataUpdate) || weatherData == null) { - UpdateUI(); - } - else - { - // Otherwise, call the method that loads data. - await GetDataAsync(locationItem); - // Call UpdateApplicationData on the UI thread. - Dispatcher.BeginInvoke(() => UpdateUI()); + // Load remote data (aynchronous way by means of async/await) + + // Gets the data from the web. + // TODO: multiple threads writing/reading same data :( + (Application.Current as WeatherInformation.App).ApplicationDataObject = + await GetRemoteDataAsync(locationItem).ConfigureAwait(false); + + using (var db = new LocationDataContext(LocationDataContext.DBConnectionString)) + { + locationItem = db.Locations.Where(location => location.IsSelected).FirstOrDefault(); + locationItem.LastRemoteDataUpdate = DateTime.UtcNow; + db.SubmitChanges(); + } } + + // Call UpdateUI on the UI thread. + Dispatcher.BeginInvoke(() => UpdateUI()); } void UpdateUI() @@ -109,14 +114,6 @@ namespace WeatherInformation } _mainViewModel.LoadData(weatherData); - - Location locationItem = null; - using (var db = new LocationDataContext(LocationDataContext.DBConnectionString)) - { - locationItem = db.Locations.Where(location => location.IsSelected).FirstOrDefault(); - locationItem.IsNewLocation = false; - db.SubmitChanges(); - } } } @@ -131,17 +128,10 @@ namespace WeatherInformation NavigationService.Navigate(new Uri(uri, UriKind.Relative)); } - async private Task GetDataAsync(Location locationItem) - { - // Gets the data from the web. - // TODO: multiple threads writing/reading same data :( - (Application.Current as WeatherInformation.App).ApplicationDataObject = await LoadDataAsync(locationItem); - } - /// /// Retrieve remote weather data. /// - async public Task LoadDataAsync(Location locationItem) + async public Task GetRemoteDataAsync(Location locationItem) { double latitude = locationItem.Latitude; double longitude = locationItem.Longitude; @@ -152,12 +142,12 @@ namespace WeatherInformation string formattedForecastURL = String.Format( CultureInfo.InvariantCulture, AppResources.URIAPIOpenWeatherMapForecast, AppResources.APIVersionOpenWeatherMap, latitude, longitude, resultsNumber); - string JSONRemoteForecastWeather = await httpClient.GetWeatherDataAsync(formattedForecastURL); + string JSONRemoteForecastWeather = await httpClient.GetWeatherDataAsync(formattedForecastURL).ConfigureAwait(false); string formattedCurrentURL = String.Format( CultureInfo.InvariantCulture, AppResources.URIAPIOpenWeatherMapCurrent, AppResources.APIVersionOpenWeatherMap, latitude, longitude, resultsNumber); - string JSONRemoteCurrentWeather = await httpClient.GetWeatherDataAsync(formattedCurrentURL); + string JSONRemoteCurrentWeather = await httpClient.GetWeatherDataAsync(formattedCurrentURL).ConfigureAwait(false); var parser = new ServiceParser(new JsonParser()); var weatherData = parser.WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); @@ -167,6 +157,24 @@ namespace WeatherInformation return weatherData; } + private bool IsDataFresh(DateTime? lastUpdate) + { + if (lastUpdate == null) + { + return false; + } + + // Check the time elapsed since data was last saved to isolated storage. + TimeSpan TimeSinceLastSave = DateTime.UtcNow - lastUpdate.Value; + + if (TimeSinceLastSave.TotalSeconds < 30) + { + return true; + } + + return false; + } + private void Location_Click(object sender, EventArgs e) { NavigationService.Navigate(new Uri("/MapPage.xaml", UriKind.Relative)); diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs b/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs index 1131802..585d39e 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs @@ -180,8 +180,8 @@ namespace WeatherInformation locationItem.VerticalAccuracy = geocoordinate.VerticalAccuracy; locationItem.Speed = geocoordinate.Speed; locationItem.Course = geocoordinate.Course; - locationItem.IsNewLocation = true; locationItem.IsSelected = true; + locationItem.LastRemoteDataUpdate = null; } else { @@ -196,8 +196,8 @@ namespace WeatherInformation VerticalAccuracy = geocoordinate.VerticalAccuracy, Speed = geocoordinate.Speed, Course = geocoordinate.Course, - IsNewLocation = true, IsSelected = true, + LastRemoteDataUpdate = null, }; // Add a location item to the local database. diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs index 43f93c6..d68a1cc 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs @@ -225,21 +225,21 @@ namespace WeatherInformation.Model } } - private bool _isNewLocation; - [Column(CanBeNull = false)] - public bool IsNewLocation + private DateTime? _lastRemoteDataUpdate; + [Column(CanBeNull = true)] + public DateTime? LastRemoteDataUpdate { get { - return _isNewLocation; + return _lastRemoteDataUpdate; } set { - if (_isNewLocation != value) + if (_lastRemoteDataUpdate != value) { - NotifyPropertyChanging("IsNewLocation"); - _isNewLocation = value; - NotifyPropertyChanged("IsNewLocation"); + NotifyPropertyChanging("LastRemoteDataUpdate"); + _lastRemoteDataUpdate = value; + NotifyPropertyChanged("LastRemoteDataUpdate"); } } } diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/CustomHTTPClient.cs b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/CustomHTTPClient.cs index aa98917..28cd3e1 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/CustomHTTPClient.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/CustomHTTPClient.cs @@ -48,7 +48,7 @@ namespace WeatherInformation.Model.Services // TODO: HttpCompletionOption, without it, by default, I am buffering the received data. // in this case it is not a problem but when receiving loads of bytes I do not // think it is a great idea to buffer all of them... :( - using (HttpResponseMessage response = await client.GetAsync(uriWindowsCacheSucks)) + using (HttpResponseMessage response = await client.GetAsync(uriWindowsCacheSucks).ConfigureAwait(false)) { response.EnsureSuccessStatusCode(); -- 2.1.4