Minor code improvements

This commit is contained in:
ClemensF 2012-07-07 17:19:10 +02:00
parent 6b25260536
commit 472658a6d9
19 changed files with 301 additions and 175 deletions

View file

@ -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()

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
{

View file

@ -5,7 +5,6 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace MapControl
{

View file

@ -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;

View file

@ -4,7 +4,6 @@
using System;
using System.Windows;
using System.Windows.Media;
namespace MapControl
{

View file

@ -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;

View file

@ -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
{

View file

@ -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
/// <summary>
/// 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.
/// </summary>
public static TimeSpan CacheExpiration { get; set; }
/// <summary>
/// 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.
/// </summary>
public static TimeSpan CacheUpdateAge { get; set; }
/// <summary>
/// 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.
/// </summary>
public static void CreateCache<T>(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;
}

View file

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;

View file

@ -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
{

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="SampleApplication.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<applicationSettings>
<SampleApplication.Properties.Settings>
<setting name="UsePersistentCache" serializeAs="String">
<value>True</value>
</setting>
</SampleApplication.Properties.Settings>
</applicationSettings>
</configuration>

View file

@ -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<FileDbCache>();
}
InitializeComponent();

View file

@ -0,0 +1,35 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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"]));
}
}
}
}

View file

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="SampleApplication.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="UsePersistentCache" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">True</Value>
</Setting>
</Settings>
</SettingsFile>

View file

@ -54,6 +54,11 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="SampleItems.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
@ -84,6 +89,13 @@
<Name>MapControl</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SurfaceApplication")]
[assembly: AssemblyDescription("WPF Map Control Sample SurfaceApplication")]
[assembly: AssemblyDescription("WPF Map Control Sample Surface Application")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyProduct("WPF Map Control")]