diff --git a/CacheDrive.Tests/CachingObjectsTests.cs b/CacheDrive.Tests/CachingObjectsTests.cs index 113964f..c3fd04d 100644 --- a/CacheDrive.Tests/CachingObjectsTests.cs +++ b/CacheDrive.Tests/CachingObjectsTests.cs @@ -36,8 +36,6 @@ public async Task ObjectShouldBeProperlyCachedAndRestoredFromTheMemory() ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); - // Act await cacheService.SetAsync(_testClass.Id.ToString(), _testClass); @@ -62,8 +60,6 @@ public async Task ObjectShoulBeProperlyCachedInMemoryAndPersistedToTheFile() ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); - // Act await cacheService.SetAsync(_testClass.Id.ToString(), _testClass); @@ -88,8 +84,6 @@ public async Task CacheShouldBeProperlyLoadedFromTheFile_Then_ObjectShouldBeProp ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); - // Act var cachedTestClass = await cacheService.GetAsync(_testClass.Id.ToString()); @@ -111,8 +105,6 @@ public async Task CacheShouldExpiredDuringLoadedFromTheFile_Then_ObjectShouldNot cacheType: CacheType.MemoryAndFile); ICacheService cacheService = serviceProvider.GetRequiredService(); - - await cacheService.InitializeAsync(); // Act var cachedTestClass = await cacheService.GetAsync(_testClass.Id.ToString()); diff --git a/CacheDrive.Tests/CachingSimpleTypesTests.cs b/CacheDrive.Tests/CachingSimpleTypesTests.cs index a326c77..efadbd7 100644 --- a/CacheDrive.Tests/CachingSimpleTypesTests.cs +++ b/CacheDrive.Tests/CachingSimpleTypesTests.cs @@ -21,8 +21,6 @@ public async Task SimpleTypesShouldBeProperlyCachedAndRestoredFromTheMemory() ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); - // Act (string key, int value) intItem = ("int_test", 10); await cacheService.SetAsync(intItem.key, intItem.value); @@ -45,8 +43,6 @@ public async Task SimpleTypesShouldBeProperlyCachedAndRestoredFromTheMemory() double cachedDoubleItem = await cacheService.GetAsync(doubleItem.key); bool cachedBoolItem = await cacheService.GetAsync(boolItem.key); - await cacheService.FlushAsync(); - // Assert cachedIntItem.Should().Be(intItem.value); cachedCharItem.Should().Be(charItem.value); @@ -68,8 +64,6 @@ public async Task SimpleTypesShouldBeProperlyDeletedFromTheMemoryCache() ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); - // Act // Add 50 items diff --git a/CacheDrive.Tests/MemoryAndFileTests.cs b/CacheDrive.Tests/MemoryAndFileTests.cs index 106c535..03ddc47 100644 --- a/CacheDrive.Tests/MemoryAndFileTests.cs +++ b/CacheDrive.Tests/MemoryAndFileTests.cs @@ -14,8 +14,6 @@ public async Task CacheShouldBeSavedCorrectly() ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now); ICacheService cacheService = serviceProvider.GetRequiredService(); - - await cacheService.InitializeAsync(); // Act string key = "name"; @@ -26,13 +24,15 @@ public async Task CacheShouldBeSavedCorrectly() if (cacheService.TryGetValue(key, out string cachedValue)) { cachedValue.Should().Be("John"); - await cacheService.FlushAsync(); + await cacheService.FlushAsync(); } else { await cacheService.FlushAsync(); Assert.Fail(); } + + await cacheService.FlushAsync(); } [Test, Order(2)] @@ -42,7 +42,6 @@ public async Task CacheShouldBeLoadCorrectly() ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now); ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); string key = "name"; @@ -66,7 +65,6 @@ public async Task CacheShouldExpired() ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now.AddHours(3)); ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); string key = "name"; diff --git a/CacheDrive.Tests/MemoryTests.cs b/CacheDrive.Tests/MemoryTests.cs index 53c15b0..0698980 100644 --- a/CacheDrive.Tests/MemoryTests.cs +++ b/CacheDrive.Tests/MemoryTests.cs @@ -13,19 +13,20 @@ public class MemoryTests public async Task StringShouldBeCorrectlySavedAndReadFromTheCacheInMemory(string key, string text) { // Arrange - ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now, true, CacheExpirationType.Hours, 2, CacheType.Memory); + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); ICacheService cacheService = serviceProvider.GetRequiredService(); - await cacheService.InitializeAsync(); - // Act await cacheService.SetAsync(key, text); string resultValue = await cacheService.GetAsync(key); - await cacheService.FlushAsync(); - // Assert resultValue.Should().Be(text); } diff --git a/CacheDrive/Services/ICacheService.cs b/CacheDrive/Services/ICacheService.cs index 4428d7c..cb9bcc7 100644 --- a/CacheDrive/Services/ICacheService.cs +++ b/CacheDrive/Services/ICacheService.cs @@ -10,7 +10,7 @@ public interface ICacheService /// Can be used many times, each time adding or overwriting data if they have the same keys. /// Task InitializeAsync(); - + /// /// Dumps cached data into files, database, and so on. /// Usually it should be run before the application terminates. diff --git a/CacheDrive/Services/MemoryCacheFileStorageService.cs b/CacheDrive/Services/MemoryCacheFileStorageService.cs index 7fb8d7b..c0620c1 100644 --- a/CacheDrive/Services/MemoryCacheFileStorageService.cs +++ b/CacheDrive/Services/MemoryCacheFileStorageService.cs @@ -15,6 +15,12 @@ public MemoryCacheFileStorageService(IOptions settings, IDateServ : base(settings, dateService) { CreateCacheDirectory(); + Initialize(); + } + + ~MemoryCacheFileStorageService() + { + Flush(); } public override async Task FlushAsync() @@ -26,25 +32,39 @@ public override async Task FlushAsync() string cachedItem = JsonSerializer.Serialize(item, JsonSettings.JsonOptions); - string safeFilename = SafeFilenameRegex().Replace(input: key, replacement: string.Empty); - - string path = CachePath(fileName: $"{safeFilename}.json"); + string path = GetFilePath(key); await using StreamWriter sw = File.CreateText(path); await sw.WriteLineAsync(cachedItem); item.Dirty = false; } } + + private void Flush() + { + foreach ((string key, CachedItem item) in Storage) + { + if (!item.Dirty || item.Expired(DateService)) + continue; + string cachedItem = JsonSerializer.Serialize(item, JsonSettings.JsonOptions); + + string path = GetFilePath(key); + + using StreamWriter sw = File.CreateText(path); + sw.WriteLineAsync(cachedItem); + item.Dirty = false; + } + } + public override async Task InitializeAsync() { - string[] dirs = Directory.GetFiles(path: GetCacheDirectory(), searchPattern: "*.json"); + string[] files = GetCacheFiles(); - foreach (string file in dirs) + foreach (string file in files) { string jsonString = await File.ReadAllTextAsync(file); - - CachedItem cachedItem = JsonSerializer.Deserialize(jsonString, JsonSettings.JsonOptions); + CachedItem cachedItem = DeserializeCachedItem(jsonString); if (cachedItem is null) return; @@ -57,6 +77,27 @@ public override async Task InitializeAsync() await SetAsync(cachedItem); } } + + private void Initialize() + { + string[] files = GetCacheFiles(); + + foreach (string file in files) + { + string jsonString = File.ReadAllText(file); + CachedItem cachedItem = DeserializeCachedItem(jsonString); + + if (cachedItem is null) return; + + if (cachedItem.Expired(DateService)) + { + File.Delete(file); + continue; + } + + Set(cachedItem); + } + } private Regex _safeFilenameRegex; @@ -71,11 +112,24 @@ private Regex SafeFilenameRegex() return _safeFilenameRegex; } + private string GetFilePath(string key) + { + string safeFilename = SafeFilenameRegex().Replace(input: key, replacement: string.Empty); + string path = CachePath(fileName: $"{safeFilename}.json"); + return path; + } + + private string[] GetCacheFiles() + => Directory.GetFiles(path: GetCacheDirectory(), searchPattern: "*.json"); + private string CachePath(string fileName) => Path.Combine(Environment.CurrentDirectory, CacheSettings.CacheFolderName, fileName); private string GetCacheDirectory() => Path.Combine(Environment.CurrentDirectory, CacheSettings.CacheFolderName); + + private CachedItem DeserializeCachedItem(string jsonString) + => JsonSerializer.Deserialize(jsonString, JsonSettings.JsonOptions); private void CreateCacheDirectory() { diff --git a/CacheDrive/Services/MemoryCacheService.cs b/CacheDrive/Services/MemoryCacheService.cs index 7c51128..901cde6 100644 --- a/CacheDrive/Services/MemoryCacheService.cs +++ b/CacheDrive/Services/MemoryCacheService.cs @@ -25,7 +25,7 @@ public MemoryCacheService(IOptions settings, IDateService dateSer } protected IDateService DateService => _dateService; - + public virtual Task InitializeAsync() => Task.CompletedTask; @@ -156,7 +156,7 @@ private Task SetAsync(T item, int expirySeconds = 0) where T : ICacheable return SetAsync(cachedItem); } - private void Set(CachedItem cachedItem) + internal void Set(CachedItem cachedItem) { Storage.AddOrUpdate(cachedItem.Key, _ => cachedItem, (_, _) => cachedItem); }