diff --git a/Caches/FileDbCache/FileDbCache.cs b/Caches/FileDbCache/FileDbCache.cs index 51889f96..d2c5ae4a 100644 --- a/Caches/FileDbCache/FileDbCache.cs +++ b/Caches/FileDbCache/FileDbCache.cs @@ -1,13 +1,23 @@ using FileDbNs; using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; -using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; namespace MapControl.Caching { + public class FileDbCacheOptions : IOptions + { + public FileDbCacheOptions Value => this; + + public string Path { get; set; } + + public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromHours(1); + } + /// /// IDistributedCache implementation based on FileDb, https://github.com/eztools-software/FileDb. /// @@ -19,14 +29,27 @@ namespace MapControl.Caching private readonly FileDb fileDb = new FileDb { AutoFlush = true }; private readonly Timer timer; + private readonly ILogger logger; - public FileDbCache(string path) - : this(path, TimeSpan.FromHours(1)) + public FileDbCache(string path, ILoggerFactory loggerFactory = null) + : this(new FileDbCacheOptions { Path = path }, loggerFactory) { } - public FileDbCache(string path, TimeSpan expirationScanFrequency) + public FileDbCache(IOptions optionsAccessor, ILoggerFactory loggerFactory = null) + : this(optionsAccessor.Value, loggerFactory) { + } + + public FileDbCache(FileDbCacheOptions options, ILoggerFactory loggerFactory = null) + { + if (loggerFactory != null) + { + logger = loggerFactory.CreateLogger(); + } + + var path = options.Path; + if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(Path.GetExtension(path))) { path = Path.Combine(path ?? "", "TileCache.fdb"); @@ -36,7 +59,7 @@ namespace MapControl.Caching { fileDb.Open(path); - Debug.WriteLine($"{nameof(FileDbCache)}: Opened database {path}"); + logger?.LogInformation("Opened database {path}", path); } catch { @@ -56,12 +79,12 @@ namespace MapControl.Caching new Field(expiresField, DataTypeEnum.DateTime) }); - Debug.WriteLine($"{nameof(FileDbCache)}: Created database {path}"); + logger?.LogInformation("Created database {path}", path); } - if (expirationScanFrequency > TimeSpan.Zero) + if (options.ExpirationScanFrequency > TimeSpan.Zero) { - timer = new Timer(_ => DeleteExpiredItems(), null, TimeSpan.Zero, expirationScanFrequency); + timer = new Timer(_ => DeleteExpiredItems(), null, TimeSpan.Zero, options.ExpirationScanFrequency); } } @@ -88,7 +111,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(FileDbCache)}.Get({key}): {ex.Message}"); + logger?.LogError(ex, "Get({key})", key); } } @@ -128,7 +151,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(FileDbCache)}.Set({key}): {ex.Message}"); + logger?.LogError(ex, "Set({key})", key); } } } @@ -159,7 +182,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(FileDbCache)}.Remove({key}): {ex.Message}"); + logger?.LogError(ex, "Remove({key})", key); } } } @@ -173,13 +196,13 @@ namespace MapControl.Caching public void DeleteExpiredItems() { - var deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThanOrEqual)); + var deletedItemsCount = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThanOrEqual)); - if (deleted > 0) + if (deletedItemsCount > 0) { fileDb.Clean(); - Debug.WriteLine($"{nameof(FileDbCache)}: Deleted {deleted} expired items"); + logger?.LogInformation("Deleted {count} expired items", deletedItemsCount); } } } diff --git a/Caches/FileDbCache/FileDbCache.csproj b/Caches/FileDbCache/FileDbCache.csproj index 240195e7..c5449843 100644 --- a/Caches/FileDbCache/FileDbCache.csproj +++ b/Caches/FileDbCache/FileDbCache.csproj @@ -11,7 +11,9 @@ - + + + diff --git a/Caches/SQLiteCache/SQLiteCache.cs b/Caches/SQLiteCache/SQLiteCache.cs index f7da93f9..af0b3549 100644 --- a/Caches/SQLiteCache/SQLiteCache.cs +++ b/Caches/SQLiteCache/SQLiteCache.cs @@ -1,13 +1,23 @@ using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Data.SQLite; -using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; namespace MapControl.Caching { + public class SQLiteCacheOptions : IOptions + { + public SQLiteCacheOptions Value => this; + + public string Path { get; set; } + + public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromHours(1); + } + /// /// IDistributedCache implementation based on System.Data.SQLite, https://system.data.sqlite.org/. /// @@ -15,14 +25,27 @@ namespace MapControl.Caching { private readonly SQLiteConnection connection; private readonly Timer timer; + private readonly ILogger logger; - public SQLiteCache(string path) - : this(path, TimeSpan.FromHours(1)) + public SQLiteCache(string path, ILoggerFactory loggerFactory = null) + : this(new SQLiteCacheOptions { Path = path }, loggerFactory) { } - public SQLiteCache(string path, TimeSpan expirationScanFrequency) + public SQLiteCache(IOptions optionsAccessor, ILoggerFactory loggerFactory = null) + : this(optionsAccessor.Value, loggerFactory) { + } + + public SQLiteCache(SQLiteCacheOptions options, ILoggerFactory loggerFactory = null) + { + if (loggerFactory != null) + { + logger = loggerFactory.CreateLogger(); + } + + var path = options.Path; + if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(Path.GetExtension(path))) { path = Path.Combine(path ?? "", "TileCache.sqlite"); @@ -41,11 +64,11 @@ namespace MapControl.Caching command.ExecuteNonQuery(); } - Debug.WriteLine($"{nameof(SQLiteCache)}: Opened database {path}"); + logger?.LogInformation("Opened database {path}", path); - if (expirationScanFrequency > TimeSpan.Zero) + if (options.ExpirationScanFrequency > TimeSpan.Zero) { - timer = new Timer(_ => DeleteExpiredItems(), null, TimeSpan.Zero, expirationScanFrequency); + timer = new Timer(_ => DeleteExpiredItems(), null, TimeSpan.Zero, options.ExpirationScanFrequency); } } @@ -70,7 +93,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(SQLiteCache)}.Get({key}): {ex.Message}"); + logger?.LogError(ex, "Get({key})", key); } } @@ -92,7 +115,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(SQLiteCache)}.GetAsync({key}): {ex.Message}"); + logger?.LogError(ex, "GetAsync({key})", key); } } @@ -112,7 +135,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(SQLiteCache)}.Set({key}): {ex.Message}"); + logger?.LogError(ex, "Set({key})", key); } } } @@ -130,7 +153,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(SQLiteCache)}.SetAsync({key}): {ex.Message}"); + logger?.LogError(ex, "SetAsync({key})", key); } } } @@ -157,7 +180,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(SQLiteCache)}.Remove({key}): {ex.Message}"); + logger?.LogError(ex, "Remove({key})", key); } } } @@ -175,28 +198,28 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(SQLiteCache)}.RemoveAsync({key}): {ex.Message}"); + logger?.LogError(ex, "RemoveAsync({key})", key); } } } public void DeleteExpiredItems() { - long deleted; + long deletedItemsCount; using (var command = DeleteExpiredItemsCommand()) { - deleted = (long)command.ExecuteScalar(); + deletedItemsCount = (long)command.ExecuteScalar(); } - if (deleted > 0) + if (deletedItemsCount > 0) { using (var command = new SQLiteCommand("vacuum", connection)) { command.ExecuteNonQuery(); } - Debug.WriteLine($"{nameof(SQLiteCache)}: Deleted {deleted} expired items"); + logger?.LogInformation("Deleted {count} expired items", deletedItemsCount); } } diff --git a/Caches/SQLiteCache/SQLiteCache.csproj b/Caches/SQLiteCache/SQLiteCache.csproj index 15965932..a10c9c36 100644 --- a/Caches/SQLiteCache/SQLiteCache.csproj +++ b/Caches/SQLiteCache/SQLiteCache.csproj @@ -12,6 +12,8 @@ + + diff --git a/MapControl/Shared/ImageFileCache.cs b/MapControl/Shared/ImageFileCache.cs index a5558049..d73e430e 100644 --- a/MapControl/Shared/ImageFileCache.cs +++ b/MapControl/Shared/ImageFileCache.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -10,6 +10,15 @@ using System.Threading.Tasks; namespace MapControl.Caching { + public class ImageFileCacheOptions : IOptions + { + public ImageFileCacheOptions Value => this; + + public string Path { get; set; } + + public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromHours(1); + } + /// /// IDistributedCache implementation that creates a single file per cache entry. /// The cache expiration time is stored in the file's CreationTime property. @@ -19,30 +28,43 @@ namespace MapControl.Caching private readonly MemoryDistributedCache memoryCache; private readonly DirectoryInfo rootDirectory; private readonly Timer expirationScanTimer; + private readonly ILogger logger; private bool scanningExpiration; - public ImageFileCache(string path) - : this(path, TimeSpan.FromHours(1)) + public ImageFileCache(string path, ILoggerFactory loggerFactory = null) + : this(new ImageFileCacheOptions { Path = path }, loggerFactory) { } - public ImageFileCache(string path, TimeSpan expirationScanFrequency) + public ImageFileCache(IOptions optionsAccessor, ILoggerFactory loggerFactory = null) + : this(optionsAccessor.Value, loggerFactory) { + } + + public ImageFileCache(ImageFileCacheOptions options, ILoggerFactory loggerFactory = null) + { + if (loggerFactory != null) + { + logger = loggerFactory.CreateLogger(); + } + + var path = options.Path; + rootDirectory = new DirectoryInfo(!string.IsNullOrEmpty(path) ? path : "TileCache"); rootDirectory.Create(); - Debug.WriteLine($"{nameof(ImageFileCache)}: {rootDirectory.FullName}"); + logger?.LogInformation("Started in {name}", rootDirectory.FullName); - var options = new MemoryDistributedCacheOptions(); + var memoryCacheOptions = new MemoryDistributedCacheOptions(); - if (expirationScanFrequency > TimeSpan.Zero) + if (options.ExpirationScanFrequency > TimeSpan.Zero) { - options.ExpirationScanFrequency = expirationScanFrequency; + memoryCacheOptions.ExpirationScanFrequency = options.ExpirationScanFrequency; - expirationScanTimer = new Timer(_ => DeleteExpiredItems(), null, TimeSpan.Zero, expirationScanFrequency); + expirationScanTimer = new Timer(_ => DeleteExpiredItems(), null, TimeSpan.Zero, options.ExpirationScanFrequency); } - memoryCache = new MemoryDistributedCache(Options.Create(options)); + memoryCache = new MemoryDistributedCache(Options.Create(memoryCacheOptions)); } public void Dispose() @@ -75,7 +97,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed reading {file.FullName}: {ex.Message}"); + logger?.LogError(ex, "Failed reading {name}", file.FullName); } } } @@ -108,7 +130,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed reading {file.FullName}: {ex.Message}"); + logger?.LogError(ex, "Failed reading {name}", file.FullName); } } } @@ -140,7 +162,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed writing {file.FullName}: {ex.Message}"); + logger?.LogError(ex, "Failed writing {name}", file.FullName); } } } @@ -169,7 +191,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed writing {file.FullName}: {ex.Message}"); + logger?.LogError(ex, "Failed writing {name}", file.FullName); } } } @@ -207,7 +229,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed deleting {file.FullName}: {ex.Message}"); + logger?.LogError(ex, "Failed deleting {name}", file.FullName); } } } @@ -229,7 +251,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed deleting {file.FullName}: {ex.Message}"); + logger?.LogError(ex, "Failed deleting {name}", file.FullName); } } } @@ -246,7 +268,7 @@ namespace MapControl.Caching if (deletedFileCount > 0) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Deleted {deletedFileCount} expired items in {directory.Name}."); + logger?.LogInformation("Deleted {count} expired items in {name}", deletedFileCount, directory.FullName); } } @@ -254,6 +276,34 @@ namespace MapControl.Caching } } + private int ScanDirectory(DirectoryInfo directory) + { + var deletedFileCount = 0; + + try + { + deletedFileCount = directory.EnumerateDirectories().Sum(ScanDirectory); + + foreach (var file in directory.EnumerateFiles() + .Where(f => f.CreationTime > f.LastWriteTime && f.CreationTime <= DateTime.Now)) + { + file.Delete(); + deletedFileCount++; + } + + if (!directory.EnumerateFileSystemInfos().Any()) + { + directory.Delete(); + } + } + catch (Exception ex) + { + logger?.LogError(ex, "Failed cleaning {name}", directory.FullName); + } + + return deletedFileCount; + } + private FileInfo GetFile(string key) { try @@ -262,7 +312,7 @@ namespace MapControl.Caching } catch (Exception ex) { - Debug.WriteLine($"{nameof(ImageFileCache)}: Invalid key {key}: {ex.Message}"); + logger?.LogError(ex, "Invalid key {key}", key); } return null; @@ -306,33 +356,5 @@ namespace MapControl.Caching ? options.AbsoluteExpiration.Value.LocalDateTime : DateTime.Now.Add(options.AbsoluteExpirationRelativeToNow ?? options.SlidingExpiration ?? TimeSpan.FromDays(1)); } - - private static int ScanDirectory(DirectoryInfo directory) - { - var deletedFileCount = 0; - - try - { - deletedFileCount = directory.EnumerateDirectories().Sum(ScanDirectory); - - foreach (var file in directory.EnumerateFiles() - .Where(f => f.CreationTime > f.LastWriteTime && f.CreationTime <= DateTime.Now)) - { - file.Delete(); - deletedFileCount++; - } - - if (!directory.EnumerateFileSystemInfos().Any()) - { - directory.Delete(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"{nameof(ImageFileCache)}: Failed cleaning {directory.FullName}: {ex.Message}"); - } - - return deletedFileCount; - } } }