From c12e929fcc02d1338b787875635b6a6e8953dd9c Mon Sep 17 00:00:00 2001 From: ClemensFischer Date: Sat, 3 Feb 2024 20:53:32 +0100 Subject: [PATCH] Use IDistributedCache on all platforms --- Caches/FileDbCache/FileDbCache.cs | 196 ++++++++++++++ Caches/FileDbCache/FileDbCache.csproj | 10 + Caches/SQLiteCache/SQLiteCache.cs | 245 ++++++++++++++++++ Caches/SQLiteCache/SQLiteCache.csproj | 10 + FileDbCache/Shared/FileDbCache.cs | 124 --------- FileDbCache/UWP/FileDbCache.UWP.csproj | 81 ------ FileDbCache/UWP/Properties/AssemblyInfo.cs | 13 - .../UWP/Properties/FileDbCache.UWP.rd.xml | 5 - FileDbCache/WPF/FileDbCache.WPF.cs | 185 ------------- FileDbCache/WPF/FileDbCache.WPF.csproj | 38 --- FileDbCache/WinUI/FileDbCache.WinUI.cs | 32 --- FileDbCache/WinUI/FileDbCache.WinUI.csproj | 38 --- MBTiles/WPF/MBTiles.WPF.csproj | 2 +- MBTiles/WinUI/MBTiles.WinUI.csproj | 2 +- MapControl/Shared/ImageFileCache.cs | 235 ++++++++++++++--- MapControl/Shared/TileImageLoader.cs | 70 +++-- MapControl/UWP/MapControl.UWP.csproj | 6 +- MapControl/UWP/TileImageLoader.UWP.cs | 39 --- MapControl/WPF/ImageFileCache.WPF.cs | 241 ----------------- MapControl/WPF/MapControl.WPF.csproj | 13 +- MapControl/WPF/TileImageLoader.WPF.cs | 34 +-- MapControl/WinUI/ImageFileCache.WinUI.cs | 60 ----- MapControl/WinUI/MapControl.WinUI.csproj | 3 +- MapControl/WinUI/TileImageLoader.WinUI.cs | 39 --- MapControlExtended.sln | 158 +++-------- MapProjections/WPF/MapProjections.WPF.csproj | 4 +- .../WinUI/MapProjections.WinUI.csproj | 2 +- MapUiTools/WPF/MapUiTools.WPF.csproj | 2 +- MapUiTools/WinUI/MapUiTools.WinUI.csproj | 2 +- SQLiteCache/Shared/SQLiteCache.cs | 98 ------- SQLiteCache/UWP/Properties/AssemblyInfo.cs | 13 - .../UWP/Properties/SQLiteCache.UWP.rd.xml | 33 --- SQLiteCache/UWP/SQLiteCache.UWP.csproj | 81 ------ SQLiteCache/WPF/SQLiteCache.WPF.cs | 215 --------------- SQLiteCache/WPF/SQLiteCache.WPF.csproj | 38 --- SQLiteCache/WinUI/SQLiteCache.WinUI.cs | 50 ---- SQLiteCache/WinUI/SQLiteCache.WinUI.csproj | 38 --- SampleApps/WpfApplication/MainWindow.xaml.cs | 1 - .../WpfApplication/WpfApplication.csproj | 3 +- 39 files changed, 773 insertions(+), 1686 deletions(-) create mode 100644 Caches/FileDbCache/FileDbCache.cs create mode 100644 Caches/FileDbCache/FileDbCache.csproj create mode 100644 Caches/SQLiteCache/SQLiteCache.cs create mode 100644 Caches/SQLiteCache/SQLiteCache.csproj delete mode 100644 FileDbCache/Shared/FileDbCache.cs delete mode 100644 FileDbCache/UWP/FileDbCache.UWP.csproj delete mode 100644 FileDbCache/UWP/Properties/AssemblyInfo.cs delete mode 100644 FileDbCache/UWP/Properties/FileDbCache.UWP.rd.xml delete mode 100644 FileDbCache/WPF/FileDbCache.WPF.cs delete mode 100644 FileDbCache/WPF/FileDbCache.WPF.csproj delete mode 100644 FileDbCache/WinUI/FileDbCache.WinUI.cs delete mode 100644 FileDbCache/WinUI/FileDbCache.WinUI.csproj delete mode 100644 MapControl/WPF/ImageFileCache.WPF.cs delete mode 100644 MapControl/WinUI/ImageFileCache.WinUI.cs delete mode 100644 SQLiteCache/Shared/SQLiteCache.cs delete mode 100644 SQLiteCache/UWP/Properties/AssemblyInfo.cs delete mode 100644 SQLiteCache/UWP/Properties/SQLiteCache.UWP.rd.xml delete mode 100644 SQLiteCache/UWP/SQLiteCache.UWP.csproj delete mode 100644 SQLiteCache/WPF/SQLiteCache.WPF.cs delete mode 100644 SQLiteCache/WPF/SQLiteCache.WPF.csproj delete mode 100644 SQLiteCache/WinUI/SQLiteCache.WinUI.cs delete mode 100644 SQLiteCache/WinUI/SQLiteCache.WinUI.csproj diff --git a/Caches/FileDbCache/FileDbCache.cs b/Caches/FileDbCache/FileDbCache.cs new file mode 100644 index 00000000..7be602d0 --- /dev/null +++ b/Caches/FileDbCache/FileDbCache.cs @@ -0,0 +1,196 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2024 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using FileDbNs; +using Microsoft.Extensions.Caching.Distributed; +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MapControl.Caching +{ + /// + /// IDistributedCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software. + /// See http://www.eztools-software.com/tools/filedb/. + /// + public class FileDbCache : IDistributedCache, IDisposable + { + private const string keyField = "Key"; + private const string valueField = "Value"; + private const string expiresField = "Expires"; + + private readonly FileDb fileDb = new FileDb { AutoFlush = true }; + + public FileDbCache(string path) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException("The path argument must not be null or empty.", nameof(path)); + } + + if (string.IsNullOrEmpty(Path.GetExtension(path))) + { + path = Path.Combine(path, "TileCache.fdb"); + } + + try + { + fileDb.Open(path); + Debug.WriteLine($"FileDbCache: Opened database {path}"); + + Clean(); + } + catch + { + if (File.Exists(path)) + { + File.Delete(path); + } + else + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + } + + fileDb.Create(path, 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 {path}"); + } + } + + public void Dispose() + { + fileDb.Dispose(); + } + + public byte[] Get(string key) + { + byte[] value = null; + + try + { + var record = fileDb.GetRecordByKey(key, new string[] { valueField, expiresField }, false); + + if (record != null) + { + if ((DateTime)record[1] > DateTime.UtcNow) + { + value = (byte[])record[0]; + } + else + { + fileDb.DeleteRecordByKey(key); + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"FileDbCache.Get({key}): {ex.Message}"); + } + + return value; + } + + public Task GetAsync(string key, CancellationToken token = default) + { + return Task.FromResult(Get(key)); + } + + public void Set(string key, byte[] value, DistributedCacheEntryOptions options) + { + DateTime expiration; + + if (options.AbsoluteExpiration.HasValue) + { + expiration = options.AbsoluteExpiration.Value.DateTime; + } + else if (options.AbsoluteExpirationRelativeToNow.HasValue) + { + expiration = DateTime.UtcNow.Add(options.AbsoluteExpirationRelativeToNow.Value); + } + else if (options.SlidingExpiration.HasValue) + { + expiration = DateTime.UtcNow.Add(options.SlidingExpiration.Value); + } + else + { + expiration = DateTime.UtcNow.Add(TimeSpan.FromDays(1)); + } + + var fieldValues = new FieldValues(3) + { + { valueField, value ?? new byte[0] }, + { expiresField, expiration } + }; + + try + { + if (fileDb.GetRecordByKey(key, new string[0], false) != null) + { + fileDb.UpdateRecordByKey(key, fieldValues); + } + else + { + fieldValues.Add(keyField, key); + fileDb.AddRecord(fieldValues); + } + } + catch (Exception ex) + { + Debug.WriteLine($"FileDbCache.Set({key}): {ex.Message}"); + } + } + + public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default) + { + Set(key, value, options); + return Task.CompletedTask; + } + + public void Refresh(string key) + { + throw new NotSupportedException(); + } + + public Task RefreshAsync(string key, CancellationToken token = default) + { + throw new NotSupportedException(); + } + + public void Remove(string key) + { + try + { + fileDb.DeleteRecordByKey(key); + } + catch (Exception ex) + { + Debug.WriteLine($"FileDbCache.Remove({key}): {ex.Message}"); + } + } + + public Task RemoveAsync(string key, CancellationToken token = default) + { + Remove(key); + return Task.CompletedTask; + } + + public void Clean() + { + var deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThanOrEqual)); + + if (deleted > 0) + { + Debug.WriteLine($"FileDbCache: Deleted {deleted} expired items"); + fileDb.Clean(); + } + } + } +} diff --git a/Caches/FileDbCache/FileDbCache.csproj b/Caches/FileDbCache/FileDbCache.csproj new file mode 100644 index 00000000..56f24ad7 --- /dev/null +++ b/Caches/FileDbCache/FileDbCache.csproj @@ -0,0 +1,10 @@ + + + netstandard2.0 + + + + + + + diff --git a/Caches/SQLiteCache/SQLiteCache.cs b/Caches/SQLiteCache/SQLiteCache.cs new file mode 100644 index 00000000..6cfdfb82 --- /dev/null +++ b/Caches/SQLiteCache/SQLiteCache.cs @@ -0,0 +1,245 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2024 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using Microsoft.Extensions.Caching.Distributed; +using System; +using System.Data.Common; +using System.Data.SQLite; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MapControl.Caching +{ + /// + /// IDistributedCache implementation based on SQLite. + /// + public class SQLiteCache : IDistributedCache, IDisposable + { + private readonly SQLiteConnection connection; + + public SQLiteCache(string path) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException("The path argument must not be null or empty.", nameof(path)); + } + + if (string.IsNullOrEmpty(Path.GetExtension(path))) + { + path = Path.Combine(path, "TileCache.sqlite"); + } + + var connection = new SQLiteConnection("Data Source=" + Path.GetFullPath(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}"); + + Clean(); + } + + public void Dispose() + { + connection.Dispose(); + } + + public byte[] Get(string key) + { + byte[] value = null; + + try + { + using (var command = GetItemCommand(key)) + { + var reader = command.ExecuteReader(); + + if (reader.Read() && !ReadValue(reader, ref value)) + { + Remove(key); + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"SQLiteCache.Get({key}): {ex.Message}"); + } + + return value; + } + + public async Task GetAsync(string key, CancellationToken token = default) + { + byte[] value = null; + + try + { + using (var command = GetItemCommand(key)) + { + var reader = await command.ExecuteReaderAsync(token); + + if (await reader.ReadAsync() && !ReadValue(reader, ref value)) + { + await RemoveAsync(key); + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"SQLiteCache.GetAsync({key}): {ex.Message}"); + } + + return value; + } + + public void Set(string key, byte[] value, DistributedCacheEntryOptions options) + { + try + { + using (var command = SetItemCommand(key, value, options)) + { + command.ExecuteNonQuery(); + } + } + catch (Exception ex) + { + Debug.WriteLine($"SQLiteCache.Set({key}): {ex.Message}"); + } + } + + public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default) + { + try + { + using (var command = SetItemCommand(key, value, options)) + { + await command.ExecuteNonQueryAsync(token); + } + } + catch (Exception ex) + { + Debug.WriteLine($"SQLiteCache.SetAsync({key}): {ex.Message}"); + } + } + + public void Refresh(string key) + { + throw new NotSupportedException(); + } + + public Task RefreshAsync(string key, CancellationToken token = default) + { + throw new NotSupportedException(); + } + + public void Remove(string key) + { + try + { + using (var command = RemoveItemCommand(key)) + { + command.ExecuteNonQuery(); + } + } + catch (Exception ex) + { + Debug.WriteLine($"SQLiteCache.Remove({key}): {ex.Message}"); + } + } + + public async Task RemoveAsync(string key, CancellationToken token = default) + { + try + { + using (var command = RemoveItemCommand(key)) + { + await command.ExecuteNonQueryAsync(); + } + } + catch (Exception ex) + { + Debug.WriteLine($"SQLiteCache.RemoveAsync({key}): {ex.Message}"); + } + } + + public void Clean() + { + using (var command = new SQLiteCommand("delete from items where expiration < @exp", connection)) + { + command.Parameters.AddWithValue("@exp", DateTimeOffset.UtcNow.Ticks); + command.ExecuteNonQuery(); + } +#if DEBUG + using (var command = new SQLiteCommand("select changes()", connection)) + { + var deleted = (long)command.ExecuteScalar(); + if (deleted > 0) + { + Debug.WriteLine($"SQLiteCache: Deleted {deleted} expired items"); + } + } +#endif + } + + 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 RemoveItemCommand(string key) + { + var command = new SQLiteCommand("delete from items where key = @key", connection); + command.Parameters.AddWithValue("@key", key); + return command; + } + + private SQLiteCommand SetItemCommand(string key, byte[] buffer, DistributedCacheEntryOptions options) + { + DateTimeOffset expiration; + + if (options.AbsoluteExpiration.HasValue) + { + expiration = options.AbsoluteExpiration.Value; + } + else if (options.AbsoluteExpirationRelativeToNow.HasValue) + { + expiration = DateTimeOffset.UtcNow.Add(options.AbsoluteExpirationRelativeToNow.Value); + } + else if (options.SlidingExpiration.HasValue) + { + expiration = DateTimeOffset.UtcNow.Add(options.SlidingExpiration.Value); + } + else + { + expiration = DateTimeOffset.UtcNow.Add(TimeSpan.FromDays(1)); + } + + 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 ?? new byte[0]); + return command; + } + + private bool ReadValue(DbDataReader reader, ref byte[] value) + { + var expiration = new DateTimeOffset((long)reader["expiration"], TimeSpan.Zero); + + if (expiration <= DateTimeOffset.UtcNow) + { + return false; + } + + value = (byte[])reader["buffer"]; + return true; + } + } +} diff --git a/Caches/SQLiteCache/SQLiteCache.csproj b/Caches/SQLiteCache/SQLiteCache.csproj new file mode 100644 index 00000000..c4c12774 --- /dev/null +++ b/Caches/SQLiteCache/SQLiteCache.csproj @@ -0,0 +1,10 @@ + + + netstandard2.0 + + + + + + + diff --git a/FileDbCache/Shared/FileDbCache.cs b/FileDbCache/Shared/FileDbCache.cs deleted file mode 100644 index 3f0ab3e0..00000000 --- a/FileDbCache/Shared/FileDbCache.cs +++ /dev/null @@ -1,124 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 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 }; - - public FileDbCache(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentException("The path argument must not be null or empty.", nameof(path)); - } - - if (string.IsNullOrEmpty(Path.GetExtension(path))) - { - path = Path.Combine(path, "TileCache.fdb"); - } - - Open(path); - } - - public void Dispose() - { - fileDb.Dispose(); - } - - public void Clean() - { - var deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThan)); - - if (deleted > 0) - { - Debug.WriteLine($"FileDbCache: Deleted {deleted} expired items"); - fileDb.Clean(); - } - } - - private void Open(string path) - { - try - { - fileDb.Open(path); - Debug.WriteLine($"FileDbCache: Opened database {path}"); - - Clean(); - } - catch - { - if (File.Exists(path)) - { - File.Delete(path); - } - else - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - } - - fileDb.Create(path, 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 {path}"); - } - } - - private Record GetRecordByKey(string key) - { - try - { - return fileDb.GetRecordByKey(key, new string[] { valueField, expiresField }, false); - } - catch (Exception ex) - { - Debug.WriteLine($"FileDbCache.GetRecordByKey({key}): {ex.Message}"); - } - - return null; - } - - private void AddOrUpdateRecord(string key, byte[] buffer, DateTime expiration) - { - var fieldValues = new FieldValues(3); - fieldValues.Add(valueField, buffer ?? new byte[0]); - 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); - } - } - catch (Exception ex) - { - Debug.WriteLine($"FileDbCache.AddOrUpdateRecord({key}): {ex.Message}"); - } - } - } -} diff --git a/FileDbCache/UWP/FileDbCache.UWP.csproj b/FileDbCache/UWP/FileDbCache.UWP.csproj deleted file mode 100644 index dcc0871c..00000000 --- a/FileDbCache/UWP/FileDbCache.UWP.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D} - Library - Properties - MapControl.Caching - FileDbCache.UWP - en-US - UAP - 10.0.22000.0 - 10.0.17763.0 - 14 - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;UWP - prompt - 4 - - - AnyCPU - none - true - bin\Release\ - UWP - prompt - 4 - - - PackageReference - - - - FileDbCache.cs - - - FileDbCache.WinUI.cs - - - - - - - 7.4.4 - - - 6.2.14 - - - - - MapControl.snk - - - - - {9545f73c-9c35-4cf6-baae-19a0baebd344} - MapControl.UWP - - - - 14.0 - - - true - - - ..\..\MapControl.snk - - - \ No newline at end of file diff --git a/FileDbCache/UWP/Properties/AssemblyInfo.cs b/FileDbCache/UWP/Properties/AssemblyInfo.cs deleted file mode 100644 index cfb2f4a4..00000000 --- a/FileDbCache/UWP/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("XAML Map Control FileDbCache Library for UWP")] -[assembly: AssemblyProduct("XAML Map Control")] -[assembly: AssemblyCompany("Clemens Fischer")] -[assembly: AssemblyCopyright("Copyright © 2023 Clemens Fischer")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("8.3.0")] -[assembly: AssemblyFileVersion("8.3.0")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] diff --git a/FileDbCache/UWP/Properties/FileDbCache.UWP.rd.xml b/FileDbCache/UWP/Properties/FileDbCache.UWP.rd.xml deleted file mode 100644 index 32fbaacb..00000000 --- a/FileDbCache/UWP/Properties/FileDbCache.UWP.rd.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/FileDbCache/WPF/FileDbCache.WPF.cs b/FileDbCache/WPF/FileDbCache.WPF.cs deleted file mode 100644 index aff9304a..00000000 --- a/FileDbCache/WPF/FileDbCache.WPF.cs +++ /dev/null @@ -1,185 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Caching; - -namespace MapControl.Caching -{ - public partial class FileDbCache : ObjectCache - { - public override string Name => string.Empty; - - public override DefaultCacheCapabilities DefaultCacheCapabilities => - DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; - - public override object this[string key] - { - get => 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(nameof(key)); - } - - try - { - return fileDb.GetRecordByKey(key, Array.Empty(), false) != null; - } - catch (Exception ex) - { - Debug.WriteLine($"FileDbCache.Contains({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(nameof(key)); - } - - var record = GetRecordByKey(key); - - if (record == null) - { - return null; - } - - return Tuple.Create((byte[])record[0], (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(nameof(key)); - } - - if (!(value is Tuple cacheItem)) - { - throw new ArgumentException("The value argument must be a Tuple.", nameof(value)); - } - - AddOrUpdateRecord(key, cacheItem.Item1, cacheItem.Item2); - } - - 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({key}): {ex.Message}"); - } - } - - return oldValue; - } - } -} diff --git a/FileDbCache/WPF/FileDbCache.WPF.csproj b/FileDbCache/WPF/FileDbCache.WPF.csproj deleted file mode 100644 index 6b46a7c9..00000000 --- a/FileDbCache/WPF/FileDbCache.WPF.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net8.0-windows;net7.0-windows;net6.0-windows;net48;net462 - true - MapControl.Caching - XAML Map Control FileDbCache Library for WPF - XAML Map Control - 8.3.0 - Clemens Fischer - Copyright © 2023 Clemens Fischer - true - ..\..\MapControl.snk - false - false - XAML.MapControl.FileDbCache - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FileDbCache/WinUI/FileDbCache.WinUI.cs b/FileDbCache/WinUI/FileDbCache.WinUI.cs deleted file mode 100644 index 6cbbc59b..00000000 --- a/FileDbCache/WinUI/FileDbCache.WinUI.cs +++ /dev/null @@ -1,32 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Threading.Tasks; - -namespace MapControl.Caching -{ - public partial class FileDbCache : IImageCache - { - public Task> GetAsync(string key) - { - return Task.Run(() => - { - var record = GetRecordByKey(key); - - if (record == null) - { - return null; - } - - return Tuple.Create((byte[])record[0], (DateTime)record[1]); - }); - } - - public Task SetAsync(string key, byte[] buffer, DateTime expiration) - { - return Task.Run(() => AddOrUpdateRecord(key, buffer, expiration)); - } - } -} diff --git a/FileDbCache/WinUI/FileDbCache.WinUI.csproj b/FileDbCache/WinUI/FileDbCache.WinUI.csproj deleted file mode 100644 index 64c29a9d..00000000 --- a/FileDbCache/WinUI/FileDbCache.WinUI.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0 - win10-x86;win10-x64;win10-arm64 - true - true - MapControl.Caching - XAML Map Control FileDbCache Library for WinUI - XAML Map Control - 8.3.0 - Clemens Fischer - Copyright © 2023 Clemens Fischer - true - ..\..\MapControl.snk - false - false - XAML.MapControl.FileDbCache - WINUI - - - - - - - - - - - - - - - - - - - - diff --git a/MBTiles/WPF/MBTiles.WPF.csproj b/MBTiles/WPF/MBTiles.WPF.csproj index 5f82dd8c..846b76c7 100644 --- a/MBTiles/WPF/MBTiles.WPF.csproj +++ b/MBTiles/WPF/MBTiles.WPF.csproj @@ -1,6 +1,6 @@  - net7.0-windows;net6.0-windows;net48;net462 + net6.0-windows;net462 true MapControl.MBTiles XAML Map Control MBTiles Library for WPF diff --git a/MBTiles/WinUI/MBTiles.WinUI.csproj b/MBTiles/WinUI/MBTiles.WinUI.csproj index 852fa8fa..242dcfc5 100644 --- a/MBTiles/WinUI/MBTiles.WinUI.csproj +++ b/MBTiles/WinUI/MBTiles.WinUI.csproj @@ -1,6 +1,6 @@  - net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0 + net6.0-windows10.0.17763.0 win10-x86;win10-x64;win10-arm64 true true diff --git a/MapControl/Shared/ImageFileCache.cs b/MapControl/Shared/ImageFileCache.cs index a3f86afd..270da7c8 100644 --- a/MapControl/Shared/ImageFileCache.cs +++ b/MapControl/Shared/ImageFileCache.cs @@ -1,21 +1,22 @@ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer +// Copyright © 2024 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using Microsoft.Extensions.Caching.Distributed; using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace MapControl.Caching { /// - /// Image Cache implementation based on local image files. - /// The only valid data type for cached values is Tuple. + /// IDistributedCache implementation based on local image files. /// - public partial class ImageFileCache + public class ImageFileCache : IDistributedCache { private const string expiresTag = "EXPIRES:"; @@ -38,6 +39,155 @@ namespace MapControl.Caching return Task.Factory.StartNew(CleanRootDirectory, TaskCreationOptions.LongRunning); } + public byte[] Get(string key) + { + byte[] buffer = null; + var path = GetPath(key); + + try + { + if (path != null && File.Exists(path)) + { + buffer = File.ReadAllBytes(path); + + CheckExpiration(path, ref buffer); + } + } + catch (Exception ex) + { + Debug.WriteLine($"ImageFileCache: Failed reading {path}: {ex.Message}"); + } + + return buffer; + } + + public async Task GetAsync(string key, CancellationToken token = default) + { + byte[] buffer = null; + var path = GetPath(key); + + try + { + if (path != null && File.Exists(path)) + { +#if NETFRAMEWORK + using (var stream = File.OpenRead(path)) + { + buffer = new byte[stream.Length]; + var offset = 0; + while (offset < buffer.Length) + { + offset += await stream.ReadAsync(buffer, offset, buffer.Length - offset, token).ConfigureAwait(false); + } + } +#else + buffer = await File.ReadAllBytesAsync(path, token).ConfigureAwait(false); +#endif + CheckExpiration(path, ref buffer); + } + } + catch (Exception ex) + { + Debug.WriteLine($"ImageFileCache: Failed reading {path}: {ex.Message}"); + } + + return buffer; + } + + public void Set(string key, byte[] buffer, DistributedCacheEntryOptions options) + { + var path = GetPath(key); + + if (path != null && buffer != null && buffer.Length > 0) + { + try + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + using (var stream = File.Create(path)) + { + stream.Write(buffer, 0, buffer.Length); + + var expiration = GetExpiration(options); + + if (expiration.HasValue) + { + stream.Write(Encoding.ASCII.GetBytes(expiresTag), 0, 8); + stream.Write(BitConverter.GetBytes(expiration.Value.Ticks), 0, 8); + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"ImageFileCache: Failed writing {path}: {ex.Message}"); + } + } + } + + public async Task SetAsync(string key, byte[] buffer, DistributedCacheEntryOptions options, CancellationToken token = default) + { + var path = GetPath(key); + + if (path != null && buffer != null && buffer.Length > 0) + { + try + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + using (var stream = File.Create(path)) + { + await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + + var expiration = GetExpiration(options); + + if (expiration.HasValue) + { + await stream.WriteAsync(Encoding.ASCII.GetBytes(expiresTag), 0, 8).ConfigureAwait(false); + await stream.WriteAsync(BitConverter.GetBytes(expiration.Value.Ticks), 0, 8).ConfigureAwait(false); + } + } + } + catch (Exception ex) + { + Debug.WriteLine($"ImageFileCache: Failed writing {path}: {ex.Message}"); + } + } + } + + public void Refresh(string key) + { + throw new NotSupportedException(); + } + + public Task RefreshAsync(string key, CancellationToken token = default) + { + throw new NotSupportedException(); + } + + public void Remove(string key) + { + var path = GetPath(key); + + try + { + if (path != null && File.Exists(path)) + { + File.Delete(path); + } + } + catch (Exception ex) + { + Debug.WriteLine($"ImageFileCache: Failed deleting {path}: {ex.Message}"); + } + + } + + public Task RemoveAsync(string key, CancellationToken token = default) + { + Remove(key); + return Task.CompletedTask; + } + private string GetPath(string key) { try @@ -101,7 +251,9 @@ namespace MapControl.Caching try { - if (ReadExpiration(file) < DateTime.UtcNow) + var expiration = ReadExpiration(file); + + if (expiration.HasValue && expiration.Value <= DateTimeOffset.UtcNow) { file.Delete(); deletedFileCount = 1; @@ -115,9 +267,40 @@ namespace MapControl.Caching return deletedFileCount; } - private static DateTime ReadExpiration(FileInfo file) + private static DateTimeOffset? GetExpiration(DistributedCacheEntryOptions options) { - DateTime? expiration = null; + DateTimeOffset? expiration = null; + + if (options.AbsoluteExpiration.HasValue) + { + expiration = options.AbsoluteExpiration.Value; + } + else if (options.AbsoluteExpirationRelativeToNow.HasValue) + { + expiration = DateTimeOffset.UtcNow.Add(options.AbsoluteExpirationRelativeToNow.Value); + } + else if (options.SlidingExpiration.HasValue) + { + expiration = DateTimeOffset.UtcNow.Add(options.SlidingExpiration.Value); + } + + return expiration; + } + + private static void CheckExpiration(string path, ref byte[] buffer) + { + var expiration = ReadExpiration(ref buffer); + + if (expiration.HasValue && expiration.Value <= DateTimeOffset.UtcNow) + { + File.Delete(path); + buffer = null; + } + } + + private static DateTimeOffset? ReadExpiration(FileInfo file) + { + DateTimeOffset? expiration = null; if (file.Length > 16) { @@ -134,46 +317,32 @@ namespace MapControl.Caching } } - return expiration ?? DateTime.Today; + return expiration; } - private static DateTime ReadExpiration(ref byte[] buffer) + private static DateTimeOffset? ReadExpiration(ref byte[] buffer) { - DateTime? expiration = ReadExpiration(buffer); + var expiration = ReadExpiration(buffer); if (expiration.HasValue) { Array.Resize(ref buffer, buffer.Length - 16); - - return expiration.Value; - } - - return DateTime.Today; - } - - private static DateTime? ReadExpiration(byte[] buffer) - { - DateTime? expiration = null; - - if (buffer.Length >= 16 && - Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag) - { - expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc); } return expiration; } - private static void WriteExpiration(Stream stream, DateTime expiration) + private static DateTimeOffset? ReadExpiration(byte[] buffer) { - stream.Write(Encoding.ASCII.GetBytes(expiresTag), 0, 8); - stream.Write(BitConverter.GetBytes(expiration.Ticks), 0, 8); - } + DateTimeOffset? expiration = null; - private static async Task WriteExpirationAsync(Stream stream, DateTime expiration) - { - await stream.WriteAsync(Encoding.ASCII.GetBytes(expiresTag), 0, 8); - await stream.WriteAsync(BitConverter.GetBytes(expiration.Ticks), 0, 8); + if (buffer.Length >= 16 && + Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag) + { + expiration = new DateTimeOffset(BitConverter.ToInt64(buffer, buffer.Length - 8), TimeSpan.Zero); + } + + return expiration; } } } diff --git a/MapControl/Shared/TileImageLoader.cs b/MapControl/Shared/TileImageLoader.cs index 31bee072..819d0265 100644 --- a/MapControl/Shared/TileImageLoader.cs +++ b/MapControl/Shared/TileImageLoader.cs @@ -2,6 +2,7 @@ // Copyright © 2023 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using Microsoft.Extensions.Caching.Distributed; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -39,9 +40,9 @@ namespace MapControl } /// - /// Maximum number of parallel tile loading tasks. The default value is 4. + /// An IDistributedCache implementation used to cache tile images. /// - public static int MaxLoadTasks { get; set; } = 4; + public static IDistributedCache Cache { get; set; } /// /// Default expiration time for cached tile images. Used when no expiration time @@ -55,6 +56,12 @@ namespace MapControl /// public static TimeSpan MaxCacheExpiration { get; set; } = TimeSpan.FromDays(10); + /// + /// Maximum number of parallel tile loading tasks. The default value is 4. + /// + public static int MaxLoadTasks { get; set; } = 4; + + private TileQueue pendingTiles; /// @@ -125,34 +132,55 @@ namespace MapControl if (uri != null) { - var extension = Path.GetExtension(uri.LocalPath); - - if (string.IsNullOrEmpty(extension) || extension == ".jpeg") - { - extension = ".jpg"; - } - - var cacheKey = string.Format(CultureInfo.InvariantCulture, - "{0}/{1}/{2}/{3}{4}", cacheName, tile.ZoomLevel, tile.Column, tile.Row, extension); - - return LoadCachedTileAsync(tile, uri, cacheKey); + return LoadCachedTileAsync(tile, uri, cacheName); } return Task.CompletedTask; } - private static DateTime GetExpiration(TimeSpan? maxAge) + private static async Task LoadCachedTileAsync(Tile tile, Uri uri, string cacheName) { - if (!maxAge.HasValue) + var extension = Path.GetExtension(uri.LocalPath); + + if (string.IsNullOrEmpty(extension) || extension == ".jpeg") { - maxAge = DefaultCacheExpiration; - } - else if (maxAge.Value > MaxCacheExpiration) - { - maxAge = MaxCacheExpiration; + extension = ".jpg"; } - return DateTime.UtcNow.Add(maxAge.Value); + var cacheKey = string.Format(CultureInfo.InvariantCulture, + "{0}/{1}/{2}/{3}{4}", cacheName, tile.ZoomLevel, tile.Column, tile.Row, extension); + + var buffer = await Cache.GetAsync(cacheKey).ConfigureAwait(false); + + if (buffer == null) + { + var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false); + + if (response != null) // download succeeded + { + buffer = response.Buffer ?? Array.Empty(); // may be empty when no tile available, but still be cached + + var maxAge = response.MaxAge ?? DefaultCacheExpiration; + + if (maxAge > MaxCacheExpiration) + { + maxAge = MaxCacheExpiration; + } + + var cacheOptions = new DistributedCacheEntryOptions + { + AbsoluteExpiration = DateTimeOffset.UtcNow.Add(maxAge) + }; + + await Cache.SetAsync(cacheKey, buffer, cacheOptions).ConfigureAwait(false); + } + } + //else System.Diagnostics.Debug.WriteLine($"Cached: {cacheKey}"); + + if (buffer != null && buffer.Length > 0) + { + await LoadTileAsync(tile, () => ImageLoader.LoadImageAsync(buffer)).ConfigureAwait(false); + } } } } diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index 3dda270e..0a9ce60c 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -233,9 +233,6 @@ GeoImage.WinUI.cs - - ImageFileCache.WinUI.cs - ImageLoader.WinUI.cs @@ -286,6 +283,9 @@ + + 8.0.0 + 6.2.14 diff --git a/MapControl/UWP/TileImageLoader.UWP.cs b/MapControl/UWP/TileImageLoader.UWP.cs index cbff898a..e042464d 100644 --- a/MapControl/UWP/TileImageLoader.UWP.cs +++ b/MapControl/UWP/TileImageLoader.UWP.cs @@ -9,16 +9,6 @@ using Windows.UI.Xaml.Media; namespace MapControl { - namespace Caching - { - public interface IImageCache - { - Task> GetAsync(string key); - - Task SetAsync(string key, byte[] buffer, DateTime expiration); - } - } - public partial class TileImageLoader { /// @@ -27,35 +17,6 @@ namespace MapControl /// public static string DefaultCacheFolder => Windows.Storage.ApplicationData.Current.TemporaryFolder.Path; - /// - /// An IImageCache implementation used to cache tile images. - /// - public static Caching.IImageCache Cache { get; set; } - - - private static async Task LoadCachedTileAsync(Tile tile, Uri uri, string cacheKey) - { - var cacheItem = await Cache.GetAsync(cacheKey).ConfigureAwait(false); - var buffer = cacheItem?.Item1; - - if (cacheItem == null || cacheItem.Item2 < DateTime.UtcNow) - { - var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false); - - if (response != null) // download succeeded - { - buffer = response.Buffer; // may be null or empty when no tile available, but still be cached - - await Cache.SetAsync(cacheKey, buffer, GetExpiration(response.MaxAge)).ConfigureAwait(false); - } - } - //else System.Diagnostics.Debug.WriteLine($"Cached: {cacheKey}"); - - if (buffer != null && buffer.Length > 0) - { - await LoadTileAsync(tile, () => ImageLoader.LoadImageAsync(buffer)).ConfigureAwait(false); - } - } private static async Task LoadTileAsync(Tile tile, Func> loadImageFunc) { diff --git a/MapControl/WPF/ImageFileCache.WPF.cs b/MapControl/WPF/ImageFileCache.WPF.cs deleted file mode 100644 index 9b5ff2fb..00000000 --- a/MapControl/WPF/ImageFileCache.WPF.cs +++ /dev/null @@ -1,241 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 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; -using System.Security.AccessControl; -using System.Security.Principal; - -namespace MapControl.Caching -{ - public partial class ImageFileCache : ObjectCache - { - private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule( - new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), - FileSystemRights.FullControl, AccessControlType.Allow); - - private readonly MemoryCache memoryCache = MemoryCache.Default; - - public override string Name => string.Empty; - - public override DefaultCacheCapabilities DefaultCacheCapabilities => DefaultCacheCapabilities.None; - - public override object this[string key] - { - get => Get(key); - set => Set(key, value, null); - } - - protected override IEnumerator> GetEnumerator() - { - throw new NotSupportedException("ImageFileCache does not support the ability to enumerate items."); - } - - public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null) - { - throw new NotSupportedException("ImageFileCache does not support the ability to create change monitors."); - } - - public override long GetCount(string regionName = null) - { - throw new NotSupportedException("ImageFileCache does not support the ability to count items."); - } - - 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(nameof(key)); - } - - if (memoryCache.Contains(key)) - { - return true; - } - - var path = GetPath(key); - - try - { - return path != null && File.Exists(path); - } - catch (Exception ex) - { - Debug.WriteLine($"ImageFileCache: Failed finding {path}: {ex.Message}"); - } - - return false; - } - - 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(nameof(key)); - } - - var cacheItem = memoryCache.Get(key) as Tuple; - - if (cacheItem == null) - { - var path = GetPath(key); - - try - { - if (path != null && File.Exists(path)) - { - var buffer = File.ReadAllBytes(path); - var expiration = ReadExpiration(ref buffer); - - cacheItem = new Tuple(buffer, expiration); - - memoryCache.Set(key, cacheItem, new CacheItemPolicy { AbsoluteExpiration = expiration }); - } - } - catch (Exception ex) - { - Debug.WriteLine($"ImageFileCache: Failed reading {path}: {ex.Message}"); - } - } - - return cacheItem; - } - - 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("ImageFileCache does not support named regions."); - } - - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (!(value is Tuple cacheItem)) - { - throw new ArgumentException("The value argument must be a Tuple.", nameof(value)); - } - - memoryCache.Set(key, cacheItem, policy); - - var buffer = cacheItem.Item1; - var path = GetPath(key); - - if (buffer != null && buffer.Length > 0 && path != null) - { - try - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - using (var stream = File.Create(path)) - { - stream.Write(buffer, 0, buffer.Length); - WriteExpiration(stream, cacheItem.Item2); - } - - var fileInfo = new FileInfo(path); - var fileSecurity = fileInfo.GetAccessControl(); - fileSecurity.AddAccessRule(fullControlRule); - fileInfo.SetAccessControl(fileSecurity); - } - catch (Exception ex) - { - Debug.WriteLine($"ImageFileCache: Failed writing {path}: {ex.Message}"); - } - } - } - - 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) - { - if (regionName != null) - { - throw new NotSupportedException("ImageFileCache does not support named regions."); - } - - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - memoryCache.Remove(key); - - var path = GetPath(key); - - try - { - if (path != null && File.Exists(path)) - { - File.Delete(path); - } - } - catch (Exception ex) - { - Debug.WriteLine($"ImageFileCache: Failed removing {path}: {ex.Message}"); - } - - return null; - } - } -} diff --git a/MapControl/WPF/MapControl.WPF.csproj b/MapControl/WPF/MapControl.WPF.csproj index 5be9a50f..965fa2e3 100644 --- a/MapControl/WPF/MapControl.WPF.csproj +++ b/MapControl/WPF/MapControl.WPF.csproj @@ -1,6 +1,6 @@  - net8.0-windows;net7.0-windows;net6.0-windows;net48;net462 + net6.0-windows;net462 true MapControl XAML Map Control Library for WPF @@ -24,13 +24,12 @@ - - - - - + - + + + + diff --git a/MapControl/WPF/TileImageLoader.WPF.cs b/MapControl/WPF/TileImageLoader.WPF.cs index 3ac99767..c17b3839 100644 --- a/MapControl/WPF/TileImageLoader.WPF.cs +++ b/MapControl/WPF/TileImageLoader.WPF.cs @@ -4,7 +4,6 @@ using System; using System.IO; -using System.Runtime.Caching; using System.Threading.Tasks; using System.Windows.Media; @@ -13,42 +12,11 @@ namespace MapControl public partial class TileImageLoader { /// - /// Default folder path where an ObjectCache instance may save cached data, i.e. C:\ProgramData\MapControl\TileCache + /// Default folder path where an IImageCache instance may save cached data, i.e. C:\ProgramData\MapControl\TileCache /// public static string DefaultCacheFolder => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl", "TileCache"); - /// - /// An ObjectCache instance used to cache tile image data. The default value is MemoryCache.Default. - /// - public static ObjectCache Cache { get; set; } = MemoryCache.Default; - - - private static async Task LoadCachedTileAsync(Tile tile, Uri uri, string cacheKey) - { - var cacheItem = Cache.Get(cacheKey) as Tuple; - var buffer = cacheItem?.Item1; - - if (cacheItem == null || cacheItem.Item2 < DateTime.UtcNow) - { - var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false); - - if (response != null) // download succeeded - { - buffer = response.Buffer; // may be null or empty when no tile available, but still be cached - - cacheItem = Tuple.Create(buffer, GetExpiration(response.MaxAge)); - - Cache.Set(cacheKey, cacheItem, new CacheItemPolicy { AbsoluteExpiration = cacheItem.Item2 }); - } - } - //else System.Diagnostics.Debug.WriteLine($"Cached: {cacheKey}"); - - if (buffer != null && buffer.Length > 0) - { - await LoadTileAsync(tile, () => ImageLoader.LoadImageAsync(buffer)).ConfigureAwait(false); - } - } private static async Task LoadTileAsync(Tile tile, Func> loadImageFunc) { diff --git a/MapControl/WinUI/ImageFileCache.WinUI.cs b/MapControl/WinUI/ImageFileCache.WinUI.cs deleted file mode 100644 index 57d0283e..00000000 --- a/MapControl/WinUI/ImageFileCache.WinUI.cs +++ /dev/null @@ -1,60 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - -namespace MapControl.Caching -{ - public partial class ImageFileCache : IImageCache - { - public async Task> GetAsync(string key) - { - Tuple cacheItem = null; - var path = GetPath(key); - - try - { - if (path != null && File.Exists(path)) - { - var buffer = await File.ReadAllBytesAsync(path); - var expiration = ReadExpiration(ref buffer); - - cacheItem = Tuple.Create(buffer, expiration); - } - } - catch (Exception ex) - { - Debug.WriteLine($"ImageFileCache: Failed reading {path}: {ex.Message}"); - } - - return cacheItem; - } - - public async Task SetAsync(string key, byte[] buffer, DateTime expiration) - { - var path = GetPath(key); - - if (buffer != null && buffer.Length > 0 && path != null) - { - try - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - using (var stream = File.Create(path)) - { - await stream.WriteAsync(buffer, 0, buffer.Length); - await WriteExpirationAsync(stream, expiration); - } - } - catch (Exception ex) - { - Debug.WriteLine($"ImageFileCache: Failed writing {path}: {ex.Message}"); - } - } - } - } -} diff --git a/MapControl/WinUI/MapControl.WinUI.csproj b/MapControl/WinUI/MapControl.WinUI.csproj index 935a1b2c..c9becc1e 100644 --- a/MapControl/WinUI/MapControl.WinUI.csproj +++ b/MapControl/WinUI/MapControl.WinUI.csproj @@ -1,6 +1,6 @@  - net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0 + net6.0-windows10.0.17763.0 win10-x86;win10-x64;win10-arm64 true true @@ -29,5 +29,6 @@ + diff --git a/MapControl/WinUI/TileImageLoader.WinUI.cs b/MapControl/WinUI/TileImageLoader.WinUI.cs index 91edf88a..b494c5e6 100644 --- a/MapControl/WinUI/TileImageLoader.WinUI.cs +++ b/MapControl/WinUI/TileImageLoader.WinUI.cs @@ -10,16 +10,6 @@ using System.Threading.Tasks; namespace MapControl { - namespace Caching - { - public interface IImageCache - { - Task> GetAsync(string key); - - Task SetAsync(string key, byte[] buffer, DateTime expiration); - } - } - public partial class TileImageLoader { /// @@ -28,35 +18,6 @@ namespace MapControl public static string DefaultCacheFolder => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl", "TileCache"); - /// - /// An IImageCache implementation used to cache tile images. - /// - public static Caching.IImageCache Cache { get; set; } - - - private static async Task LoadCachedTileAsync(Tile tile, Uri uri, string cacheKey) - { - var cacheItem = await Cache.GetAsync(cacheKey).ConfigureAwait(false); - var buffer = cacheItem?.Item1; - - if (cacheItem == null || cacheItem.Item2 < DateTime.UtcNow) - { - var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false); - - if (response != null) // download succeeded - { - buffer = response.Buffer; // may be null or empty when no tile available, but still be cached - - await Cache.SetAsync(cacheKey, buffer, GetExpiration(response.MaxAge)).ConfigureAwait(false); - } - } - //else System.Diagnostics.Debug.WriteLine($"Cached: {cacheKey}"); - - if (buffer != null && buffer.Length > 0) - { - await LoadTileAsync(tile, () => ImageLoader.LoadImageAsync(buffer)).ConfigureAwait(false); - } - } private static Task LoadTileAsync(Tile tile, Func> loadImageFunc) { diff --git a/MapControlExtended.sln b/MapControlExtended.sln index f4663b54..5f48a774 100644 --- a/MapControlExtended.sln +++ b/MapControlExtended.sln @@ -15,12 +15,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MBTiles", "MBTiles", "{CEAD EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBTiles.WPF", "MBTiles\WPF\MBTiles.WPF.csproj", "{38B18AB6-6E70-4696-8FB4-E8C8E12BF50C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FileDbCache", "FileDbCache", "{261905DE-9653-4567-B498-1F46BEA2A4F3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDbCache.WPF", "FileDbCache\WPF\FileDbCache.WPF.csproj", "{AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache.UWP", "FileDbCache\UWP\FileDbCache.UWP.csproj", "{BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.UWP", "MapControl\UWP\MapControl.UWP.csproj", "{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBTiles.UWP", "MBTiles\UWP\MBTiles.UWP.csproj", "{DCC111E9-EC8B-492A-A09D-DF390D83AE8D}" @@ -33,18 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfApplication", "SampleApp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapProjections.UWP", "MapProjections\UWP\MapProjections.UWP.csproj", "{9EE69591-5EDC-45E3-893E-2F9A4B82D538}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SQLiteCache", "SQLiteCache", "{96FD1258-1597-48A2-8D64-1ADAE13E886A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteCache.UWP", "SQLiteCache\UWP\SQLiteCache.UWP.csproj", "{56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLiteCache.WPF", "SQLiteCache\WPF\SQLiteCache.WPF.csproj", "{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapControl.WinUI", "MapControl\WinUI\MapControl.WinUI.csproj", "{ACA8E56C-0F82-4010-A83E-2DBFF5D16919}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDbCache.WinUI", "FileDbCache\WinUI\FileDbCache.WinUI.csproj", "{DFE09FD5-530D-48AB-8A46-4611F21BBBC3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLiteCache.WinUI", "SQLiteCache\WinUI\SQLiteCache.WinUI.csproj", "{E33FC359-F713-462C-8A8E-7EEA15E36BE1}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapProjections.WinUI", "MapProjections\WinUI\MapProjections.WinUI.csproj", "{3572F71A-83FE-459D-8370-002CA28827FE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBTiles.WinUI", "MBTiles\WinUI\MBTiles.WinUI.csproj", "{817D606F-A22D-485C-89CF-86062C8E97EF}" @@ -61,6 +45,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapUiTools.WinUI", "MapUiTo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapUiTools.WPF", "MapUiTools\WPF\MapUiTools.WPF.csproj", "{12430DAE-DC53-4C37-95D5-B8923B5FD3D7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caches", "Caches", "{69E6CD1A-5619-4549-95FF-2FD126F1A5D2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache", "Caches\FileDbCache\FileDbCache.csproj", "{E5A7A66A-36EC-4775-850A-A64253DF0383}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteCache", "Caches\SQLiteCache\SQLiteCache.csproj", "{FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,38 +119,6 @@ Global {38B18AB6-6E70-4696-8FB4-E8C8E12BF50C}.Release|x64.Build.0 = Release|Any CPU {38B18AB6-6E70-4696-8FB4-E8C8E12BF50C}.Release|x86.ActiveCfg = Release|Any CPU {38B18AB6-6E70-4696-8FB4-E8C8E12BF50C}.Release|x86.Build.0 = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|arm64.ActiveCfg = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|arm64.Build.0 = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|x64.ActiveCfg = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|x64.Build.0 = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|x86.ActiveCfg = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Debug|x86.Build.0 = Debug|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|Any CPU.Build.0 = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|arm64.ActiveCfg = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|arm64.Build.0 = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|x64.ActiveCfg = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|x64.Build.0 = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|x86.ActiveCfg = Release|Any CPU - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}.Release|x86.Build.0 = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|arm64.ActiveCfg = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|arm64.Build.0 = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|x64.ActiveCfg = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|x64.Build.0 = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|x86.ActiveCfg = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Debug|x86.Build.0 = Debug|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|Any CPU.Build.0 = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|arm64.ActiveCfg = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|arm64.Build.0 = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|x64.ActiveCfg = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|x64.Build.0 = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|x86.ActiveCfg = Release|Any CPU - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}.Release|x86.Build.0 = Release|Any CPU {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Debug|Any CPU.Build.0 = Debug|Any CPU {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -241,38 +199,6 @@ Global {9EE69591-5EDC-45E3-893E-2F9A4B82D538}.Release|x64.Build.0 = Release|Any CPU {9EE69591-5EDC-45E3-893E-2F9A4B82D538}.Release|x86.ActiveCfg = Release|Any CPU {9EE69591-5EDC-45E3-893E-2F9A4B82D538}.Release|x86.Build.0 = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|arm64.ActiveCfg = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|arm64.Build.0 = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|x64.ActiveCfg = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|x64.Build.0 = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|x86.ActiveCfg = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Debug|x86.Build.0 = Debug|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|Any CPU.Build.0 = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|arm64.ActiveCfg = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|arm64.Build.0 = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|x64.ActiveCfg = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|x64.Build.0 = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|x86.ActiveCfg = Release|Any CPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}.Release|x86.Build.0 = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|arm64.ActiveCfg = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|arm64.Build.0 = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|x64.ActiveCfg = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|x64.Build.0 = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|x86.ActiveCfg = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Debug|x86.Build.0 = Debug|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|Any CPU.Build.0 = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|arm64.ActiveCfg = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|arm64.Build.0 = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x64.ActiveCfg = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x64.Build.0 = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x86.ActiveCfg = Release|Any CPU - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x86.Build.0 = Release|Any CPU {ACA8E56C-0F82-4010-A83E-2DBFF5D16919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ACA8E56C-0F82-4010-A83E-2DBFF5D16919}.Debug|Any CPU.Build.0 = Debug|Any CPU {ACA8E56C-0F82-4010-A83E-2DBFF5D16919}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -289,38 +215,6 @@ Global {ACA8E56C-0F82-4010-A83E-2DBFF5D16919}.Release|x64.Build.0 = Release|Any CPU {ACA8E56C-0F82-4010-A83E-2DBFF5D16919}.Release|x86.ActiveCfg = Release|Any CPU {ACA8E56C-0F82-4010-A83E-2DBFF5D16919}.Release|x86.Build.0 = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|arm64.ActiveCfg = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|arm64.Build.0 = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|x64.ActiveCfg = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|x64.Build.0 = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|x86.ActiveCfg = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Debug|x86.Build.0 = Debug|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|Any CPU.Build.0 = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|arm64.ActiveCfg = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|arm64.Build.0 = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|x64.ActiveCfg = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|x64.Build.0 = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|x86.ActiveCfg = Release|Any CPU - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3}.Release|x86.Build.0 = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|arm64.ActiveCfg = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|arm64.Build.0 = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|x64.ActiveCfg = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|x64.Build.0 = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|x86.ActiveCfg = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Debug|x86.Build.0 = Debug|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|Any CPU.Build.0 = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|arm64.ActiveCfg = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|arm64.Build.0 = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|x64.ActiveCfg = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|x64.Build.0 = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|x86.ActiveCfg = Release|Any CPU - {E33FC359-F713-462C-8A8E-7EEA15E36BE1}.Release|x86.Build.0 = Release|Any CPU {3572F71A-83FE-459D-8370-002CA28827FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3572F71A-83FE-459D-8370-002CA28827FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {3572F71A-83FE-459D-8370-002CA28827FE}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -441,6 +335,38 @@ Global {12430DAE-DC53-4C37-95D5-B8923B5FD3D7}.Release|x64.Build.0 = Release|Any CPU {12430DAE-DC53-4C37-95D5-B8923B5FD3D7}.Release|x86.ActiveCfg = Release|Any CPU {12430DAE-DC53-4C37-95D5-B8923B5FD3D7}.Release|x86.Build.0 = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|arm64.ActiveCfg = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|arm64.Build.0 = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|x64.Build.0 = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Debug|x86.Build.0 = Debug|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|Any CPU.Build.0 = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|arm64.ActiveCfg = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|arm64.Build.0 = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|x64.ActiveCfg = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|x64.Build.0 = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|x86.ActiveCfg = Release|Any CPU + {E5A7A66A-36EC-4775-850A-A64253DF0383}.Release|x86.Build.0 = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|arm64.ActiveCfg = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|arm64.Build.0 = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|x64.Build.0 = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Debug|x86.Build.0 = Debug|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|Any CPU.Build.0 = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|arm64.ActiveCfg = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|arm64.Build.0 = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|x64.ActiveCfg = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|x64.Build.0 = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|x86.ActiveCfg = Release|Any CPU + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -449,18 +375,12 @@ Global {A204A102-C745-4D65-AEC8-7B96FAEDEF2D} = {52AECE49-F314-4F76-98F2-FA800F07824B} {AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC} {38B18AB6-6E70-4696-8FB4-E8C8E12BF50C} = {CEAD0EA1-A971-4F5F-9EAE-C72F75D1F737} - {AD1CB53E-7AA4-4EC0-B901-B4E0E2665133} = {261905DE-9653-4567-B498-1F46BEA2A4F3} - {BEEB142A-5FA3-468D-810A-32A4A5BD6D5D} = {261905DE-9653-4567-B498-1F46BEA2A4F3} {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344} = {52AECE49-F314-4F76-98F2-FA800F07824B} {DCC111E9-EC8B-492A-A09D-DF390D83AE8D} = {CEAD0EA1-A971-4F5F-9EAE-C72F75D1F737} {426C21C0-5F14-491F-BCD1-6D2993510420} = {7BC11E28-8D3B-4C5B-AC08-AB249CC95F6D} {F92DA93D-75DB-4308-A5F9-6B4C3908A675} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC} {9EE69591-5EDC-45E3-893E-2F9A4B82D538} = {7BC11E28-8D3B-4C5B-AC08-AB249CC95F6D} - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1} = {96FD1258-1597-48A2-8D64-1ADAE13E886A} - {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349} = {96FD1258-1597-48A2-8D64-1ADAE13E886A} {ACA8E56C-0F82-4010-A83E-2DBFF5D16919} = {52AECE49-F314-4F76-98F2-FA800F07824B} - {DFE09FD5-530D-48AB-8A46-4611F21BBBC3} = {261905DE-9653-4567-B498-1F46BEA2A4F3} - {E33FC359-F713-462C-8A8E-7EEA15E36BE1} = {96FD1258-1597-48A2-8D64-1ADAE13E886A} {3572F71A-83FE-459D-8370-002CA28827FE} = {7BC11E28-8D3B-4C5B-AC08-AB249CC95F6D} {817D606F-A22D-485C-89CF-86062C8E97EF} = {CEAD0EA1-A971-4F5F-9EAE-C72F75D1F737} {751EF297-7CF4-4879-BA8F-42661FA68668} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC} @@ -468,6 +388,8 @@ Global {DFFE8E49-AA07-457E-A459-99326B44F828} = {90C681E9-12AE-4B5F-932D-7EF5D35D8436} {C412209E-D81D-4ACB-BECD-FEEF52B93468} = {90C681E9-12AE-4B5F-932D-7EF5D35D8436} {12430DAE-DC53-4C37-95D5-B8923B5FD3D7} = {90C681E9-12AE-4B5F-932D-7EF5D35D8436} + {E5A7A66A-36EC-4775-850A-A64253DF0383} = {69E6CD1A-5619-4549-95FF-2FD126F1A5D2} + {FDD70FB5-3B6D-43DF-8C2E-04100315C8BC} = {69E6CD1A-5619-4549-95FF-2FD126F1A5D2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {458346DD-B23F-4FDC-8F9D-A10F1882A4DB} diff --git a/MapProjections/WPF/MapProjections.WPF.csproj b/MapProjections/WPF/MapProjections.WPF.csproj index acee5d35..d88efb94 100644 --- a/MapProjections/WPF/MapProjections.WPF.csproj +++ b/MapProjections/WPF/MapProjections.WPF.csproj @@ -1,6 +1,6 @@  - net8.0-windows;net7.0-windows;net6.0-windows;net48;net462 + net6.0-windows;net462 true MapControl.Projections XAML Map Control Projections Library for WPF @@ -24,7 +24,7 @@ - + diff --git a/MapProjections/WinUI/MapProjections.WinUI.csproj b/MapProjections/WinUI/MapProjections.WinUI.csproj index 19923426..8785c637 100644 --- a/MapProjections/WinUI/MapProjections.WinUI.csproj +++ b/MapProjections/WinUI/MapProjections.WinUI.csproj @@ -1,6 +1,6 @@  - net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0 + net6.0-windows10.0.17763.0 win10-x86;win10-x64;win10-arm64 true true diff --git a/MapUiTools/WPF/MapUiTools.WPF.csproj b/MapUiTools/WPF/MapUiTools.WPF.csproj index 59fed734..f8abc6a3 100644 --- a/MapUiTools/WPF/MapUiTools.WPF.csproj +++ b/MapUiTools/WPF/MapUiTools.WPF.csproj @@ -1,6 +1,6 @@ - net8.0-windows;net7.0-windows;net6.0-windows;net48;net462 + net6.0-windows;net462 true MapControl.UiTools XAML Map Control UI Tools Library for WPF diff --git a/MapUiTools/WinUI/MapUiTools.WinUI.csproj b/MapUiTools/WinUI/MapUiTools.WinUI.csproj index d8dc9d54..2200deec 100644 --- a/MapUiTools/WinUI/MapUiTools.WinUI.csproj +++ b/MapUiTools/WinUI/MapUiTools.WinUI.csproj @@ -1,6 +1,6 @@  - net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0 + net6.0-windows10.0.17763.0 win10-x86;win10-x64;win10-arm64 true true diff --git a/SQLiteCache/Shared/SQLiteCache.cs b/SQLiteCache/Shared/SQLiteCache.cs deleted file mode 100644 index e7371819..00000000 --- a/SQLiteCache/Shared/SQLiteCache.cs +++ /dev/null @@ -1,98 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Data.SQLite; -using System.Diagnostics; -using System.IO; - -namespace MapControl.Caching -{ - /// - /// Image cache implementation based on SqLite. - /// - public sealed partial class SQLiteCache : IDisposable - { - private readonly SQLiteConnection connection; - - public SQLiteCache(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentException("The path argument must not be null or empty.", nameof(path)); - } - - if (string.IsNullOrEmpty(Path.GetExtension(path))) - { - path = Path.Combine(path, "TileCache.sqlite"); - } - - connection = Open(Path.GetFullPath(path)); - - Clean(); - } - - 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(); - } -#if DEBUG - using (var command = new SQLiteCommand("select changes()", connection)) - { - var deleted = (long)command.ExecuteScalar(); - if (deleted > 0) - { - Debug.WriteLine($"SQLiteCache: Deleted {deleted} expired items"); - } - } -#endif - } - - private SQLiteCommand RemoveItemCommand(string key) - { - var command = new SQLiteCommand("delete from items where key = @key", connection); - command.Parameters.AddWithValue("@key", key); - return command; - } - - 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, byte[] buffer, DateTime expiration) - { - 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 ?? new byte[0]); - return command; - } - } -} diff --git a/SQLiteCache/UWP/Properties/AssemblyInfo.cs b/SQLiteCache/UWP/Properties/AssemblyInfo.cs deleted file mode 100644 index f44b7f5c..00000000 --- a/SQLiteCache/UWP/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("XAML Map Control SQLiteCache Library for UWP")] -[assembly: AssemblyProduct("XAML Map Control")] -[assembly: AssemblyCompany("Clemens Fischer")] -[assembly: AssemblyCopyright("Copyright © 2023 Clemens Fischer")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("8.3.0")] -[assembly: AssemblyFileVersion("8.3.0")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] diff --git a/SQLiteCache/UWP/Properties/SQLiteCache.UWP.rd.xml b/SQLiteCache/UWP/Properties/SQLiteCache.UWP.rd.xml deleted file mode 100644 index efe89ca3..00000000 --- a/SQLiteCache/UWP/Properties/SQLiteCache.UWP.rd.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - diff --git a/SQLiteCache/UWP/SQLiteCache.UWP.csproj b/SQLiteCache/UWP/SQLiteCache.UWP.csproj deleted file mode 100644 index f8834207..00000000 --- a/SQLiteCache/UWP/SQLiteCache.UWP.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1} - Library - Properties - MapControl.Caching - SQLiteCache.UWP - en-US - UAP - 10.0.22000.0 - 10.0.17763.0 - 14 - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;UWP - prompt - 4 - - - AnyCPU - none - true - bin\Release\ - UWP - prompt - 4 - - - PackageReference - - - - SQLiteCache.cs - - - SQLiteCache.WinUI.cs - - - - - - - 6.2.14 - - - 1.0.118 - - - - - MapControl.snk - - - - - {9545f73c-9c35-4cf6-baae-19a0baebd344} - MapControl.UWP - - - - 14.0 - - - true - - - ..\..\MapControl.snk - - - \ No newline at end of file diff --git a/SQLiteCache/WPF/SQLiteCache.WPF.cs b/SQLiteCache/WPF/SQLiteCache.WPF.cs deleted file mode 100644 index 75a9e5d5..00000000 --- a/SQLiteCache/WPF/SQLiteCache.WPF.cs +++ /dev/null @@ -1,215 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Collections.Generic; -using System.Data.SQLite; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Caching; - -namespace MapControl.Caching -{ - public partial class SQLiteCache : ObjectCache - { - public override string Name => string.Empty; - - public override DefaultCacheCapabilities DefaultCacheCapabilities => - DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; - - public override object this[string key] - { - get => Get(key); - set => Set(key, value, null); - } - - protected override IEnumerator> GetEnumerator() - { - 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."); - } - - public override long GetCount(string regionName = null) - { - if (regionName != null) - { - throw new NotSupportedException("SQLiteCache does not support named regions."); - } - - try - { - using (var command = new SQLiteCommand("select count(*) from items", connection)) - { - return (long)command.ExecuteScalar(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.GetCount(): {ex.Message}"); - } - - return 0; - } - - 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(nameof(key)); - } - - try - { - using (var command = GetItemCommand(key)) - { - return command.ExecuteReader().Read(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.Contains({key}): {ex.Message}"); - } - - return false; - } - - 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(nameof(key)); - } - - try - { - using (var command = GetItemCommand(key)) - { - var reader = command.ExecuteReader(); - - if (reader.Read()) - { - return Tuple.Create((byte[])reader["buffer"], new DateTime((long)reader["expiration"])); - } - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.Get({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 (regionName != null) - { - throw new NotSupportedException("SQLiteCache does not support named regions."); - } - - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (!(value is Tuple cacheItem)) - { - throw new ArgumentException("The value argument must be a Tuple.", nameof(value)); - } - - try - { - using (var command = SetItemCommand(key, cacheItem.Item1, cacheItem.Item2)) - { - command.ExecuteNonQuery(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.Set({key}): {ex.Message}"); - } - } - - 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 - { - using (var command = RemoveItemCommand(key)) - { - command.ExecuteNonQuery(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.Remove({key}): {ex.Message}"); - } - } - - return oldValue; - } - } -} diff --git a/SQLiteCache/WPF/SQLiteCache.WPF.csproj b/SQLiteCache/WPF/SQLiteCache.WPF.csproj deleted file mode 100644 index 9409abbd..00000000 --- a/SQLiteCache/WPF/SQLiteCache.WPF.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net8.0-windows;net7.0-windows;net6.0-windows;net48;net462 - true - MapControl.Caching - XAML Map Control SQLiteCache Library for WPF - XAML Map Control - 8.3.0 - Clemens Fischer - Copyright © 2023 Clemens Fischer - true - ..\..\MapControl.snk - false - false - XAML.MapControl.SQLiteCache - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SQLiteCache/WinUI/SQLiteCache.WinUI.cs b/SQLiteCache/WinUI/SQLiteCache.WinUI.cs deleted file mode 100644 index 713a1259..00000000 --- a/SQLiteCache/WinUI/SQLiteCache.WinUI.cs +++ /dev/null @@ -1,50 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2023 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Diagnostics; -using System.Threading.Tasks; - -namespace MapControl.Caching -{ - public partial class SQLiteCache : IImageCache - { - public async Task> GetAsync(string key) - { - try - { - using (var command = GetItemCommand(key)) - { - var reader = await command.ExecuteReaderAsync(); - - if (await reader.ReadAsync()) - { - return Tuple.Create((byte[])reader["buffer"], new DateTime((long)reader["expiration"])); - } - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.GetAsync({key}): {ex.Message}"); - } - - return null; - } - - public async Task SetAsync(string key, byte[] buffer, DateTime expiration) - { - try - { - using (var command = SetItemCommand(key, buffer, expiration)) - { - await command.ExecuteNonQueryAsync(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"SQLiteCache.SetAsync({key}): {ex.Message}"); - } - } - } -} diff --git a/SQLiteCache/WinUI/SQLiteCache.WinUI.csproj b/SQLiteCache/WinUI/SQLiteCache.WinUI.csproj deleted file mode 100644 index 21afc96b..00000000 --- a/SQLiteCache/WinUI/SQLiteCache.WinUI.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0 - win10-x86;win10-x64;win10-arm64 - true - true - MapControl.Caching - XAML Map Control SQLiteCache Library for WinUI - XAML Map Control - 8.3.0 - Clemens Fischer - Copyright © 2023 Clemens Fischer - true - ..\..\MapControl.snk - false - false - XAML.MapControl.SQLiteCache - WINUI - - - - - - - - - - - - - - - - - - - - diff --git a/SampleApps/WpfApplication/MainWindow.xaml.cs b/SampleApps/WpfApplication/MainWindow.xaml.cs index 04da4435..c166a75a 100644 --- a/SampleApps/WpfApplication/MainWindow.xaml.cs +++ b/SampleApps/WpfApplication/MainWindow.xaml.cs @@ -19,7 +19,6 @@ namespace SampleApplication TileImageLoader.Cache = new ImageFileCache(TileImageLoader.DefaultCacheFolder); //TileImageLoader.Cache = new FileDbCache(TileImageLoader.DefaultCacheFolder); //TileImageLoader.Cache = new SQLiteCache(TileImageLoader.DefaultCacheFolder); - //TileImageLoader.Cache = null; var bingMapsApiKeyPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl", "BingMapsApiKey.txt"); diff --git a/SampleApps/WpfApplication/WpfApplication.csproj b/SampleApps/WpfApplication/WpfApplication.csproj index 658b33b4..7e890be7 100644 --- a/SampleApps/WpfApplication/WpfApplication.csproj +++ b/SampleApps/WpfApplication/WpfApplication.csproj @@ -21,6 +21,8 @@ + + @@ -36,6 +38,5 @@ -