diff --git a/FileDbCache/Shared/FileDbCache.cs b/FileDbCache/Shared/FileDbCache.cs
new file mode 100644
index 00000000..d278375d
--- /dev/null
+++ b/FileDbCache/Shared/FileDbCache.cs
@@ -0,0 +1,141 @@
+// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
+// © 2019 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+using FileDbNs;
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace MapControl.Caching
+{
+ ///
+ /// Image cache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
+ /// See http://www.eztools-software.com/tools/filedb/.
+ ///
+ public sealed partial class FileDbCache : IDisposable
+ {
+ private const string keyField = "Key";
+ private const string valueField = "Value";
+ private const string expiresField = "Expires";
+
+ private readonly FileDb fileDb = new FileDb() { AutoFlush = true };
+ private readonly string dbPath;
+
+ public void Dispose()
+ {
+ fileDb.Dispose();
+ }
+
+ public void Clean()
+ {
+ if (fileDb.IsOpen)
+ {
+ int deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThan));
+
+ if (deleted > 0)
+ {
+ Debug.WriteLine("FileDbCache: Deleted {0} expired items", deleted);
+ fileDb.Clean();
+ }
+ }
+ }
+
+ private void Open()
+ {
+ if (!fileDb.IsOpen)
+ {
+ try
+ {
+ fileDb.Open(dbPath);
+ Debug.WriteLine("FileDbCache: Opened database " + dbPath);
+
+ Clean();
+ }
+ catch
+ {
+ CreateDatabase();
+ }
+ }
+ }
+
+ private void Close()
+ {
+ if (fileDb.IsOpen)
+ {
+ fileDb.Close();
+ }
+ }
+
+ private void CreateDatabase()
+ {
+ Close();
+
+ if (File.Exists(dbPath))
+ {
+ File.Delete(dbPath);
+ }
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(dbPath));
+ }
+
+ fileDb.Create(dbPath, new Field[]
+ {
+ new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
+ new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
+ new Field(expiresField, DataTypeEnum.DateTime)
+ });
+
+ Debug.WriteLine("FileDbCache: Created database " + dbPath);
+ }
+
+ private Record GetRecordByKey(string key)
+ {
+ Record record = null;
+
+ if (fileDb.IsOpen)
+ {
+ try
+ {
+ record = fileDb.GetRecordByKey(key, new string[] { valueField, expiresField }, false);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDbCache.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
+ }
+ }
+
+ return record;
+ }
+
+ private void AddOrUpdateRecord(string key, byte[] value, DateTime expiration)
+ {
+ if (fileDb.IsOpen)
+ {
+ var fieldValues = new FieldValues(3);
+ fieldValues.Add(valueField, value);
+ fieldValues.Add(expiresField, expiration);
+
+ try
+ {
+ if (fileDb.GetRecordByKey(key, new string[0], false) != null)
+ {
+ fileDb.UpdateRecordByKey(key, fieldValues);
+ }
+ else
+ {
+ fieldValues.Add(keyField, key);
+ fileDb.AddRecord(fieldValues);
+ }
+
+ //Debug.WriteLine("FileDbCache: Writing \"{0}\", Expires {1}", key, imageCacheItem.Expiration.ToLocalTime());
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDbCache.AddOrUpdateRecord(\"{0}\"): {1}", key, ex.Message); return;
+ }
+ }
+ }
+ }
+}
diff --git a/FileDbCache/UWP/FileDbCache.UWP.cs b/FileDbCache/UWP/FileDbCache.UWP.cs
new file mode 100644
index 00000000..eac150ee
--- /dev/null
+++ b/FileDbCache/UWP/FileDbCache.UWP.cs
@@ -0,0 +1,76 @@
+// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
+// © 2019 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices.WindowsRuntime;
+using System.Threading.Tasks;
+using Windows.Storage;
+using Windows.Storage.Streams;
+using Windows.UI.Xaml;
+
+namespace MapControl.Caching
+{
+ public partial class FileDbCache : IImageCache
+ {
+ public FileDbCache(StorageFolder folder, string fileName = "TileCache.fdb")
+ {
+ if (folder == null)
+ {
+ throw new ArgumentNullException("The parameter folder must not be null.");
+ }
+
+ if (string.IsNullOrEmpty(fileName))
+ {
+ throw new ArgumentNullException("The parameter fileName must not be null.");
+ }
+
+ dbPath = Path.Combine(folder.Path, "TileCache.fdb");
+
+ Open();
+
+ Application.Current.Resuming += (s, e) => Open();
+ Application.Current.Suspending += (s, e) => Close();
+ }
+
+ public Task GetAsync(string key)
+ {
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ return Task.Run(() =>
+ {
+ var record = GetRecordByKey(key);
+
+ if (record == null)
+ {
+ return null;
+ }
+
+ return new ImageCacheItem
+ {
+ Buffer = ((byte[])record[0]).AsBuffer(),
+ Expiration = (DateTime)record[1]
+ };
+ });
+ }
+
+ public Task SetAsync(string key, IBuffer buffer, DateTime expiration)
+ {
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("The parameter buffer must not be null.");
+ }
+
+ return Task.Run(() => AddOrUpdateRecord(key, buffer.ToArray(), expiration));
+ }
+ }
+}
diff --git a/FileDbCache/UWP/FileDbCache.UWP.csproj b/FileDbCache/UWP/FileDbCache.UWP.csproj
index e80fd41a..19b99d0e 100644
--- a/FileDbCache/UWP/FileDbCache.UWP.csproj
+++ b/FileDbCache/UWP/FileDbCache.UWP.csproj
@@ -40,7 +40,10 @@
PackageReference
-
+
+ FileDbCache.cs
+
+
diff --git a/FileDbCache/UWP/FileDbCache.cs b/FileDbCache/UWP/FileDbCache.cs
deleted file mode 100644
index 34eb4be9..00000000
--- a/FileDbCache/UWP/FileDbCache.cs
+++ /dev/null
@@ -1,247 +0,0 @@
-// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
-// © 2019 Clemens Fischer
-// Licensed under the Microsoft Public License (Ms-PL)
-
-using FileDbNs;
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Runtime.InteropServices.WindowsRuntime;
-using System.Threading.Tasks;
-using Windows.Storage;
-using Windows.Storage.Streams;
-using Windows.UI.Xaml;
-
-namespace MapControl.Caching
-{
- ///
- /// IImageCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
- /// See http://www.eztools-software.com/tools/filedb/.
- ///
- public sealed class FileDbCache : IImageCache, IDisposable
- {
- private const string keyField = "Key";
- private const string valueField = "Value";
- private const string expiresField = "Expires";
-
- private readonly FileDb fileDb = new FileDb();
- private readonly string dbPath;
-
- public FileDbCache(StorageFolder folder, string fileName = "TileCache.fdb", bool autoFlush = true, int autoCleanThreshold = -1)
- {
- if (folder == null)
- {
- throw new ArgumentNullException("The parameter folder must not be null.");
- }
-
- if (string.IsNullOrEmpty(fileName))
- {
- throw new ArgumentNullException("The parameter fileName must not be null.");
- }
-
- dbPath = Path.Combine(folder.Path, "TileCache.fdb");
-
- fileDb.AutoFlush = autoFlush;
- fileDb.AutoCleanThreshold = autoCleanThreshold;
-
- Application.Current.Resuming += (s, e) => Open();
- Application.Current.Suspending += (s, e) => Close();
-
- Open();
- }
-
- public void Dispose()
- {
- Close();
- }
-
- public void Clean()
- {
- if (fileDb.IsOpen)
- {
- int deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThan));
-
- if (deleted > 0)
- {
- Debug.WriteLine("FileDbCache: Deleted {0} expired items", deleted);
- fileDb.Clean();
- }
- }
- }
-
- public Task GetAsync(string key)
- {
- if (key == null)
- {
- throw new ArgumentNullException("The parameter key must not be null.");
- }
-
- return fileDb.IsOpen ? Task.Run(() => Get(key)) : null;
- }
-
- public Task SetAsync(string key, IBuffer buffer, DateTime expiration)
- {
- if (key == null)
- {
- throw new ArgumentNullException("The parameter key must not be null.");
- }
-
- if (buffer == null)
- {
- throw new ArgumentNullException("The parameter buffer must not be null.");
- }
-
- return Task.Run(() =>
- {
- if (fileDb.IsOpen)
- {
- var bytes = buffer.ToArray();
- var ok = AddOrUpdateRecord(key, bytes, expiration);
-
- if (!ok && RepairDatabase())
- {
- AddOrUpdateRecord(key, bytes, expiration);
- }
- }
- });
- }
-
- private void Open()
- {
- if (!fileDb.IsOpen)
- {
- try
- {
- fileDb.Open(dbPath);
- Debug.WriteLine("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, dbPath);
-
- Clean();
- }
- catch
- {
- CreateDatabase();
- }
- }
- }
-
- private void Close()
- {
- if (fileDb.IsOpen)
- {
- fileDb.Close();
- }
- }
-
- private void CreateDatabase()
- {
- Close();
-
- fileDb.Create(dbPath, new Field[]
- {
- new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
- new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
- new Field(expiresField, DataTypeEnum.DateTime)
- });
-
- Debug.WriteLine("FileDbCache: Created database " + dbPath);
- }
-
- private bool RepairDatabase()
- {
- try
- {
- fileDb.Reindex();
- return true;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.Reindex(): " + ex.Message);
- }
-
- try
- {
- CreateDatabase();
- return true;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: Creating database {0}: {1}", dbPath, ex.Message);
- }
-
- return false;
- }
-
- private ImageCacheItem Get(string key)
- {
- var fields = new string[] { valueField, expiresField };
-
- try
- {
- var record = fileDb.GetRecordByKey(key, fields, false);
-
- if (record != null)
- {
- return new ImageCacheItem
- {
- Buffer = ((byte[])record[0]).AsBuffer(),
- Expiration = (DateTime)record[1]
- };
- }
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
- }
-
- return null;
- }
-
- private bool AddOrUpdateRecord(string key, byte[] value, DateTime expiration)
- {
- var fieldValues = new FieldValues(3);
- fieldValues.Add(valueField, value);
- fieldValues.Add(expiresField, expiration);
-
- bool recordExists;
-
- try
- {
- recordExists = fileDb.GetRecordByKey(key, new string[0], false) != null;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
- return false;
- }
-
- if (recordExists)
- {
- try
- {
- fileDb.UpdateRecordByKey(key, fieldValues);
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\"): {1}", key, ex.Message);
- return false;
- }
- }
- else
- {
- try
- {
- fieldValues.Add(keyField, key);
- fileDb.AddRecord(fieldValues);
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.AddRecord(\"{0}\"): {1}", key, ex.Message);
- return false;
- }
- }
-
- //Debug.WriteLine("FileDbCache: Writing \"{0}\", Expires {1}", key, expiration.ToLocalTime());
- return true;
- }
- }
-}
diff --git a/FileDbCache/WPF/FileDbCache.WPF.cs b/FileDbCache/WPF/FileDbCache.WPF.cs
new file mode 100644
index 00000000..eddc854e
--- /dev/null
+++ b/FileDbCache/WPF/FileDbCache.WPF.cs
@@ -0,0 +1,216 @@
+// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
+// © 2019 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.Caching;
+
+namespace MapControl.Caching
+{
+ public partial class FileDbCache : ObjectCache
+ {
+ public FileDbCache(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentException("The parameter path must not be null or empty.");
+ }
+
+ if (string.IsNullOrEmpty(Path.GetExtension(path)))
+ {
+ path = Path.Combine(path, "TileCache.fdb");
+ }
+
+ dbPath = path;
+
+ Open();
+
+ AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();
+ }
+
+ public override string Name
+ {
+ get { return string.Empty; }
+ }
+
+ public override DefaultCacheCapabilities DefaultCacheCapabilities
+ {
+ get { return DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; }
+ }
+
+ public override object this[string key]
+ {
+ get { return Get(key); }
+ set { Set(key, value, null); }
+ }
+
+ protected override IEnumerator> GetEnumerator()
+ {
+ throw new NotSupportedException("FileDbCache does not support the ability to enumerate items.");
+ }
+
+ public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null)
+ {
+ throw new NotSupportedException("FileDbCache does not support the ability to create change monitors.");
+ }
+
+ public override long GetCount(string regionName = null)
+ {
+ if (regionName != null)
+ {
+ throw new NotSupportedException("FileDbCache does not support named regions.");
+ }
+
+ try
+ {
+ return fileDb.NumRecords;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDbCache.GetCount(): " + ex.Message);
+ }
+
+ return 0;
+ }
+
+ public override bool Contains(string key, string regionName = null)
+ {
+ if (regionName != null)
+ {
+ throw new NotSupportedException("FileDbCache does not support named regions.");
+ }
+
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ try
+ {
+ return fileDb.GetRecordByKey(key, new string[0], false) != null;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDbCache.Contains(\"{0}\"): {1}", key, ex.Message);
+ }
+
+ return false;
+ }
+
+ public override object Get(string key, string regionName = null)
+ {
+ if (regionName != null)
+ {
+ throw new NotSupportedException("FileDbCache does not support named regions.");
+ }
+
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ var record = GetRecordByKey(key);
+
+ if (record == null)
+ {
+ return null;
+ }
+
+ return new ImageCacheItem
+ {
+ Buffer = (byte[])record[0],
+ Expiration = (DateTime)record[1]
+ };
+ }
+
+ public override CacheItem GetCacheItem(string key, string regionName = null)
+ {
+ var value = Get(key, regionName);
+
+ return value != null ? new CacheItem(key, value) : null;
+ }
+
+ public override IDictionary GetValues(IEnumerable keys, string regionName = null)
+ {
+ return keys.ToDictionary(key => key, key => Get(key, regionName));
+ }
+
+ public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
+ {
+ if (regionName != null)
+ {
+ throw new NotSupportedException("FileDbCache does not support named regions.");
+ }
+
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ var imageCacheItem = value as ImageCacheItem;
+
+ if (imageCacheItem == null || imageCacheItem.Buffer == null || imageCacheItem.Buffer.Length == 0)
+ {
+ throw new ArgumentException("The parameter value must be an ImageCacheItem with a non-empty Buffer.");
+ }
+
+ AddOrUpdateRecord(key, imageCacheItem.Buffer, imageCacheItem.Expiration);
+ }
+
+ public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
+ {
+ Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
+ }
+
+ public override void Set(CacheItem item, CacheItemPolicy policy)
+ {
+ Set(item.Key, item.Value, policy, item.RegionName);
+ }
+
+ public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
+ {
+ var oldValue = Get(key, regionName);
+
+ Set(key, value, policy);
+
+ return oldValue;
+ }
+
+ public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
+ {
+ return AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
+ }
+
+ public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
+ {
+ var oldItem = GetCacheItem(item.Key, item.RegionName);
+
+ Set(item, policy);
+
+ return oldItem;
+ }
+
+ public override object Remove(string key, string regionName = null)
+ {
+ var oldValue = Get(key, regionName);
+
+ if (oldValue != null)
+ {
+ try
+ {
+ fileDb.DeleteRecordByKey(key);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDbCache.Remove(\"{0}\"): {1}", key, ex.Message);
+ }
+ }
+
+ return oldValue;
+ }
+ }
+}
diff --git a/FileDbCache/WPF/FileDbCache.WPF.csproj b/FileDbCache/WPF/FileDbCache.WPF.csproj
index 5aa9fb32..751fe391 100644
--- a/FileDbCache/WPF/FileDbCache.WPF.csproj
+++ b/FileDbCache/WPF/FileDbCache.WPF.csproj
@@ -44,7 +44,10 @@
-
+
+ FileDbCache.cs
+
+
diff --git a/FileDbCache/WPF/FileDbCache.cs b/FileDbCache/WPF/FileDbCache.cs
deleted file mode 100644
index 117b44af..00000000
--- a/FileDbCache/WPF/FileDbCache.cs
+++ /dev/null
@@ -1,392 +0,0 @@
-// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
-// © 2019 Clemens Fischer
-// Licensed under the Microsoft Public License (Ms-PL)
-
-using FileDbNs;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.Caching;
-
-namespace MapControl.Caching
-{
- ///
- /// ObjectCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
- /// See http://www.eztools-software.com/tools/filedb/.
- ///
- public sealed class FileDbCache : ObjectCache, IDisposable
- {
- private const string keyField = "Key";
- private const string valueField = "Value";
- private const string expiresField = "Expires";
-
- private readonly FileDb fileDb = new FileDb();
- private readonly string dbPath;
-
- public FileDbCache(string path, bool autoFlush = true, int autoCleanThreshold = -1)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentException("The parameter path must not be null or empty.");
- }
-
- if (string.IsNullOrEmpty(Path.GetExtension(path)))
- {
- path = Path.Combine(path, "TileCache.fdb");
- }
-
- dbPath = path;
-
- fileDb.AutoFlush = autoFlush;
- fileDb.AutoCleanThreshold = autoCleanThreshold;
-
- try
- {
- fileDb.Open(dbPath, false);
-
- Debug.WriteLine("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, dbPath);
-
- Clean();
- }
- catch
- {
- CreateDatabase();
- }
-
- AppDomain.CurrentDomain.ProcessExit += (s, e) => Close();
- }
-
- public override string Name
- {
- get { return string.Empty; }
- }
-
- public override DefaultCacheCapabilities DefaultCacheCapabilities
- {
- get { return DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; }
- }
-
- public override object this[string key]
- {
- get { return Get(key); }
- set { Set(key, value, null); }
- }
-
- protected override IEnumerator> GetEnumerator()
- {
- throw new NotSupportedException("FileDbCache does not support the ability to enumerate items.");
- }
-
- public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null)
- {
- throw new NotSupportedException("FileDbCache does not support the ability to create change monitors.");
- }
-
- public override long GetCount(string regionName = null)
- {
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
- if (fileDb.IsOpen)
- {
- try
- {
- return fileDb.NumRecords;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.NumRecords: " + ex.Message);
- }
- }
-
- return 0;
- }
-
- public override bool Contains(string key, string regionName = null)
- {
- if (key == null)
- {
- throw new ArgumentNullException("The parameter key must not be null.");
- }
-
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
- if (fileDb.IsOpen)
- {
- try
- {
- return fileDb.GetRecordByKey(key, new string[0], false) != null;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
- }
- }
-
- return false;
- }
-
- public override object Get(string key, string regionName = null)
- {
- if (key == null)
- {
- throw new ArgumentNullException("The parameter key must not be null.");
- }
-
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
- if (fileDb.IsOpen)
- {
- try
- {
- var record = fileDb.GetRecordByKey(key, new string[] { valueField, expiresField }, false);
-
- if (record != null)
- {
- return new ImageCacheItem
- {
- Buffer = (byte[])record[0],
- Expiration = (DateTime)record[1]
- };
- }
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
- }
- }
-
- return null;
- }
-
- public override CacheItem GetCacheItem(string key, string regionName = null)
- {
- var value = Get(key, regionName);
-
- return value != null ? new CacheItem(key, value) : null;
- }
-
- public override IDictionary GetValues(IEnumerable keys, string regionName = null)
- {
- return keys.ToDictionary(key => key, key => Get(key, regionName));
- }
-
- public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
- {
- if (key == null)
- {
- throw new ArgumentNullException("The parameter key must not be null.");
- }
-
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
- var imageCacheItem = value as ImageCacheItem;
-
- if (imageCacheItem == null || imageCacheItem.Buffer == null || imageCacheItem.Buffer.Length == 0)
- {
- throw new NotSupportedException("The parameter value must be an ImageCacheItem with a non-empty Buffer.");
- }
-
- if (fileDb.IsOpen && !AddOrUpdateRecord(key, imageCacheItem) && RepairDatabase())
- {
- AddOrUpdateRecord(key, imageCacheItem);
- }
- }
-
- public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
- {
- Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
- }
-
- public override void Set(CacheItem item, CacheItemPolicy policy)
- {
- Set(item.Key, item.Value, policy, item.RegionName);
- }
-
- public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
- {
- var oldValue = Get(key, regionName);
-
- Set(key, value, policy);
-
- return oldValue;
- }
-
- public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
- {
- return AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
- }
-
- public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
- {
- var oldItem = GetCacheItem(item.Key, item.RegionName);
-
- Set(item, policy);
-
- return oldItem;
- }
-
- public override object Remove(string key, string regionName = null)
- {
- var oldValue = Get(key, regionName);
-
- if (oldValue != null)
- {
- try
- {
- fileDb.DeleteRecordByKey(key);
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.DeleteRecordByKey(\"{0}\"): {1}", key, ex.Message);
- }
- }
-
- return oldValue;
- }
-
- public void Dispose()
- {
- Close();
- }
-
- public void Flush()
- {
- if (fileDb.IsOpen)
- {
- fileDb.Flush();
- }
- }
-
- public void Clean()
- {
- if (fileDb.IsOpen)
- {
- int deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThan));
-
- if (deleted > 0)
- {
- Debug.WriteLine("FileDbCache: Deleted {0} expired items", deleted);
- fileDb.Clean();
- }
- }
- }
-
- private void Close()
- {
- if (fileDb.IsOpen)
- {
- fileDb.Close();
- }
- }
-
- private void CreateDatabase()
- {
- Close();
-
- if (File.Exists(dbPath))
- {
- File.Delete(dbPath);
- }
- else
- {
- Directory.CreateDirectory(Path.GetDirectoryName(dbPath));
- }
-
- fileDb.Create(dbPath, new Field[]
- {
- new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
- new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
- new Field(expiresField, DataTypeEnum.DateTime)
- });
-
- Debug.WriteLine("FileDbCache: Created database " + dbPath);
- }
-
- private bool RepairDatabase()
- {
- try
- {
- fileDb.Reindex();
- return true;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.Reindex(): " + ex.Message);
- }
-
- try
- {
- CreateDatabase();
- return true;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: Failed creating database {0}: {1}", dbPath, ex.Message);
- }
-
- return false;
- }
-
- private bool AddOrUpdateRecord(string key, ImageCacheItem imageCacheItem)
- {
- var fieldValues = new FieldValues(3);
- fieldValues.Add(valueField, imageCacheItem.Buffer);
- fieldValues.Add(expiresField, imageCacheItem.Expiration);
-
- bool recordExists;
-
- try
- {
- recordExists = fileDb.GetRecordByKey(key, new string[0], false) != null;
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
- return false;
- }
-
- if (recordExists)
- {
- try
- {
- fileDb.UpdateRecordByKey(key, fieldValues);
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\"): {1}", key, ex.Message);
- return false;
- }
- }
- else
- {
- try
- {
- fieldValues.Add(keyField, key);
- fileDb.AddRecord(fieldValues);
- }
- catch (Exception ex)
- {
- Debug.WriteLine("FileDbCache: FileDb.AddRecord(\"{0}\"): {1}", key, ex.Message);
- return false;
- }
- }
-
- //Debug.WriteLine("FileDbCache: Writing \"{0}\", Expires {1}", key, imageCacheItem.Expiration.ToLocalTime());
- return true;
- }
- }
-}
diff --git a/MapControl/WPF/ImageFileCache.WPF.cs b/MapControl/WPF/ImageFileCache.WPF.cs
index c860b7b2..2962e2db 100644
--- a/MapControl/WPF/ImageFileCache.WPF.cs
+++ b/MapControl/WPF/ImageFileCache.WPF.cs
@@ -72,31 +72,31 @@ namespace MapControl.Caching
public override bool Contains(string key, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("ImageFileCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
return memoryCache.Contains(key) || FindFile(key) != null;
}
public override object Get(string key, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("ImageFileCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
var imageCacheItem = memoryCache.Get(key) as ImageCacheItem;
if (imageCacheItem == null)
@@ -150,21 +150,21 @@ namespace MapControl.Caching
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("ImageFileCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
var imageCacheItem = value as ImageCacheItem;
if (imageCacheItem == null || imageCacheItem.Buffer == null || imageCacheItem.Buffer.Length == 0)
{
- throw new NotSupportedException("The parameter value must be an ImageCacheItem with a non-empty Buffer.");
+ throw new ArgumentException("The parameter value must be an ImageCacheItem with a non-empty Buffer.");
}
memoryCache.Set(key, imageCacheItem, policy);
@@ -232,16 +232,16 @@ namespace MapControl.Caching
public override object Remove(string key, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("ImageFileCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
memoryCache.Remove(key);
var path = FindFile(key);
diff --git a/SQLiteCache/Shared/SQLiteCache.cs b/SQLiteCache/Shared/SQLiteCache.cs
new file mode 100644
index 00000000..cc3abea5
--- /dev/null
+++ b/SQLiteCache/Shared/SQLiteCache.cs
@@ -0,0 +1,75 @@
+// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
+// © 2019 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+using System;
+using System.Diagnostics;
+#if WINDOWS_UWP
+using SQLiteCommand = Microsoft.Data.Sqlite.SqliteCommand;
+using SQLiteConnection = Microsoft.Data.Sqlite.SqliteConnection;
+#else
+using System.Data.SQLite;
+#endif
+
+namespace MapControl.Caching
+{
+ ///
+ /// Image cache implementation based on SqLite.
+ ///
+ public sealed partial class SQLiteCache : IDisposable
+ {
+ private readonly SQLiteConnection connection;
+
+ private static SQLiteConnection Open(string path)
+ {
+ var connection = new SQLiteConnection("Data Source=" + path);
+ connection.Open();
+
+ using (var command = new SQLiteCommand("create table if not exists items (key text primary key, expiration integer, buffer blob)", connection))
+ {
+ command.ExecuteNonQuery();
+ }
+
+ Debug.WriteLine("SQLiteCache: Opened database " + path);
+
+ return connection;
+ }
+
+ public void Dispose()
+ {
+ connection.Dispose();
+ }
+
+ public void Clean()
+ {
+ using (var command = new SQLiteCommand("delete from items where expiration < @exp", connection))
+ {
+ command.Parameters.AddWithValue("@exp", DateTime.UtcNow.Ticks);
+ command.ExecuteNonQuery();
+ }
+ }
+
+ private SQLiteCommand GetItemCommand(string key)
+ {
+ var command = new SQLiteCommand("select expiration, buffer from items where key=@key", connection);
+ command.Parameters.AddWithValue("@key", key);
+ return command;
+ }
+
+ private SQLiteCommand SetItemCommand(string key, DateTime expiration, byte[] buffer)
+ {
+ var command = new SQLiteCommand("insert or replace into items (key, expiration, buffer) values (@key, @exp, @buf)", connection);
+ command.Parameters.AddWithValue("@key", key);
+ command.Parameters.AddWithValue("@exp", expiration.Ticks);
+ command.Parameters.AddWithValue("@buf", buffer);
+ return command;
+ }
+
+ private SQLiteCommand RemoveItemCommand(string key)
+ {
+ var command = new SQLiteCommand("delete from items where key = @key", connection);
+ command.Parameters.AddWithValue("@key", key);
+ return command;
+ }
+ }
+}
diff --git a/SQLiteCache/UWP/SQLiteCache.cs b/SQLiteCache/UWP/SQLiteCache.UWP.cs
similarity index 56%
rename from SQLiteCache/UWP/SQLiteCache.cs
rename to SQLiteCache/UWP/SQLiteCache.UWP.cs
index ab66f8ac..8811b41d 100644
--- a/SQLiteCache/UWP/SQLiteCache.cs
+++ b/SQLiteCache/UWP/SQLiteCache.UWP.cs
@@ -2,7 +2,6 @@
// © 2019 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
-using Microsoft.Data.Sqlite;
using System;
using System.Diagnostics;
using System.IO;
@@ -16,10 +15,8 @@ namespace MapControl.Caching
///
/// IImageCache implementation based on SqLite.
///
- public sealed class SQLiteCache : IImageCache, IDisposable
+ public partial class SQLiteCache : IImageCache
{
- private readonly SqliteConnection connection;
-
public SQLiteCache(StorageFolder folder, string fileName = "TileCache.sqlite")
{
if (folder == null)
@@ -32,33 +29,29 @@ namespace MapControl.Caching
throw new ArgumentNullException("The parameter fileName must not be null.");
}
- connection = new SqliteConnection("Data Source=" + Path.Combine(folder.Path, fileName));
+ connection = Open(Path.Combine(folder.Path, fileName));
- connection.Open();
-
- using (var command = new SqliteCommand("create table if not exists items (key text, expiration integer, buffer blob)", connection))
- {
- command.ExecuteNonQuery();
- }
- }
-
- public void Dispose()
- {
- connection.Dispose();
+ Clean();
}
public async Task GetAsync(string key)
{
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ ImageCacheItem imageCacheItem = null;
+
try
{
- using (var command = new SqliteCommand("select expiration, buffer from items where key=@key", connection))
+ using (var command = GetItemCommand(key))
{
- command.Parameters.AddWithValue("@key", key);
var reader = await command.ExecuteReaderAsync();
- if (reader.Read())
+ if (await reader.ReadAsync())
{
- return new ImageCacheItem
+ imageCacheItem = new ImageCacheItem
{
Expiration = new DateTime((long)reader["expiration"]),
Buffer = ((byte[])reader["buffer"]).AsBuffer()
@@ -68,27 +61,36 @@ namespace MapControl.Caching
}
catch (Exception ex)
{
- Debug.WriteLine("SqLiteCache: GetAsync(\"{0}\"): {1}", key, ex.Message);
+ Debug.WriteLine("SQLiteCache.GetAsync(\"{0}\"): {1}", key, ex.Message);
}
- return null;
+ return imageCacheItem;
}
public async Task SetAsync(string key, IBuffer buffer, DateTime expiration)
{
+ if (key == null)
+ {
+ throw new ArgumentNullException("The parameter key must not be null.");
+ }
+
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("The parameter buffer must not be null.");
+ }
+
try
{
- using (var command = new SqliteCommand("insert or replace into items (key, expiration, buffer) values (@key, @exp, @buf)", connection))
+ using (var command = SetItemCommand(key, expiration, buffer.ToArray()))
{
- command.Parameters.AddWithValue("@key", key);
- command.Parameters.AddWithValue("@exp", expiration.Ticks);
- command.Parameters.AddWithValue("@buf", buffer.ToArray());
await command.ExecuteNonQueryAsync();
}
+
+ //Debug.WriteLine("SQLiteCache.SetAsync(\"{0}\"): expires {1}", key, expiration.ToLocalTime());
}
catch (Exception ex)
{
- Debug.WriteLine("SqLiteCache: SetAsync(\"{0}\"): {1}", key, ex.Message);
+ Debug.WriteLine("SQLiteCache.SetAsync(\"{0}\"): {1}", key, ex.Message);
}
}
}
diff --git a/SQLiteCache/UWP/SQLiteCache.UWP.csproj b/SQLiteCache/UWP/SQLiteCache.UWP.csproj
index f9834a3c..08be08b8 100644
--- a/SQLiteCache/UWP/SQLiteCache.UWP.csproj
+++ b/SQLiteCache/UWP/SQLiteCache.UWP.csproj
@@ -40,8 +40,11 @@
PackageReference
+
+ SQLiteCache.cs
+
-
+
diff --git a/SQLiteCache/WPF/SQLiteCache.cs b/SQLiteCache/WPF/SQLiteCache.WPF.cs
similarity index 71%
rename from SQLiteCache/WPF/SQLiteCache.cs
rename to SQLiteCache/WPF/SQLiteCache.WPF.cs
index 46e295a2..431b03cc 100644
--- a/SQLiteCache/WPF/SQLiteCache.cs
+++ b/SQLiteCache/WPF/SQLiteCache.WPF.cs
@@ -15,10 +15,8 @@ namespace MapControl.Caching
///
/// ObjectCache implementation based on SqLite.
///
- public sealed class SQLiteCache : ObjectCache, IDisposable
+ public partial class SQLiteCache : ObjectCache
{
- private readonly SQLiteConnection connection;
-
public SQLiteCache(string path)
{
if (string.IsNullOrEmpty(path))
@@ -31,14 +29,9 @@ namespace MapControl.Caching
path = Path.Combine(path, "TileCache.sqlite");
}
- connection = new SQLiteConnection("Data Source=" + Path.GetFullPath(path));
+ connection = Open(Path.GetFullPath(path));
- connection.Open();
-
- using (var command = new SQLiteCommand("create table if not exists items (key text, expiration integer, buffer blob)", connection))
- {
- command.ExecuteNonQuery();
- }
+ Clean();
}
public override string Name
@@ -59,19 +52,19 @@ namespace MapControl.Caching
protected override IEnumerator> GetEnumerator()
{
- throw new NotSupportedException("SqLiteCache does not support the ability to enumerate items.");
+ throw new NotSupportedException("SQLiteCache does not support the ability to enumerate items.");
}
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null)
{
- throw new NotSupportedException("SqLiteCache does not support the ability to create change monitors.");
+ throw new NotSupportedException("SQLiteCache does not support the ability to create change monitors.");
}
public override long GetCount(string regionName = null)
{
if (regionName != null)
{
- throw new NotSupportedException("The parameter regionName must be null.");
+ throw new NotSupportedException("SQLiteCache does not support named regions.");
}
try
@@ -83,7 +76,7 @@ namespace MapControl.Caching
}
catch (Exception ex)
{
- Debug.WriteLine("SqLiteCache: GetCount(): {0}", ex.Message);
+ Debug.WriteLine("SQLiteCache.GetCount(): {0}", ex.Message);
}
return 0;
@@ -91,28 +84,26 @@ namespace MapControl.Caching
public override bool Contains(string key, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("SQLiteCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
try
{
- using (var command = new SQLiteCommand("select expiration, buffer from items where key=@key", connection))
+ using (var command = GetItemCommand(key))
{
- command.Parameters.AddWithValue("@key", key);
-
return command.ExecuteReader().Read();
}
}
catch (Exception ex)
{
- Debug.WriteLine("SqLiteCache: Get(\"{0}\"): {1}", key, ex.Message);
+ Debug.WriteLine("SQLiteCache.Contains(\"{0}\"): {1}", key, ex.Message);
}
return false;
@@ -120,26 +111,27 @@ namespace MapControl.Caching
public override object Get(string key, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("SQLiteCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
+ ImageCacheItem imageCacheItem = null;
try
{
- using (var command = new SQLiteCommand("select expiration, buffer from items where key=@key", connection))
+ using (var command = GetItemCommand(key))
{
- command.Parameters.AddWithValue("@key", key);
var reader = command.ExecuteReader();
if (reader.Read())
{
- return new ImageCacheItem
+ imageCacheItem = new ImageCacheItem
{
Expiration = new DateTime((long)reader["expiration"]),
Buffer = (byte[])reader["buffer"]
@@ -149,10 +141,10 @@ namespace MapControl.Caching
}
catch (Exception ex)
{
- Debug.WriteLine("SqLiteCache: Get(\"{0}\"): {1}", key, ex.Message);
+ Debug.WriteLine("SQLiteCache.Get(\"{0}\"): {1}", key, ex.Message);
}
- return null;
+ return imageCacheItem;
}
public override CacheItem GetCacheItem(string key, string regionName = null)
@@ -169,36 +161,35 @@ namespace MapControl.Caching
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
{
+ if (regionName != null)
+ {
+ throw new NotSupportedException("SQLiteCache does not support named regions.");
+ }
+
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
- if (regionName != null)
- {
- throw new NotSupportedException("The parameter regionName must be null.");
- }
-
var imageCacheItem = value as ImageCacheItem;
if (imageCacheItem == null || imageCacheItem.Buffer == null || imageCacheItem.Buffer.Length == 0)
{
- throw new NotSupportedException("The parameter value must be an ImageCacheItem with a non-empty Buffer.");
+ throw new ArgumentException("The parameter value must be an ImageCacheItem with a non-empty Buffer.");
}
try
{
- using (var command = new SQLiteCommand("insert or replace into items (key, expiration, buffer) values (@key, @exp, @buf)", connection))
+ using (var command = SetItemCommand(key, imageCacheItem.Expiration, imageCacheItem.Buffer))
{
- command.Parameters.AddWithValue("@key", key);
- command.Parameters.AddWithValue("@exp", imageCacheItem.Expiration.Ticks);
- command.Parameters.AddWithValue("@buf", imageCacheItem.Buffer);
command.ExecuteNonQuery();
}
+
+ //Debug.WriteLine("SQLiteCache.Set(\"{0}\"): expires {1}", key, expiration.ToLocalTime());
}
catch (Exception ex)
{
- Debug.WriteLine("SqLiteCache: Set(\"{0}\"): {1}", key, ex.Message);
+ Debug.WriteLine("SQLiteCache.Set(\"{0}\"): {1}", key, ex.Message);
}
}
@@ -237,12 +228,24 @@ namespace MapControl.Caching
public override object Remove(string key, string regionName = null)
{
- throw new NotImplementedException();
- }
+ var oldValue = Get(key, regionName);
- public void Dispose()
- {
- connection.Dispose();
+ if (oldValue != null)
+ {
+ try
+ {
+ using (var command = RemoveItemCommand(key))
+ {
+ command.ExecuteNonQuery();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("SQLiteCache.Remove(\"{0}\"): {1}", key, ex.Message);
+ }
+ }
+
+ return oldValue;
}
}
}
diff --git a/SQLiteCache/WPF/SQLiteCache.WPF.csproj b/SQLiteCache/WPF/SQLiteCache.WPF.csproj
index 96ba137c..ab87b643 100644
--- a/SQLiteCache/WPF/SQLiteCache.WPF.csproj
+++ b/SQLiteCache/WPF/SQLiteCache.WPF.csproj
@@ -49,11 +49,14 @@
+
+ SQLiteCache.cs
+
Code
-
+