diff --git a/Caching/FileDbCache/FileDbCache.cs b/Caching/FileDbCache/FileDbCache.cs index 0edd4fba..eaa3e0a2 100644 --- a/Caching/FileDbCache/FileDbCache.cs +++ b/Caching/FileDbCache/FileDbCache.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Runtime.Caching; @@ -18,25 +19,61 @@ namespace Caching private const string expiresField = "Expires"; private readonly BinaryFormatter formatter = new BinaryFormatter(); - private readonly FileDb fileDb = new FileDb(); + private readonly FileDb fileDb = new FileDb { AutoFlush = false, AutoCleanThreshold = -1 }; private readonly string name; private readonly string path; - public FileDbCache(string name, string path) + public FileDbCache(string name, NameValueCollection config) + : this(name, config["directory"]) { - if (string.IsNullOrEmpty(path)) + string autoFlush = config["autoFlush"]; + string autoCleanThreshold = config["autoCleanThreshold"]; + + if (autoFlush != null) { - throw new ArgumentException("The parameter path must not be null or empty."); + try + { + fileDb.AutoFlush = bool.Parse(autoFlush); + } + catch (Exception ex) + { + throw new ArgumentException("The configuration parameter autoFlush must be a boolean value.", ex); + } } + if (autoCleanThreshold != null) + { + try + { + fileDb.AutoCleanThreshold = int.Parse(autoCleanThreshold); + } + catch (Exception ex) + { + throw new ArgumentException("The configuration parameter autoCleanThreshold must be an integer value.", ex); + } + } + } + + public FileDbCache(string name, string directory) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentException("The parameter name must not be null or empty."); + } + + if (string.IsNullOrEmpty(directory)) + { + throw new ArgumentException("The parameter directory must not be null or empty."); + } + + this.name = name; + path = Path.Combine(directory, name); + if (string.IsNullOrEmpty(Path.GetExtension(path))) { path += ".fdb"; } - this.name = name; - this.path = path; - try { fileDb.Open(path, false); @@ -46,7 +83,7 @@ namespace Caching CreateDatebase(); } - Trace.TraceInformation("FileDbCache created with {0} cached items", fileDb.NumRecords); + Trace.TraceInformation("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, path); } public bool AutoFlush @@ -96,21 +133,24 @@ namespace Caching long count = 0; - try + if (fileDb.IsOpen) { - count = fileDb.NumRecords; - } - catch - { - if (CheckReindex()) + try { + count = fileDb.NumRecords; + } + catch (Exception ex1) + { + Trace.TraceWarning("FileDbCache: FileDb.NumRecords failed: {0}", ex1.Message); + try { + fileDb.Reindex(); count = fileDb.NumRecords; } - catch + catch (Exception ex2) { - CreateDatebase(); + Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex2.Message); } } } @@ -118,75 +158,64 @@ namespace Caching return count; } - public override bool Contains(string key, string regionName = null) + private Record GetRecord(string key) { - if (regionName != null) + Record record = null; + + if (fileDb.IsOpen) { - throw new NotSupportedException("The parameter regionName must be null."); + try + { + record = fileDb.GetRecordByKey(key, new string[] { valueField }, false); + } + catch (Exception ex1) + { + Trace.TraceWarning("FileDbCache: FileDb.GetRecordByKey(\"{0}\") failed: {1}", key, ex1.Message); + + try + { + fileDb.Reindex(); + record = fileDb.GetRecordByKey(key, new string[] { valueField }, false); + } + catch (Exception ex2) + { + Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex2.Message); + } + } } + return record; + } + + public override bool Contains(string key, string regionName = null) + { if (key == null) { throw new ArgumentNullException("The parameter key must not be null."); } - bool contains = false; - - try + if (regionName != null) { - contains = fileDb.GetRecordByKey(key, new string[0], false) != null; - } - catch - { - if (CheckReindex()) - { - try - { - contains = fileDb.GetRecordByKey(key, new string[0], false) != null; - } - catch - { - CreateDatebase(); - } - } + throw new NotSupportedException("The parameter regionName must be null."); } - return contains; + return GetRecord(key) != null; } public override object Get(string key, string regionName = null) { - if (regionName != null) - { - throw new NotSupportedException("The parameter regionName must be null."); - } - if (key == null) { throw new ArgumentNullException("The parameter key must not be null."); } - object value = null; - Record record = null; + if (regionName != null) + { + throw new NotSupportedException("The parameter regionName must be null."); + } - try - { - record = fileDb.GetRecordByKey(key, new string[] { valueField }, false); - } - catch - { - if (CheckReindex()) - { - try - { - record = fileDb.GetRecordByKey(key, new string[] { valueField }, false); - } - catch - { - CreateDatebase(); - } - } - } + object value = null; + Record record = GetRecord(key); if (record != null) { @@ -197,16 +226,17 @@ namespace Caching value = formatter.Deserialize(stream); } } - catch (Exception exc) + catch (Exception ex1) { - Trace.TraceWarning("FileDbCache.Get({0}): {1}", key, exc.Message); + Trace.TraceWarning("FileDbCache: Failed deserializing item \"{0}\": {1}", key, ex1.Message); try { fileDb.DeleteRecordByKey(key); } - catch + catch (Exception ex2) { + Trace.TraceWarning("FileDbCache: FileDb.DeleteRecordByKey(\"{0}\") failed: {1}", key, ex2.Message); } } } @@ -239,9 +269,9 @@ namespace Caching public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) { - if (regionName != null) + if (key == null) { - throw new NotSupportedException("The parameter regionName must be null."); + throw new ArgumentNullException("The parameter key must not be null."); } if (value == null) @@ -249,53 +279,57 @@ namespace Caching throw new ArgumentNullException("The parameter value must not be null."); } - if (key == null) + if (regionName != null) { - throw new ArgumentNullException("The parameter key must not be null."); + throw new NotSupportedException("The parameter regionName must be null."); } - byte[] valueBuffer = null; - - try + if (fileDb.IsOpen) { - using (MemoryStream stream = new MemoryStream()) - { - formatter.Serialize(stream, value); - valueBuffer = stream.ToArray(); - } - } - catch (Exception exc) - { - Trace.TraceWarning("FileDbCache.Set({0}): {1}", key, exc.Message); - } - - if (valueBuffer != null) - { - DateTime expires = DateTime.MaxValue; - - if (policy.AbsoluteExpiration != InfiniteAbsoluteExpiration) - { - expires = policy.AbsoluteExpiration.DateTime; - } - else if (policy.SlidingExpiration != NoSlidingExpiration) - { - expires = DateTime.UtcNow + policy.SlidingExpiration; - } + byte[] valueBuffer = null; try { - AddOrUpdateRecord(key, valueBuffer, expires); - } - catch - { - if (CheckReindex()) + using (MemoryStream stream = new MemoryStream()) { + formatter.Serialize(stream, value); + valueBuffer = stream.ToArray(); + } + } + catch (Exception ex) + { + Trace.TraceWarning("FileDbCache: Failed serializing item \"{0}\": {1}", key, ex.Message); + } + + if (valueBuffer != null) + { + DateTime expires = DateTime.MaxValue; + + if (policy.AbsoluteExpiration != InfiniteAbsoluteExpiration) + { + expires = policy.AbsoluteExpiration.DateTime; + } + else if (policy.SlidingExpiration != NoSlidingExpiration) + { + expires = DateTime.UtcNow + policy.SlidingExpiration; + } + + try + { + AddOrUpdateRecord(key, valueBuffer, expires); + } + catch (Exception ex1) + { + Trace.TraceWarning("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\") failed: {1}", key, ex1.Message); + try { + fileDb.Reindex(); AddOrUpdateRecord(key, valueBuffer, expires); } - catch + catch (Exception ex2) { + Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}, creating new database", ex2.Message); CreateDatebase(); AddOrUpdateRecord(key, valueBuffer, expires); } @@ -343,8 +377,9 @@ namespace Caching { fileDb.DeleteRecordByKey(key); } - catch + catch (Exception ex) { + Trace.TraceWarning("FileDbCache: FileDb.DeleteRecordByKey(\"{0}\") failed: {1}", key, ex.Message); } } @@ -357,9 +392,9 @@ namespace Caching { fileDb.Flush(); } - catch + catch (Exception ex) { - CheckReindex(); + Trace.TraceWarning("FileDbCache: FileDb.Flush() failed: {0}", ex.Message); } } @@ -369,40 +404,29 @@ namespace Caching { fileDb.Clean(); } - catch + catch (Exception ex) { - CheckReindex(); + Trace.TraceWarning("FileDbCache: FileDb.Clean() failed: {0}", ex.Message); } } public void Dispose() - { - try - { - fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, EqualityEnum.LessThanOrEqual)); - Trace.TraceInformation("FileDbCache has deleted {0} expired items", fileDb.NumDeleted); - fileDb.Clean(); - fileDb.Close(); - } - catch - { - if (CheckReindex()) - { - fileDb.Close(); - } - } - } - - private bool CheckReindex() { if (fileDb.IsOpen) { - Trace.TraceWarning("FileDbCache is reindexing the cache database"); - fileDb.Reindex(); - return true; - } + try + { + fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, EqualityEnum.LessThanOrEqual)); + Trace.TraceInformation("FileDbCache: Deleting {0} expired items", fileDb.NumDeleted); + fileDb.Clean(); + } + catch + { + fileDb.Reindex(); + } - return false; + fileDb.Close(); + } } private void CreateDatebase() diff --git a/MapControl/Location.cs b/MapControl/Location.cs index 7035698d..82218306 100644 --- a/MapControl/Location.cs +++ b/MapControl/Location.cs @@ -1,4 +1,8 @@ -using System; +// WPF MapControl - http://wpfmapcontrol.codeplex.com/ +// Copyright © 2012 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; using System.ComponentModel; using System.Globalization; using System.Windows; diff --git a/MapControl/LocationAnimation.cs b/MapControl/LocationAnimation.cs index 83642e7a..0fd49e9a 100644 --- a/MapControl/LocationAnimation.cs +++ b/MapControl/LocationAnimation.cs @@ -1,4 +1,8 @@ -using System; +// WPF MapControl - http://wpfmapcontrol.codeplex.com/ +// Copyright © 2012 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; using System.Windows; using System.Windows.Media.Animation; diff --git a/MapControl/LocationCollection.cs b/MapControl/LocationCollection.cs index 3e5a921a..e48a84f7 100644 --- a/MapControl/LocationCollection.cs +++ b/MapControl/LocationCollection.cs @@ -1,4 +1,8 @@ -using System; +// WPF MapControl - http://wpfmapcontrol.codeplex.com/ +// Copyright © 2012 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; diff --git a/MapControl/MapItem.cs b/MapControl/MapItem.cs index 7ea31070..bade02bb 100644 --- a/MapControl/MapItem.cs +++ b/MapControl/MapItem.cs @@ -2,12 +2,10 @@ // Copyright © 2012 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; -using System.Windows.Media; namespace MapControl { diff --git a/MapControl/MapPanel.cs b/MapControl/MapPanel.cs index 0f19d30d..d6dfa99e 100644 --- a/MapControl/MapPanel.cs +++ b/MapControl/MapPanel.cs @@ -5,7 +5,6 @@ using System; using System.Windows; using System.Windows.Controls; -using System.Windows.Media; namespace MapControl { diff --git a/MapControl/MapTransform.cs b/MapControl/MapTransform.cs index f0797c1c..227a3ef7 100644 --- a/MapControl/MapTransform.cs +++ b/MapControl/MapTransform.cs @@ -2,7 +2,6 @@ // Copyright © 2012 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; using System.Collections.Generic; using System.Linq; using System.Windows; diff --git a/MapControl/MercatorTransform.cs b/MapControl/MercatorTransform.cs index 2fd59020..a4a33b8e 100644 --- a/MapControl/MercatorTransform.cs +++ b/MapControl/MercatorTransform.cs @@ -4,7 +4,6 @@ using System; using System.Windows; -using System.Windows.Media; namespace MapControl { diff --git a/MapControl/Pushpin.cs b/MapControl/Pushpin.cs index 745f7ce2..4c48ad07 100644 --- a/MapControl/Pushpin.cs +++ b/MapControl/Pushpin.cs @@ -1,4 +1,7 @@ -using System; +// WPF MapControl - http://wpfmapcontrol.codeplex.com/ +// Copyright © 2012 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + using System.Windows; using System.Windows.Controls; diff --git a/MapControl/TileContainer.cs b/MapControl/TileContainer.cs index ae9284a9..05a212d7 100644 --- a/MapControl/TileContainer.cs +++ b/MapControl/TileContainer.cs @@ -3,12 +3,11 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -using System.Collections.Generic; +using System.Collections; using System.Collections.Specialized; using System.Windows; using System.Windows.Media; using System.Windows.Threading; -using System.Collections; namespace MapControl { diff --git a/MapControl/TileImageLoader.cs b/MapControl/TileImageLoader.cs index 577e0927..5050b6eb 100644 --- a/MapControl/TileImageLoader.cs +++ b/MapControl/TileImageLoader.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; @@ -11,7 +12,6 @@ using System.Net; using System.Runtime.Caching; using System.Threading; using System.Windows; -using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; @@ -48,19 +48,52 @@ namespace MapControl /// /// The time interval after which cached images expire. The default value is 30 days. /// When an image is not retrieved from the cache during this interval it is considered - /// as expired and will be removed from the cache. If an image is retrieved from the cache - /// and the CacheUpdateAge time interval has expired, the image is downloaded again and - /// rewritten to the cache with new expiration time. + /// as expired and will be removed from the cache. If an image is retrieved from the + /// cache and the CacheUpdateAge time interval has expired, the image is downloaded + /// again and rewritten to the cache with a new expiration time. /// public static TimeSpan CacheExpiration { get; set; } /// /// The time interval after which a cached image is updated and rewritten to the cache. - /// The default value is one day. This time interval should be shorter than the value of - /// the CacheExpiration property. + /// The default value is one day. This time interval should be shorter than the value + /// of the CacheExpiration property. /// public static TimeSpan CacheUpdateAge { get; set; } + /// + /// Creates an instance of the ObjectCache-derived type T and sets the static Cache + /// property to this instance. Class T must (like System.Runtime.Caching.MemoryCache) + /// provide a constructor with two parameters, first a string that gets the name of + /// the cache instance, second a NameValueCollection that gets the config parameter. + /// If config is null, a new NameValueCollection is created. If config does not already + /// contain an entry with key "directory", a new entry is added with this key and a + /// value that specifies the path to an application data directory where the cache + /// implementation may store persistent cache data files. + /// + public static void CreateCache(NameValueCollection config = null) where T : ObjectCache + { + if (config == null) + { + config = new NameValueCollection(1); + } + + if (config["directory"] == null) + { + config["directory"] = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl"); + } + + try + { + Cache = (ObjectCache)Activator.CreateInstance(typeof(T), "TileCache", config); + } + catch (Exception ex) + { + Trace.TraceWarning("Could not create instance of type {0} with String and NameValueCollection constructor parameters: {1}", typeof(T), ex.Message); + throw; + } + } + static TileImageLoader() { Cache = MemoryCache.Default; @@ -77,7 +110,7 @@ namespace MapControl }; } - public TileImageLoader(TileLayer tileLayer) + internal TileImageLoader(TileLayer tileLayer) { this.tileLayer = tileLayer; } @@ -128,7 +161,7 @@ namespace MapControl } else if (!CreateTileImage(tile, cachedImage.ImageBuffer)) { - // got garbage from cache + // got corrupted buffer from cache Cache.Remove(key); pendingTiles.Enqueue(tile); } @@ -215,28 +248,28 @@ namespace MapControl TraceInformation("{0} - Completed", tile.Uri); } - catch (WebException exc) + catch (WebException ex) { buffer = null; - if (exc.Status == WebExceptionStatus.ProtocolError) + if (ex.Status == WebExceptionStatus.ProtocolError) { - TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)exc.Response).StatusCode); + TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)ex.Response).StatusCode); } - else if (exc.Status == WebExceptionStatus.RequestCanceled) // by HttpWebRequest.Abort in CancelDownloadTiles + else if (ex.Status == WebExceptionStatus.RequestCanceled) // by HttpWebRequest.Abort in CancelDownloadTiles { - TraceInformation("{0} - {1}", tile.Uri, exc.Status); + TraceInformation("{0} - {1}", tile.Uri, ex.Status); } else { - TraceWarning("{0} - {1}", tile.Uri, exc.Status); + TraceWarning("{0} - {1}", tile.Uri, ex.Status); } } - catch (Exception exc) + catch (Exception ex) { buffer = null; - TraceWarning("{0} - {1}", tile.Uri, exc.Message); + TraceWarning("{0} - {1}", tile.Uri, ex.Message); } if (request != null) @@ -267,9 +300,9 @@ namespace MapControl Dispatcher.BeginInvoke((Action)(() => tile.Image = bitmap)); } - catch (Exception exc) + catch (Exception ex) { - TraceWarning("Creating tile image failed: {0}", exc.Message); + TraceWarning("Creating tile image failed: {0}", ex.Message); return false; } diff --git a/MapControl/TileLayer.cs b/MapControl/TileLayer.cs index dac36127..792179c9 100644 --- a/MapControl/TileLayer.cs +++ b/MapControl/TileLayer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Windows; using System.Windows.Markup; using System.Windows.Media; diff --git a/MapControl/TileLayerCollection.cs b/MapControl/TileLayerCollection.cs index 9d993660..7684e42a 100644 --- a/MapControl/TileLayerCollection.cs +++ b/MapControl/TileLayerCollection.cs @@ -2,9 +2,7 @@ // Copyright © 2012 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; using System.Collections.ObjectModel; -using System.Windows.Media; namespace MapControl { diff --git a/SampleApps/SampleApplication/App.config b/SampleApps/SampleApplication/App.config new file mode 100644 index 00000000..19261611 --- /dev/null +++ b/SampleApps/SampleApplication/App.config @@ -0,0 +1,15 @@ + + + + +
+ + + + + + True + + + + \ No newline at end of file diff --git a/SampleApps/SampleApplication/MainWindow.xaml.cs b/SampleApps/SampleApplication/MainWindow.xaml.cs index aa7a17d6..805cacbb 100644 --- a/SampleApps/SampleApplication/MainWindow.xaml.cs +++ b/SampleApps/SampleApplication/MainWindow.xaml.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using System.Windows.Media; -using MapControl; using Caching; +using MapControl; namespace SampleApplication { @@ -15,13 +11,9 @@ namespace SampleApplication { public MainWindow() { - bool usePersistentCache = false; - - if (usePersistentCache) + if (Properties.Settings.Default.UsePersistentCache) { - string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); - string cachePath = Path.Combine(appDataFolder, "MapControl", "Cache.fdb"); - TileImageLoader.Cache = new FileDbCache("MapControlCache", cachePath) { AutoFlush = false }; + TileImageLoader.CreateCache(); } InitializeComponent(); diff --git a/SampleApps/SampleApplication/Properties/Settings.Designer.cs b/SampleApps/SampleApplication/Properties/Settings.Designer.cs new file mode 100644 index 00000000..3e61f19c --- /dev/null +++ b/SampleApps/SampleApplication/Properties/Settings.Designer.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SampleApplication.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool UsePersistentCache { + get { + return ((bool)(this["UsePersistentCache"])); + } + } + } +} diff --git a/SampleApps/SampleApplication/Properties/Settings.settings b/SampleApps/SampleApplication/Properties/Settings.settings new file mode 100644 index 00000000..81f05344 --- /dev/null +++ b/SampleApps/SampleApplication/Properties/Settings.settings @@ -0,0 +1,9 @@ + + + + + + True + + + \ No newline at end of file diff --git a/SampleApps/SampleApplication/SampleApplication.csproj b/SampleApps/SampleApplication/SampleApplication.csproj index d9ab5d07..f8ae4383 100644 --- a/SampleApps/SampleApplication/SampleApplication.csproj +++ b/SampleApps/SampleApplication/SampleApplication.csproj @@ -54,6 +54,11 @@ MSBuild:Compile Designer + + True + True + Settings.settings + MSBuild:Compile @@ -84,6 +89,13 @@ MapControl + + + + SettingsSingleFileGenerator + Settings.Designer.cs + +