mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Use IDistributedCache on all platforms
This commit is contained in:
parent
16115413d8
commit
c12e929fcc
196
Caches/FileDbCache/FileDbCache.cs
Normal file
196
Caches/FileDbCache/FileDbCache.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// IDistributedCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
|
||||
/// See http://www.eztools-software.com/tools/filedb/.
|
||||
/// </summary>
|
||||
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<byte[]> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Caches/FileDbCache/FileDbCache.csproj
Normal file
10
Caches/FileDbCache/FileDbCache.csproj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="FileDb.Standard" Version="7.4.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
245
Caches/SQLiteCache/SQLiteCache.cs
Normal file
245
Caches/SQLiteCache/SQLiteCache.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// IDistributedCache implementation based on SQLite.
|
||||
/// </summary>
|
||||
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<byte[]> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Caches/SQLiteCache/SQLiteCache.csproj
Normal file
10
Caches/SQLiteCache/SQLiteCache.csproj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Image cache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
|
||||
/// See http://www.eztools-software.com/tools/filedb/.
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyName>FileDbCache.UWP</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22000.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\FileDbCache.cs">
|
||||
<Link>FileDbCache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WinUI\FileDbCache.WinUI.cs">
|
||||
<Link>FileDbCache.WinUI.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Properties\FileDbCache.UWP.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FileDb.Standard">
|
||||
<Version>7.4.4</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.2.14</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MapControl\UWP\MapControl.UWP.csproj">
|
||||
<Project>{9545f73c-9c35-4cf6-baae-19a0baebd344}</Project>
|
||||
<Name>MapControl.UWP</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
</Project>
|
||||
|
|
@ -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)]
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="FileDbCache.UWP">
|
||||
</Library>
|
||||
</Directives>
|
||||
|
|
@ -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<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException("FileDbCache does not support the ability to enumerate items.");
|
||||
}
|
||||
|
||||
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> 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<string>(), 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<string, object> GetValues(IEnumerable<string> 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<byte[], DateTime> cacheItem))
|
||||
{
|
||||
throw new ArgumentException("The value argument must be a Tuple<byte[], DateTime>.", 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows;net7.0-windows;net6.0-windows;net48;net462</TargetFrameworks>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control FileDbCache Library for WPF</AssemblyTitle>
|
||||
<Product>XAML Map Control</Product>
|
||||
<Version>8.3.0</Version>
|
||||
<Authors>Clemens Fischer</Authors>
|
||||
<Copyright>Copyright © 2023 Clemens Fischer</Copyright>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<PackageId>XAML.MapControl.FileDbCache</PackageId>
|
||||
<DefineConstants></DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework.EndsWith('windows'))">
|
||||
<PackageReference Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`2468`))'=='net'">
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FileDb.Standard" Version="7.4.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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<Tuple<byte[], DateTime>> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control FileDbCache Library for WinUI</AssemblyTitle>
|
||||
<Product>XAML Map Control</Product>
|
||||
<Version>8.3.0</Version>
|
||||
<Authors>Clemens Fischer</Authors>
|
||||
<Copyright>Copyright © 2023 Clemens Fischer</Copyright>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<PackageId>XAML.MapControl.FileDbCache</PackageId>
|
||||
<DefineConstants>WINUI</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FileDb.Standard" Version="7.4.4" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231219000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MapControl\WinUI\MapControl.WinUI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0-windows;net6.0-windows;net48;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0-windows;net462</TargetFrameworks>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>MapControl.MBTiles</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control MBTiles Library for WPF</AssemblyTitle>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0</TargetFrameworks>
|
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Image Cache implementation based on local image files.
|
||||
/// The only valid data type for cached values is Tuple<byte[], DateTime>.
|
||||
/// IDistributedCache implementation based on local image files.
|
||||
/// </summary>
|
||||
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<byte[]> 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;
|
||||
|
||||
if (buffer.Length >= 16 &&
|
||||
Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag)
|
||||
{
|
||||
expiration = new DateTimeOffset(BitConverter.ToInt64(buffer, buffer.Length - 8), TimeSpan.Zero);
|
||||
}
|
||||
|
||||
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);
|
||||
return expiration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of parallel tile loading tasks. The default value is 4.
|
||||
/// An IDistributedCache implementation used to cache tile images.
|
||||
/// </summary>
|
||||
public static int MaxLoadTasks { get; set; } = 4;
|
||||
public static IDistributedCache Cache { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default expiration time for cached tile images. Used when no expiration time
|
||||
|
|
@ -55,6 +56,12 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public static TimeSpan MaxCacheExpiration { get; set; } = TimeSpan.FromDays(10);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of parallel tile loading tasks. The default value is 4.
|
||||
/// </summary>
|
||||
public static int MaxLoadTasks { get; set; } = 4;
|
||||
|
||||
|
||||
private TileQueue pendingTiles;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -124,6 +131,14 @@ namespace MapControl
|
|||
var uri = tileSource.GetUri(tile.Column, tile.Row, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
return LoadCachedTileAsync(tile, uri, cacheName);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async Task LoadCachedTileAsync(Tile tile, Uri uri, string cacheName)
|
||||
{
|
||||
var extension = Path.GetExtension(uri.LocalPath);
|
||||
|
||||
|
|
@ -135,24 +150,37 @@ namespace MapControl
|
|||
var cacheKey = string.Format(CultureInfo.InvariantCulture,
|
||||
"{0}/{1}/{2}/{3}{4}", cacheName, tile.ZoomLevel, tile.Column, tile.Row, extension);
|
||||
|
||||
return LoadCachedTileAsync(tile, uri, cacheKey);
|
||||
}
|
||||
var buffer = await Cache.GetAsync(cacheKey).ConfigureAwait(false);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (buffer == null)
|
||||
{
|
||||
var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false);
|
||||
|
||||
private static DateTime GetExpiration(TimeSpan? maxAge)
|
||||
if (response != null) // download succeeded
|
||||
{
|
||||
if (!maxAge.HasValue)
|
||||
{
|
||||
maxAge = DefaultCacheExpiration;
|
||||
}
|
||||
else if (maxAge.Value > MaxCacheExpiration)
|
||||
buffer = response.Buffer ?? Array.Empty<byte>(); // may be empty when no tile available, but still be cached
|
||||
|
||||
var maxAge = response.MaxAge ?? DefaultCacheExpiration;
|
||||
|
||||
if (maxAge > MaxCacheExpiration)
|
||||
{
|
||||
maxAge = MaxCacheExpiration;
|
||||
}
|
||||
|
||||
return DateTime.UtcNow.Add(maxAge.Value);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,9 +233,6 @@
|
|||
<Compile Include="..\WinUI\GeoImage.WinUI.cs">
|
||||
<Link>GeoImage.WinUI.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WinUI\ImageFileCache.WinUI.cs">
|
||||
<Link>ImageFileCache.WinUI.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WinUI\ImageLoader.WinUI.cs">
|
||||
<Link>ImageLoader.WinUI.cs</Link>
|
||||
</Compile>
|
||||
|
|
@ -286,6 +283,9 @@
|
|||
<EmbeddedResource Include="Properties\MapControl.UWP.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions">
|
||||
<Version>8.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.2.14</Version>
|
||||
</PackageReference>
|
||||
|
|
|
|||
|
|
@ -9,16 +9,6 @@ using Windows.UI.Xaml.Media;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
namespace Caching
|
||||
{
|
||||
public interface IImageCache
|
||||
{
|
||||
Task<Tuple<byte[], DateTime>> GetAsync(string key);
|
||||
|
||||
Task SetAsync(string key, byte[] buffer, DateTime expiration);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class TileImageLoader
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -27,35 +17,6 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public static string DefaultCacheFolder => Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
|
||||
|
||||
/// <summary>
|
||||
/// An IImageCache implementation used to cache tile images.
|
||||
/// </summary>
|
||||
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<Task<ImageSource>> loadImageFunc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException("ImageFileCache does not support the ability to enumerate items.");
|
||||
}
|
||||
|
||||
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> 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<byte[], DateTime>;
|
||||
|
||||
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<byte[], DateTime>(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<string, object> GetValues(IEnumerable<string> 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<byte[], DateTime> cacheItem))
|
||||
{
|
||||
throw new ArgumentException("The value argument must be a Tuple<byte[], DateTime>.", 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows;net7.0-windows;net6.0-windows;net48;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0-windows;net462</TargetFrameworks>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control Library for WPF</AssemblyTitle>
|
||||
|
|
@ -24,13 +24,12 @@
|
|||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework.EndsWith('windows'))">
|
||||
<PackageReference Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`2468`))'=='net'">
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public static string DefaultCacheFolder =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl", "TileCache");
|
||||
|
||||
/// <summary>
|
||||
/// An ObjectCache instance used to cache tile image data. The default value is MemoryCache.Default.
|
||||
/// </summary>
|
||||
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<byte[], DateTime>;
|
||||
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<Task<ImageSource>> loadImageFunc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<Tuple<byte[], DateTime>> GetAsync(string key)
|
||||
{
|
||||
Tuple<byte[], DateTime> 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0</TargetFrameworks>
|
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
|
|
@ -29,5 +29,6 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231219000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -10,16 +10,6 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
namespace Caching
|
||||
{
|
||||
public interface IImageCache
|
||||
{
|
||||
Task<Tuple<byte[], DateTime>> GetAsync(string key);
|
||||
|
||||
Task SetAsync(string key, byte[] buffer, DateTime expiration);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class TileImageLoader
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -28,35 +18,6 @@ namespace MapControl
|
|||
public static string DefaultCacheFolder =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl", "TileCache");
|
||||
|
||||
/// <summary>
|
||||
/// An IImageCache implementation used to cache tile images.
|
||||
/// </summary>
|
||||
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<Task<ImageSource>> loadImageFunc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows;net7.0-windows;net6.0-windows;net48;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0-windows;net462</TargetFrameworks>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>MapControl.Projections</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control Projections Library for WPF</AssemblyTitle>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`2468`))'=='net'">
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0</TargetFrameworks>
|
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows;net7.0-windows;net6.0-windows;net48;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0-windows;net462</TargetFrameworks>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>MapControl.UiTools</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control UI Tools Library for WPF</AssemblyTitle>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0</TargetFrameworks>
|
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Image cache implementation based on SqLite.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)]
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file contains Runtime Directives, specifications about types your application accesses
|
||||
through reflection and other dynamic code patterns. Runtime Directives are used to control the
|
||||
.NET Native optimizer and ensure that it does not remove code accessed by your library. If your
|
||||
library does not do any reflection, then you generally do not need to edit this file. However,
|
||||
if your library reflects over types, especially types passed to it or derived from its types,
|
||||
then you should write Runtime Directives.
|
||||
|
||||
The most common use of reflection in libraries is to discover information about types passed
|
||||
to the library. Runtime Directives have three ways to express requirements on types passed to
|
||||
your library.
|
||||
|
||||
1. Parameter, GenericParameter, TypeParameter, TypeEnumerableParameter
|
||||
Use these directives to reflect over types passed as a parameter.
|
||||
|
||||
2. SubTypes
|
||||
Use a SubTypes directive to reflect over types derived from another type.
|
||||
|
||||
3. AttributeImplies
|
||||
Use an AttributeImplies directive to indicate that your library needs to reflect over
|
||||
types or methods decorated with an attribute.
|
||||
|
||||
For more information on writing Runtime Directives for libraries, please visit
|
||||
https://go.microsoft.com/fwlink/?LinkID=391919
|
||||
-->
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="SQLiteCache.UWP">
|
||||
|
||||
<!-- add directives for your library here -->
|
||||
|
||||
</Library>
|
||||
</Directives>
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyName>SQLiteCache.UWP</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22000.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\SQLiteCache.cs">
|
||||
<Link>SQLiteCache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WinUI\SQLiteCache.WinUI.cs">
|
||||
<Link>SQLiteCache.WinUI.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Properties\SQLiteCache.UWP.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.2.14</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Data.SQLite.Core">
|
||||
<Version>1.0.118</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MapControl\UWP\MapControl.UWP.csproj">
|
||||
<Project>{9545f73c-9c35-4cf6-baae-19a0baebd344}</Project>
|
||||
<Name>MapControl.UWP</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
</Project>
|
||||
|
|
@ -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<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException("SQLiteCache does not support the ability to enumerate items.");
|
||||
}
|
||||
|
||||
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> 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<string, object> GetValues(IEnumerable<string> 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<byte[], DateTime> cacheItem))
|
||||
{
|
||||
throw new ArgumentException("The value argument must be a Tuple<byte[], DateTime>.", 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows;net7.0-windows;net6.0-windows;net48;net462</TargetFrameworks>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control SQLiteCache Library for WPF</AssemblyTitle>
|
||||
<Product>XAML Map Control</Product>
|
||||
<Version>8.3.0</Version>
|
||||
<Authors>Clemens Fischer</Authors>
|
||||
<Copyright>Copyright © 2023 Clemens Fischer</Copyright>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<PackageId>XAML.MapControl.SQLiteCache</PackageId>
|
||||
<DefineConstants></DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework.EndsWith('windows'))">
|
||||
<PackageReference Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`2468`))'=='net'">
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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<Tuple<byte[], DateTime>> 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0-windows10.0.17763.0;net7.0-windows10.0.17763.0;net6.0-windows10.0.17763.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyTitle>XAML Map Control SQLiteCache Library for WinUI</AssemblyTitle>
|
||||
<Product>XAML Map Control</Product>
|
||||
<Version>8.3.0</Version>
|
||||
<Authors>Clemens Fischer</Authors>
|
||||
<Copyright>Copyright © 2023 Clemens Fischer</Copyright>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<PackageId>XAML.MapControl.SQLiteCache</PackageId>
|
||||
<DefineConstants>WINUI</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231219000" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MapControl\WinUI\MapControl.WinUI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Caches\FileDbCache\FileDbCache.csproj" />
|
||||
<ProjectReference Include="..\..\Caches\SQLiteCache\SQLiteCache.csproj" />
|
||||
<ProjectReference Include="..\..\MapControl\WPF\MapControl.WPF.csproj" />
|
||||
<ProjectReference Include="..\..\MapUiTools\WPF\MapUiTools.WPF.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
@ -36,6 +38,5 @@
|
|||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
Loading…
Reference in a new issue