diff --git a/README.md b/README.md index 86766aa..e48e940 100644 --- a/README.md +++ b/README.md @@ -83,25 +83,22 @@ async Task> GetMonkeysAsync() var url = "http://montemagno.com/monkeys.json"; //Dev handle online/offline scenario - if(!CrossConnectivity.Current.IsConnected) + if (!CrossConnectivity.Current.IsConnected) { return Barrel.Current.Get>(key: url); } //Dev handles checking if cache is expired - if(!Barrel.Current.IsExpired(key: url)) + if (!Barrel.Current.IsExpired(key: url)) { return Barrel.Current.Get>(key: url); } - var client = new HttpClient(); - var json = await client.GetStringAsync(url); - var monkeys = JsonConvert.DeserializeObject>(json); + var monkeys = await client.GetFromJsonAsync>(url); //Saves the cache and pass it a timespan for expiration Barrel.Current.Add(key: url, data: monkeys, expireIn: TimeSpan.FromDays(1)); - } ``` @@ -110,22 +107,17 @@ Ideally, you can make these calls extremely generic and just pass in a string: ```csharp public async Task GetAsync(string url, int days = 7, bool forceRefresh = false) { - var json = string.Empty; - if (!CrossConnectivity.Current.IsConnected) - json = Barrel.Current.Get(url); + return Barrel.Current.Get(url); if (!forceRefresh && !Barrel.Current.IsExpired(url)) - json = Barrel.Current.Get(url); + return Barrel.Current.Get(url); try { - if (string.IsNullOrWhiteSpace(json)) - { - json = await client.GetStringAsync(url); - Barrel.Current.Add(url, json, TimeSpan.FromDays(days)); - } - return JsonConvert.DeserializeObject(json); + T result = await httpClient.GetFromJsonAsync(url); + Barrel.Current.Add(url, result, TimeSpan.FromDays(days)); + return result; } catch (Exception ex) { @@ -133,7 +125,7 @@ public async Task GetAsync(string url, int days = 7, bool forceRefresh = f //probably re-throw here :) } - return default(T); + return default; } ``` @@ -188,6 +180,14 @@ BarrelUtils.SetBaseCachePath("Path"); You MUST call this before initializing or accessing anything in the Barrel, and it can only ever be called once else it will throw an `InvalidOperationException`. +#### Json Serialization + +MonkeyCache v2.0 and higher uses System.Text.Json to serialize objects to/from the backing store. By default, the default System.Text.Json serialization behavior is used. There are two options for controlling this serialization: + +1. Pass an optional [JsonSerializationOptions](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializeroptions) instance to `Barrel.Current.Add` and `Barrel.Current.Get`. +2. Pass a `JsonTypeInfo` instance to `Barrel.Current.Add` and `Barrel.Current.Get`. You can get a `JsonTypeInfo` instance by using the System.Text.Json source generator. See [How to use source generation in System.Text.Json](https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-source-generation) for more information. + +No matter which option you choose, it is recommended to use the same option between `Barrel.Current.Add` and `Barrel.Current.Get`. If the options are inconsistent, the information going into the backing store may not be read properly when retrieving it back from the Barrel. ### FAQ diff --git a/src/MonkeyCache.FileStore/Barrel.cs b/src/MonkeyCache.FileStore/Barrel.cs index db561f4..f65313d 100644 --- a/src/MonkeyCache.FileStore/Barrel.cs +++ b/src/MonkeyCache.FileStore/Barrel.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using System.Threading; -using Newtonsoft.Json; namespace MonkeyCache.FileStore { public class Barrel : IBarrel { ReaderWriterLockSlim indexLocker; - readonly JsonSerializerSettings jsonSettings; Lazy baseDirectory; HashAlgorithm hashAlgorithm; @@ -36,13 +37,6 @@ public class Barrel : IBarrel indexLocker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); - jsonSettings = new JsonSerializerSettings - { - ObjectCreationHandling = ObjectCreationHandling.Replace, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - TypeNameHandling = TypeNameHandling.All, - }; - index = new Dictionary>(); LoadIndex(); @@ -68,14 +62,7 @@ public class Barrel : IBarrel public static IBarrel Create(string cacheDirectory, HashAlgorithm hash = null) => new Barrel(cacheDirectory, hash); - /// - /// Adds an entry to the barrel - /// - /// Unique identifier for the entry - /// Data object to store - /// Time from UtcNow to expire entry in - /// Optional eTag information - void Add(string key, string data, TimeSpan expireIn, string eTag = null) + void Add(string key, string data, TimeSpan expireIn, string eTag) { indexLocker.EnterWriteLock(); @@ -99,42 +86,45 @@ void Add(string key, string data, TimeSpan expireIn, string eTag = null) } } - /// - /// Adds an entry to the barrel - /// - /// - /// Unique identifier for the entry - /// Data object to store - /// Time from UtcNow to expire entry in - /// Optional eTag information - /// Custom json serialization settings to use - public void Add(string key, - T data, - TimeSpan expireIn, - string eTag = null, - JsonSerializerSettings jsonSerializationSettings = null) + void Add( + string key, + T data, + TimeSpan expireIn, + string eTag, + Func serializer) { - if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key can not be null or empty.", nameof(key)); if (data == null) throw new ArgumentNullException("Data can not be null.", nameof(data)); - var dataJson = string.Empty; - + string dataJson; if (BarrelUtils.IsString(data)) { dataJson = data as string; } else { - dataJson = JsonConvert.SerializeObject(data, jsonSerializationSettings ?? jsonSettings); + dataJson = serializer(data); } Add(key, dataJson, expireIn, eTag); } + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + public void Add(string key, T data, TimeSpan expireIn, JsonSerializerOptions options = null, string eTag = null) => + Add(key, data, expireIn, eTag, data => JsonSerialize(data, options)); + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Workaround https://github.com/dotnet/linker/issues/2001")] + static string JsonSerialize(T data, JsonSerializerOptions options) => + JsonSerializer.Serialize(data, options); + + /// + public void Add(string key, T data, TimeSpan expireIn, JsonTypeInfo jsonTypeInfo, string eTag = null) => + Add(key, data, expireIn, eTag, data => JsonSerializer.Serialize(data, jsonTypeInfo)); + /// /// Empties all specified entries regardless if they are expired. /// Throws an exception if any deletions fail and rolls back changes. @@ -152,7 +142,7 @@ public void Empty(params string[] key) continue; var file = Path.Combine(baseDirectory.Value, Hash(k)); - if(File.Exists(file)) + if (File.Exists(file)) File.Delete(file); index.Remove(k); @@ -180,7 +170,7 @@ public void EmptyAll() { var hash = Hash(item.Key); var file = Path.Combine(baseDirectory.Value, hash); - if(File.Exists(file)) + if (File.Exists(file)) File.Delete(file); } @@ -295,13 +285,7 @@ public IEnumerable GetKeys(CacheState state = CacheState.Active) } } - /// - /// Gets the data entry for the specified key. - /// - /// Unique identifier for the entry to get - /// Custom json serialization settings to use - /// The data object that was stored if found, else default(T) - public T Get(string key, JsonSerializerSettings jsonSerializationSettings = null) + T Get(string key, Func deserialize) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key can not be null or empty.", nameof(key)); @@ -317,14 +301,13 @@ public T Get(string key, JsonSerializerSettings jsonSerializationSettings = n if (index.ContainsKey(key) && File.Exists(path) && (!AutoExpire || (AutoExpire && !IsExpired(key)))) { - var contents = File.ReadAllText(path); if (BarrelUtils.IsString(result)) { - object final = contents; - return (T)final; + return (T)(object)File.ReadAllText(path); } - result = JsonConvert.DeserializeObject(contents, jsonSerializationSettings ?? jsonSettings); + using FileStream fileStream = new(path, FileMode.Open, FileAccess.Read); + result = deserialize(fileStream); } } finally @@ -335,6 +318,19 @@ public T Get(string key, JsonSerializerSettings jsonSerializationSettings = n return result; } + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + public T Get(string key, JsonSerializerOptions options = null) => + Get(key, fileStream => JsonDeserialize(fileStream, options)); + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Workaround https://github.com/dotnet/linker/issues/2001")] + static T JsonDeserialize(FileStream fileStream, JsonSerializerOptions options) => + JsonSerializer.Deserialize(fileStream, options); + + /// + public T Get(string key, JsonTypeInfo jsonTypeInfo) => Get(key, fileStream => + JsonSerializer.Deserialize(fileStream, jsonTypeInfo)); + /// /// Gets the DateTime that the item will expire for the specified key. /// diff --git a/src/MonkeyCache.FileStore/MonkeyCache.FileStore.csproj b/src/MonkeyCache.FileStore/MonkeyCache.FileStore.csproj index 4843501..3a52ca2 100644 --- a/src/MonkeyCache.FileStore/MonkeyCache.FileStore.csproj +++ b/src/MonkeyCache.FileStore/MonkeyCache.FileStore.csproj @@ -1,38 +1,38 @@ - + - - netstandard2.0;MonoAndroid10.0;Xamarin.iOS10;Xamarin.TVOS10;Xamarin.Mac20;net6.0-android;net6.0-ios;net6.0-maccatalyst - $(TargetFrameworks);uap10.0.19041;net6.0-windows10.0.19041;net461 - $(AssemblyName) ($(TargetFramework)) - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - James Montemagno - MonkeyCache.FileStore - true - https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png - en - https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE - James Montemagno - https://github.com/jamesmontemagno/monkey-cache - A simple caching library to cache any data structure for a specific amount of time in any .NET application. - xamarin, windows, ios, android, cache, http - 🙈 MonkeyCache.FileStore - A .NET Caching Library - A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. Powered by FileStore. - - See: https://github.com/jamesmontemagno/monkey-cache - https://github.com/jamesmontemagno/monkey-cache - 2022 Refractored LLC & James Montemagno - - MonkeyCache.FileStore - - default - - $(DefineConstants);FILESTORE - true - true + + net6.0 + $(AssemblyName) ($(TargetFramework)) + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + James Montemagno + MonkeyCache.FileStore + true + https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png + en + https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE + James Montemagno + https://github.com/jamesmontemagno/monkey-cache + A simple caching library to cache any data structure for a specific amount of time in any .NET application. + xamarin, windows, ios, android, cache, http + 🙈 MonkeyCache.FileStore - A .NET Caching Library + A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. Powered by FileStore. + + See: https://github.com/jamesmontemagno/monkey-cache + https://github.com/jamesmontemagno/monkey-cache + 2022 Refractored LLC & James Montemagno + + MonkeyCache.FileStore + + default + + $(DefineConstants);FILESTORE + true + true portable + true true @@ -49,30 +49,12 @@ - - 10.0 - 10.0 - 13.1 - 10.14 - 21.0 - 10.0.16299.0 - 10.0.16299.0 - - - 10.0.16299.0 - - - - - - - - - + + + - - - + + diff --git a/src/MonkeyCache.LiteDB/Barrel.cs b/src/MonkeyCache.LiteDB/Barrel.cs index ea09d68..ddc53da 100644 --- a/src/MonkeyCache.LiteDB/Barrel.cs +++ b/src/MonkeyCache.LiteDB/Barrel.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using LiteDB; -using Newtonsoft.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace MonkeyCache.LiteDB { @@ -44,7 +47,6 @@ public static IBarrel Create(string cacheDirectory, bool cache = false) return instance; } - readonly JsonSerializerSettings jsonSettings; Barrel(string cacheDirectory = null) { var directory = string.IsNullOrEmpty(cacheDirectory) ? baseCacheDir.Value : cacheDirectory; @@ -54,31 +56,19 @@ public static IBarrel Create(string cacheDirectory, bool cache = false) Directory.CreateDirectory(directory); } -#if __MACOS__ - - if (!string.IsNullOrWhiteSpace(EncryptionKey)) - path = $"Filename={path}; Password={EncryptionKey}; Mode=Exclusive"; - else - path = $"Filename={path}; Mode=Exclusive"; -#else - if (!string.IsNullOrWhiteSpace(EncryptionKey)) path = $"Filename={path}; Password={EncryptionKey}"; else path = $"Filename={path}"; -#endif + + if (OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst()) + path += "; Mode=Exclusive"; + if(Upgrade) path += "; Upgrade=true"; db = new LiteDatabase(path); col = db.GetCollection(); - - jsonSettings = new JsonSerializerSettings - { - ObjectCreationHandling = ObjectCreationHandling.Replace, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - TypeNameHandling = TypeNameHandling.All, - }; } #region Exist and Expiration Methods @@ -148,13 +138,7 @@ public IEnumerable GetKeys(CacheState state = CacheState.Active) return new string[0]; } - /// - /// Gets the data entry for the specified key. - /// - /// Unique identifier for the entry to get - /// Custom json serialization settings to use - /// The data object that was stored if found, else default(T) - public T Get(string key, JsonSerializerSettings jsonSerializationSettings = null) + T Get(string key, Func deserialize) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key can not be null or empty.", nameof(key)); @@ -172,9 +156,21 @@ public T Get(string key, JsonSerializerSettings jsonSerializationSettings = n return (T)final; } - return JsonConvert.DeserializeObject(ent.Contents, jsonSerializationSettings ?? jsonSettings); + return deserialize(ent.Contents); } + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + public T Get(string key, JsonSerializerOptions options = null) => + Get(key, contents => JsonDeserialize(contents, options)); + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Workaround https://github.com/dotnet/linker/issues/2001")] + private static T JsonDeserialize(string contents, JsonSerializerOptions options) => + JsonSerializer.Deserialize(contents, options); + + /// + public T Get(string key, JsonTypeInfo jsonTypeInfo) => + Get(key, contents => JsonSerializer.Deserialize(contents, jsonTypeInfo)); /// /// Gets the ETag for the specified key. @@ -216,15 +212,7 @@ public string GetETag(string key) #region Add Methods - /// - /// Adds a string netry to the barrel - /// - /// - /// Unique identifier for the entry - /// Data string to store - /// Time from UtcNow to expire entry in - /// Optional eTag information - void Add(string key, string data, TimeSpan expireIn, string eTag = null) + void Add(string key, string data, TimeSpan expireIn, string eTag) { if (data == null) return; @@ -240,16 +228,7 @@ void Add(string key, string data, TimeSpan expireIn, string eTag = null) col.Upsert(ent); } - /// - /// Adds an entry to the barrel - /// - /// - /// Unique identifier for the entry - /// Data object to store - /// Time from UtcNow to expire entry in - /// Optional eTag information - /// Custom json serialization settings to use - public void Add(string key, T data, TimeSpan expireIn, string eTag = null, JsonSerializerSettings jsonSerializationSettings = null) + void Add(string key, T data, TimeSpan expireIn, string eTag, Func serializer) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key can not be null or empty.", nameof(key)); @@ -257,20 +236,32 @@ public void Add(string key, T data, TimeSpan expireIn, string eTag = null, Js if (data == null) throw new ArgumentNullException("Data can not be null.", nameof(data)); - var dataJson = string.Empty; - + string dataJson; if (BarrelUtils.IsString(data)) { dataJson = data as string; } else { - dataJson = JsonConvert.SerializeObject(data, jsonSerializationSettings ?? jsonSettings); + dataJson = serializer(data); } Add(key, dataJson, expireIn, eTag); } + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + public void Add(string key, T data, TimeSpan expireIn, JsonSerializerOptions options = null, string eTag = null) => + Add(key, data, expireIn, eTag, data => JsonSerialize(data, options)); + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Workaround https://github.com/dotnet/linker/issues/2001")] + static string JsonSerialize(T data, JsonSerializerOptions options) => + JsonSerializer.Serialize(data, options); + + /// + public void Add(string key, T data, TimeSpan expireIn, JsonTypeInfo jsonTypeInfo, string eTag = null) => + Add(key, data, expireIn, eTag, data => JsonSerializer.Serialize(data, jsonTypeInfo)); + #endregion #region Empty Methods diff --git a/src/MonkeyCache.LiteDB/MonkeyCache.LiteDB.csproj b/src/MonkeyCache.LiteDB/MonkeyCache.LiteDB.csproj index e7d4432..30246f8 100644 --- a/src/MonkeyCache.LiteDB/MonkeyCache.LiteDB.csproj +++ b/src/MonkeyCache.LiteDB/MonkeyCache.LiteDB.csproj @@ -1,38 +1,38 @@ - + - - netstandard2.0;MonoAndroid10.0;Xamarin.iOS10;Xamarin.TVOS10;Xamarin.Mac20;net6.0-android;net6.0-ios;net6.0-maccatalyst - $(TargetFrameworks);uap10.0.19041;net6.0-windows10.0.19041;net461 - $(AssemblyName) ($(TargetFramework)) - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - James Montemagno - MonkeyCache.LiteDB - true - https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png - en - https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE - James Montemagno - https://github.com/jamesmontemagno/monkey-cache - A simple caching library to cache any data structure for a specific amount of time in any .NET application. - xamarin, windows, ios, android, cache, http - 🙉 MonkeyCache.LiteDB - A .NET Caching Library - A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. Powered by LiteDB. + + net6.0 + $(AssemblyName) ($(TargetFramework)) + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + James Montemagno + MonkeyCache.LiteDB + true + https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png + en + https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE + James Montemagno + https://github.com/jamesmontemagno/monkey-cache + A simple caching library to cache any data structure for a specific amount of time in any .NET application. + xamarin, windows, ios, android, cache, http + 🙉 MonkeyCache.LiteDB - A .NET Caching Library + A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. Powered by LiteDB. + + See: https://github.com/jamesmontemagno/monkey-cache + https://github.com/jamesmontemagno/monkey-cache + 2022 Refractored LLC & James Montemagno - See: https://github.com/jamesmontemagno/monkey-cache - https://github.com/jamesmontemagno/monkey-cache - 2022 Refractored LLC & James Montemagno - - MonkeyCache.LiteDB + MonkeyCache.LiteDB - default + default - $(DefineConstants);LITEDB - true + $(DefineConstants);LITEDB + true true portable + true true @@ -48,33 +48,17 @@ - - 10.0 - 10.0 - 13.1 - 10.14 - 21.0 - 10.0.16299.0 - 10.0.16299.0 - - - 10.0.16299.0 - + + + + + + - - - - - - - + - - - - diff --git a/src/MonkeyCache.SQLite/Barrel.cs b/src/MonkeyCache.SQLite/Barrel.cs index 489f4e4..a6e3fd7 100644 --- a/src/MonkeyCache.SQLite/Barrel.cs +++ b/src/MonkeyCache.SQLite/Barrel.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using SQLite; -using Newtonsoft.Json; using System.Collections.Generic; namespace MonkeyCache.SQLite @@ -35,7 +37,6 @@ public class Barrel : IBarrel public static IBarrel Create(string cacheDirectory) => new Barrel(cacheDirectory); - readonly JsonSerializerSettings jsonSettings; Barrel(string cacheDirectory = null) { var directory = string.IsNullOrEmpty(cacheDirectory) ? baseCacheDir.Value : cacheDirectory; @@ -47,13 +48,6 @@ public static IBarrel Create(string cacheDirectory) db = new SQLiteConnection(path, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex); db.CreateTable(); - - jsonSettings = new JsonSerializerSettings - { - ObjectCreationHandling = ObjectCreationHandling.Replace, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - TypeNameHandling = TypeNameHandling.All, - }; } #region Exist and Expiration Methods @@ -135,13 +129,7 @@ public IEnumerable GetKeys(CacheState state = CacheState.Active) return new string[0]; } - /// - /// Gets the data entry for the specified key. - /// - /// Unique identifier for the entry to get - /// Custom json serialization settings to use - /// The data object that was stored if found, else default(T) - public T Get(string key, JsonSerializerSettings jsonSerializationSettings = null) + T Get(string key, Func deserialize) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key can not be null or empty.", nameof(key)); @@ -163,10 +151,22 @@ public T Get(string key, JsonSerializerSettings jsonSerializationSettings = n return (T)final; } - - return JsonConvert.DeserializeObject(ent.Contents, jsonSerializationSettings ?? jsonSettings); + return deserialize(ent.Contents); } + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + public T Get(string key, JsonSerializerOptions options = null) => + Get(key, contents => JsonDeserialize(contents, options)); + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Workaround https://github.com/dotnet/linker/issues/2001")] + static T JsonDeserialize(string contents, JsonSerializerOptions options) => + JsonSerializer.Deserialize(contents, options); + + /// + public T Get(string key, JsonTypeInfo jsonTypeInfo) => + Get(key, contents => JsonSerializer.Deserialize(contents, jsonTypeInfo)); + /// /// Gets the ETag for the specified key. /// @@ -212,15 +212,7 @@ public string GetETag(string key) #region Add Methods - /// - /// Adds a string netry to the barrel - /// - /// - /// Unique identifier for the entry - /// Data string to store - /// Time from UtcNow to expire entry in - /// Optional eTag information - void Add(string key, string data, TimeSpan expireIn, string eTag = null) + void Add(string key, string data, TimeSpan expireIn, string eTag) { var ent = new Banana { @@ -236,38 +228,40 @@ void Add(string key, string data, TimeSpan expireIn, string eTag = null) } } - /// - /// Adds an entry to the barrel - /// - /// - /// Unique identifier for the entry - /// Data object to store - /// Time from UtcNow to expire entry in - /// Optional eTag information - /// Custom json serialization settings to use - public void Add(string key, T data, TimeSpan expireIn, string eTag = null, JsonSerializerSettings jsonSerializationSettings = null) + void Add(string key, T data, TimeSpan expireIn, string eTag, Func serializer) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key can not be null or empty.", nameof(key)); - if (data == null) throw new ArgumentNullException("Data can not be null.", nameof(data)); - var dataJson = string.Empty; - + string dataJson; if (BarrelUtils.IsString(data)) { dataJson = data as string; } else { - dataJson = JsonConvert.SerializeObject(data, jsonSerializationSettings ?? jsonSettings); + dataJson = serializer(data); } Add(key, dataJson, expireIn, eTag); } + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + public void Add(string key, T data, TimeSpan expireIn, JsonSerializerOptions options = null, string eTag = null) => + Add(key, data, expireIn, eTag, data => JsonSerialize(data, options)); + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Workaround https://github.com/dotnet/linker/issues/2001")] + static string JsonSerialize(T data, JsonSerializerOptions options) => JsonSerializer.Serialize(data, options); + + /// + public void Add(string key, T data, TimeSpan expireIn, JsonTypeInfo jsonTypeInfo, string eTag = null) => + Add(key, data, expireIn, eTag, data => JsonSerializer.Serialize(data, jsonTypeInfo)); + + #endregion #region Empty Methods diff --git a/src/MonkeyCache.SQLite/MonkeyCache.SQLite.csproj b/src/MonkeyCache.SQLite/MonkeyCache.SQLite.csproj index c8e826c..6e4fa37 100644 --- a/src/MonkeyCache.SQLite/MonkeyCache.SQLite.csproj +++ b/src/MonkeyCache.SQLite/MonkeyCache.SQLite.csproj @@ -1,36 +1,36 @@ - + - - netstandard2.0;MonoAndroid10.0;Xamarin.iOS10;Xamarin.TVOS10;Xamarin.Mac20;net6.0-android;net6.0-ios;net6.0-maccatalyst - $(TargetFrameworks);uap10.0.19041;net6.0-windows10.0.19041;net461 - $(AssemblyName) ($(TargetFramework)) - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - James Montemagno - MonkeyCache.SQLite - true - https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png - en - https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE - James Montemagno - https://github.com/jamesmontemagno/monkey-cache - A simple caching library to cache any data structure for a specific amount of time in any .NET application. - xamarin, windows, ios, android, cache, http - 🙊 MonkeyCache.SQLite - A .NET Caching Library - A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. Powered by SQLite. - 2022 Refractored LLC & James Montemagno - https://github.com/jamesmontemagno/monkey-cache - See: https://github.com/jamesmontemagno/monkey-cache - - default + + net6.0 + $(AssemblyName) ($(TargetFramework)) + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + James Montemagno + MonkeyCache.SQLite + true + https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png + en + https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE + James Montemagno + https://github.com/jamesmontemagno/monkey-cache + A simple caching library to cache any data structure for a specific amount of time in any .NET application. + xamarin, windows, ios, android, cache, http + 🙊 MonkeyCache.SQLite - A .NET Caching Library + A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. Powered by SQLite. + 2022 Refractored LLC & James Montemagno + https://github.com/jamesmontemagno/monkey-cache + See: https://github.com/jamesmontemagno/monkey-cache + + default - $(DefineConstants);SQLITE - true - MonkeyCache.SQLite - true + $(DefineConstants);SQLITE + true + MonkeyCache.SQLite + true portable + true true @@ -46,32 +46,16 @@ - - 10.0 - 10.0 - 13.1 - 10.14 - 21.0 - 10.0.16299.0 - 10.0.16299.0 - - - 10.0.16299.0 - - - - - - - - - - - + + + + + + - - - + + + diff --git a/src/MonkeyCache.TestsFileStore/MonkeyCache.TestsFileStore.csproj b/src/MonkeyCache.TestsFileStore/MonkeyCache.TestsFileStore.csproj index 08b9c0d..89e2429 100644 --- a/src/MonkeyCache.TestsFileStore/MonkeyCache.TestsFileStore.csproj +++ b/src/MonkeyCache.TestsFileStore/MonkeyCache.TestsFileStore.csproj @@ -1,21 +1,7 @@ - - netcoreapp2.0 - - false - - - - - - - - - - diff --git a/src/MonkeyCache.TestsLiteDB/BarrelTestsEncryption.cs b/src/MonkeyCache.TestsLiteDB/BarrelTestsEncryption.cs index 749c74f..cf02a32 100644 --- a/src/MonkeyCache.TestsLiteDB/BarrelTestsEncryption.cs +++ b/src/MonkeyCache.TestsLiteDB/BarrelTestsEncryption.cs @@ -3,9 +3,9 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Text.Json; using Microsoft.VisualStudio.TestTools.UnitTesting; using MonkeyCache.LiteDB; -using Newtonsoft.Json; namespace MonkeyCache.Tests { @@ -28,7 +28,7 @@ public void Setup() barrel = Barrel.Current; json = @"[{""Name"":""Baboon"",""Location"":""Africa & Asia"",""Details"":""Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/9\/96\/Portrait_Of_A_Baboon.jpg\/314px-Portrait_Of_A_Baboon.jpg"",""Population"":10000},{""Name"":""Capuchin Monkey"",""Location"":""Central & South America"",""Details"":""The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/4\/40\/Capuchin_Costa_Rica.jpg\/200px-Capuchin_Costa_Rica.jpg"",""Population"":23000},{""Name"":""Blue Monkey"",""Location"":""Central and East Africa"",""Details"":""The blue monkey or diademed monkey is a species of Old World monkey native to Central and East Africa, ranging from the upper Congo River basin east to the East African Rift and south to northern Angola and Zambia"",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/8\/83\/BlueMonkey.jpg\/220px-BlueMonkey.jpg"",""Population"":12000},{""Name"":""Squirrel Monkey"",""Location"":""Central & South America"",""Details"":""The squirrel monkeys are the New World monkeys of the genus Saimiri. They are the only genus in the subfamily Saimirinae. The name of the genus Saimiri is of Tupi origin, and was also used as an English name by early researchers."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/2\/20\/Saimiri_sciureus-1_Luc_Viatour.jpg\/220px-Saimiri_sciureus-1_Luc_Viatour.jpg"",""Population"":11000},{""Name"":""Golden Lion Tamarin"",""Location"":""Brazil"",""Details"":""The golden lion tamarin also known as the golden marmoset, is a small New World monkey of the family Callitrichidae."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/8\/87\/Golden_lion_tamarin_portrait3.jpg\/220px-Golden_lion_tamarin_portrait3.jpg"",""Population"":19000},{""Name"":""Howler Monkey"",""Location"":""South America"",""Details"":""Howler monkeys are among the largest of the New World monkeys. Fifteen species are currently recognised. Previously classified in the family Cebidae, they are now placed in the family Atelidae."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/0\/0d\/Alouatta_guariba.jpg\/200px-Alouatta_guariba.jpg"",""Population"":8000},{""Name"":""Japanese Macaque"",""Location"":""Japan"",""Details"":""The Japanese macaque, is a terrestrial Old World monkey species native to Japan. They are also sometimes known as the snow monkey because they live in areas where snow covers the ground for months each"",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/c\/c1\/Macaca_fuscata_fuscata1.jpg\/220px-Macaca_fuscata_fuscata1.jpg"",""Population"":1000},{""Name"":""Mandrill"",""Location"":""Southern Cameroon, Gabon, Equatorial Guinea, and Congo"",""Details"":""The mandrill is a primate of the Old World monkey family, closely related to the baboons and even more closely to the drill. It is found in southern Cameroon, Gabon, Equatorial Guinea, and Congo."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/7\/75\/Mandrill_at_san_francisco_zoo.jpg\/220px-Mandrill_at_san_francisco_zoo.jpg"",""Population"":17000},{""Name"":""Proboscis Monkey"",""Location"":""Borneo"",""Details"":""The proboscis monkey or long-nosed monkey, known as the bekantan in Malay, is a reddish-brown arboreal Old World monkey that is endemic to the south-east Asian island of Borneo."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/e\/e5\/Proboscis_Monkey_in_Borneo.jpg\/250px-Proboscis_Monkey_in_Borneo.jpg"",""Population"":15000},{""Name"":""Sebastian"",""Location"":""Seattle"",""Details"":""This little trouble maker lives in Seattle with James and loves traveling on adventures with James and tweeting @MotzMonkeys. He by far is an Android fanboy and is getting ready for the new Nexus 6P!"",""Image"":""http:\/\/www.refractored.com\/images\/sebastian.jpg"",""Population"":1},{""Name"":""Henry"",""Location"":""Phoenix"",""Details"":""An adorable Monkey who is traveling the world with Heather and live tweets his adventures @MotzMonkeys. His favorite platform is iOS by far and is excited for the new iPhone 6s!"",""Image"":""http:\/\/www.refractored.com\/images\/henry.jpg"",""Population"":1}]"; - monkeys = JsonConvert.DeserializeObject>(json); + monkeys = JsonSerializer.Deserialize>(json); } diff --git a/src/MonkeyCache.TestsLiteDB/MonkeyCache.TestsLiteDB.csproj b/src/MonkeyCache.TestsLiteDB/MonkeyCache.TestsLiteDB.csproj index 69df677..b891c8e 100644 --- a/src/MonkeyCache.TestsLiteDB/MonkeyCache.TestsLiteDB.csproj +++ b/src/MonkeyCache.TestsLiteDB/MonkeyCache.TestsLiteDB.csproj @@ -1,32 +1,11 @@ - netcoreapp2.0 - - false - - - - default - TRACE;RELEASE;NETCOREAPP2_0;LITEDB - - - - default - TRACE;DEBUG;NETCOREAPP2_0;LITEDB; + $(DefineConstants);LITEDB - - - - - - - - - diff --git a/src/MonkeyCache.TestsSQLite/MonkeyCache.TestsSQLite.csproj b/src/MonkeyCache.TestsSQLite/MonkeyCache.TestsSQLite.csproj index c4b1e31..1bf7452 100644 --- a/src/MonkeyCache.TestsSQLite/MonkeyCache.TestsSQLite.csproj +++ b/src/MonkeyCache.TestsSQLite/MonkeyCache.TestsSQLite.csproj @@ -1,32 +1,13 @@ - netcoreapp2.0 - - false - - - - default - TRACE;RELEASE;NETCOREAPP2_0;SQLITE; - - - - default - TRACE;DEBUG;NETCOREAPP2_0;SQLITE; + $(DefineConstants);SQLITE - - - - - - - - - + + diff --git a/src/MonkeyCache.TestsShared/BarrelTests.cs b/src/MonkeyCache.TestsShared/BarrelTests.cs index c01df26..288f771 100644 --- a/src/MonkeyCache.TestsShared/BarrelTests.cs +++ b/src/MonkeyCache.TestsShared/BarrelTests.cs @@ -4,9 +4,10 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; namespace MonkeyCache.Tests { @@ -26,7 +27,7 @@ public void Setup() SetupBarrel(); url = "http://montemagno.com/monkeys.json"; json = @"[{""Name"":""Baboon"",""Location"":""Africa & Asia"",""Details"":""Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/9\/96\/Portrait_Of_A_Baboon.jpg\/314px-Portrait_Of_A_Baboon.jpg"",""Population"":10000},{""Name"":""Capuchin Monkey"",""Location"":""Central & South America"",""Details"":""The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/4\/40\/Capuchin_Costa_Rica.jpg\/200px-Capuchin_Costa_Rica.jpg"",""Population"":23000},{""Name"":""Blue Monkey"",""Location"":""Central and East Africa"",""Details"":""The blue monkey or diademed monkey is a species of Old World monkey native to Central and East Africa, ranging from the upper Congo River basin east to the East African Rift and south to northern Angola and Zambia"",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/8\/83\/BlueMonkey.jpg\/220px-BlueMonkey.jpg"",""Population"":12000},{""Name"":""Squirrel Monkey"",""Location"":""Central & South America"",""Details"":""The squirrel monkeys are the New World monkeys of the genus Saimiri. They are the only genus in the subfamily Saimirinae. The name of the genus Saimiri is of Tupi origin, and was also used as an English name by early researchers."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/2\/20\/Saimiri_sciureus-1_Luc_Viatour.jpg\/220px-Saimiri_sciureus-1_Luc_Viatour.jpg"",""Population"":11000},{""Name"":""Golden Lion Tamarin"",""Location"":""Brazil"",""Details"":""The golden lion tamarin also known as the golden marmoset, is a small New World monkey of the family Callitrichidae."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/8\/87\/Golden_lion_tamarin_portrait3.jpg\/220px-Golden_lion_tamarin_portrait3.jpg"",""Population"":19000},{""Name"":""Howler Monkey"",""Location"":""South America"",""Details"":""Howler monkeys are among the largest of the New World monkeys. Fifteen species are currently recognised. Previously classified in the family Cebidae, they are now placed in the family Atelidae."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/0\/0d\/Alouatta_guariba.jpg\/200px-Alouatta_guariba.jpg"",""Population"":8000},{""Name"":""Japanese Macaque"",""Location"":""Japan"",""Details"":""The Japanese macaque, is a terrestrial Old World monkey species native to Japan. They are also sometimes known as the snow monkey because they live in areas where snow covers the ground for months each"",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/c\/c1\/Macaca_fuscata_fuscata1.jpg\/220px-Macaca_fuscata_fuscata1.jpg"",""Population"":1000},{""Name"":""Mandrill"",""Location"":""Southern Cameroon, Gabon, Equatorial Guinea, and Congo"",""Details"":""The mandrill is a primate of the Old World monkey family, closely related to the baboons and even more closely to the drill. It is found in southern Cameroon, Gabon, Equatorial Guinea, and Congo."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/7\/75\/Mandrill_at_san_francisco_zoo.jpg\/220px-Mandrill_at_san_francisco_zoo.jpg"",""Population"":17000},{""Name"":""Proboscis Monkey"",""Location"":""Borneo"",""Details"":""The proboscis monkey or long-nosed monkey, known as the bekantan in Malay, is a reddish-brown arboreal Old World monkey that is endemic to the south-east Asian island of Borneo."",""Image"":""http:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/e\/e5\/Proboscis_Monkey_in_Borneo.jpg\/250px-Proboscis_Monkey_in_Borneo.jpg"",""Population"":15000},{""Name"":""Sebastian"",""Location"":""Seattle"",""Details"":""This little trouble maker lives in Seattle with James and loves traveling on adventures with James and tweeting @MotzMonkeys. He by far is an Android fanboy and is getting ready for the new Nexus 6P!"",""Image"":""http:\/\/www.refractored.com\/images\/sebastian.jpg"",""Population"":1},{""Name"":""Henry"",""Location"":""Phoenix"",""Details"":""An adorable Monkey who is traveling the world with Heather and live tweets his adventures @MotzMonkeys. His favorite platform is iOS by far and is excited for the new iPhone 6s!"",""Image"":""http:\/\/www.refractored.com\/images\/henry.jpg"",""Population"":1}]"; - monkeys = JsonConvert.DeserializeObject>(json); + monkeys = JsonSerializer.Deserialize>(json); } @@ -59,6 +60,40 @@ public void GetTest() Assert.AreEqual(cached.Count(), monkeys.Count()); } + [TestMethod] + public void GetTestJsonSerializerOptions() + { + JsonSerializerOptions options = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + string serializedString = JsonSerializer.Serialize(monkeys, options); + StringAssert.Contains(serializedString, "population"); + + // Save the camel-case string into the cache + barrel.Add(key: url, data: serializedString, expireIn: TimeSpan.FromDays(1)); + + // Get the value back out of the cache using the same json options + var cached = barrel.Get>(url, options); + Assert.IsNotNull(cached); + Assert.AreEqual(cached.Count(), monkeys.Count()); + } + + [TestMethod] + public void GetTestJsonTypeInfo() + { + var jsonTypeInfo = JsonContext.Default.IEnumerableMonkey; + string serializedString = JsonSerializer.Serialize(monkeys, jsonTypeInfo); + StringAssert.Contains(serializedString, "population"); + + // Save the camel-case string into the cache + barrel.Add(key: url, data: serializedString, expireIn: TimeSpan.FromDays(1)); + + // Get the value back out of the cache using the same json type info + var cached = barrel.Get>(url, jsonTypeInfo); + Assert.IsNotNull(cached); + Assert.AreEqual(cached.Count(), monkeys.Count()); + } [TestMethod] public void GetETagTest() @@ -256,6 +291,34 @@ public void AddTest() } + [TestMethod] + public void AddTestJsonSerializerOptions() + { + JsonSerializerOptions options = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + // Save the value into the cache using the options + barrel.Add(key: url, data: monkeys, expireIn: TimeSpan.FromDays(1), options); + + // Get the string value out of the cache to verify it was serialized using the supplied options + var serializedString = barrel.Get(url); + StringAssert.Contains(serializedString, "population"); + } + + [TestMethod] + public void AddTestJsonTypeInfo() + { + var jsonTypeInfo = JsonContext.Default.IEnumerableMonkey; + + // Save the value into the cache using the JsonTypeInfo + barrel.Add(key: url, data: monkeys, expireIn: TimeSpan.FromDays(1), jsonTypeInfo); + + // Get the string value out of the cache to verify it was serialized using the supplied options + var serializedString = barrel.Get(url); + StringAssert.Contains(serializedString, "population"); + } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] @@ -532,4 +595,10 @@ void PerformanceTestRunner (int threads, bool allowDuplicateKeys, int keysPerThr public partial class CustomDirBarrelTests : BarrelTests { } + + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] + [JsonSerializable(typeof(IEnumerable))] + public partial class JsonContext : JsonSerializerContext + { + } } diff --git a/src/MonkeyCache.TestsShared/HttpCacheTests.cs b/src/MonkeyCache.TestsShared/HttpCacheTests.cs index d969f68..0b1b8ed 100644 --- a/src/MonkeyCache.TestsShared/HttpCacheTests.cs +++ b/src/MonkeyCache.TestsShared/HttpCacheTests.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; namespace MonkeyCache.Tests { diff --git a/src/MonkeyCache.TestsShared/Monkey.cs b/src/MonkeyCache.TestsShared/Monkey.cs index 006d7f2..537580d 100644 --- a/src/MonkeyCache.TestsShared/Monkey.cs +++ b/src/MonkeyCache.TestsShared/Monkey.cs @@ -1,4 +1,4 @@ -// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// To parse this JSON data: // // using MonkeyCache.Tests; // @@ -7,45 +7,29 @@ namespace MonkeyCache.Tests { using System; - using System.Net; using System.Collections.Generic; - - using Newtonsoft.Json; + using System.Text.Json; public partial class Monkey { - [JsonProperty("Details")] public string Details { get; set; } - [JsonProperty("Image")] public string Image { get; set; } - [JsonProperty("Location")] public string Location { get; set; } - [JsonProperty("Name")] public string Name { get; set; } - [JsonProperty("Population")] public long Population { get; set; } } public partial class Monkey { - public static Monkey[] FromJson(string json) => JsonConvert.DeserializeObject(json, Converter.Settings); + public static Monkey[] FromJson(string json) => JsonSerializer.Deserialize(json); } public static class Serialize { - public static string ToJson(this Monkey[] self) => JsonConvert.SerializeObject(self, Converter.Settings); - } - - public class Converter - { - public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings - { - MetadataPropertyHandling = MetadataPropertyHandling.Ignore, - DateParseHandling = DateParseHandling.None, - }; + public static string ToJson(this Monkey[] self) => JsonSerializer.Serialize(self); } } diff --git a/src/MonkeyCache.TestsShared/MonkeyCache.TestsShared.projitems b/src/MonkeyCache.TestsShared/MonkeyCache.TestsShared.projitems index 0708664..7c6bd05 100644 --- a/src/MonkeyCache.TestsShared/MonkeyCache.TestsShared.projitems +++ b/src/MonkeyCache.TestsShared/MonkeyCache.TestsShared.projitems @@ -2,12 +2,19 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + net6.0 + false true 4b54b117-44fe-46f9-977e-7d5772da5d3f MonkeyCache.TestsShared + + + + + diff --git a/src/MonkeyCache/BarrelUtils.cs b/src/MonkeyCache/BarrelUtils.cs index fd62c0b..3d6a331 100644 --- a/src/MonkeyCache/BarrelUtils.cs +++ b/src/MonkeyCache/BarrelUtils.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Text; using System.IO; -#if __IOS__ || __MACOS__ +#if IOS || MACOS || MACCATALYST using Foundation; -#elif __ANDROID__ +#elif ANDROID using Android.App; #endif @@ -54,19 +54,19 @@ internal static string GetBasePath(string applicationId) if (applicationId.IndexOfAny(Path.GetInvalidPathChars()) != -1) throw new ArgumentException("ApplicationId has invalid characters"); - + if (string.IsNullOrWhiteSpace(basePath)) { // Gets full path based on device type. - #if __IOS__ || __MACOS__ +#if IOS || MACCATALYST basePath = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User)[0]; - #elif __ANDROID__ +#elif ANDROID basePath = Application.Context.CacheDir.AbsolutePath; - #elif __UWP__ || WINDOWS +#elif WINDOWS basePath = Windows.Storage.ApplicationData.Current.LocalCacheFolder.Path; - #else +#else basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - #endif +#endif } return Path.Combine(basePath, applicationId); diff --git a/src/MonkeyCache/HttpCache.cs b/src/MonkeyCache/HttpCache.cs index 0ee23aa..a555a40 100644 --- a/src/MonkeyCache/HttpCache.cs +++ b/src/MonkeyCache/HttpCache.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -75,6 +76,7 @@ public static class HttpCacheExtensions /// If we should force the update or not /// If throttled or not /// The new or cached response. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Only using string in IBarrel.Get and Add.")] public static async Task SendCachedAsync(this HttpClient http, IBarrel barrel, HttpRequestMessage req, TimeSpan expireIn, bool forceUpdate = false, bool throttled = true) { var url = req.RequestUri.ToString(); @@ -118,7 +120,7 @@ public static async Task SendCachedAsync(this HttpClient http, IBarrel b // Cache it? var newEtag = r.Headers.ETag != null ? r.Headers.ETag.Tag : null; if (!string.IsNullOrEmpty(newEtag) && newEtag != etag) - barrel.Add(url, c, expireIn, newEtag); + barrel.Add(url, c, expireIn, eTag: newEtag); return c; } else { diff --git a/src/MonkeyCache/IBarrel.cs b/src/MonkeyCache/IBarrel.cs index e8e5772..76486ff 100644 --- a/src/MonkeyCache/IBarrel.cs +++ b/src/MonkeyCache/IBarrel.cs @@ -1,6 +1,8 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; namespace MonkeyCache { @@ -21,9 +23,21 @@ public interface IBarrel /// Key to use /// Data to store of type T /// How long in the future the item should expire + /// Specific json serializer options to use /// eTag to use if needed - /// Specific json serialization to use - void Add(string key, T data, TimeSpan expireIn, string eTag = null, JsonSerializerSettings jsonSerializationSettings = null); + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + void Add(string key, T data, TimeSpan expireIn, JsonSerializerOptions options = null, string eTag = null); + + /// + /// Add an item to the barrel + /// + /// Type of item + /// Key to use + /// Data to store of type T + /// How long in the future the item should expire + /// Metadata about the type to convert. + /// eTag to use if needed + void Add(string key, T data, TimeSpan expireIn, JsonTypeInfo jsonTypeInfo, string eTag = null); /// /// Empty a set of keys @@ -54,18 +68,31 @@ public interface IBarrel /// State to get: Multiple with flags: CacheState.Active | CacheState.Expired /// The keys IEnumerable GetKeys(CacheState state = CacheState.Active); - + + /// + /// Get an object for the key + /// + /// Type of object to get + /// Key to use + /// Specific json serializer options to use + /// The object back if it exists, else null + /// + /// When AutoExpire is set to true, Get{T} will return NULL if the item is expired + /// + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] + T Get(string key, JsonSerializerOptions options = null); + /// /// Get an object for the key /// /// Type of object to get /// Key to use - /// json serialization settings to use. + /// Metadata about the type to convert. /// The object back if it exists, else null /// - /// When AutoExpire is set to true, Get will return NULL if the item is expired + /// When AutoExpire is set to true, Get{T} will return NULL if the item is expired /// - T Get(string key, JsonSerializerSettings jsonSettings = null); + T Get(string key, JsonTypeInfo jsonTypeInfo); /// /// Get the eTag for a key diff --git a/src/MonkeyCache/MonkeyCache.csproj b/src/MonkeyCache/MonkeyCache.csproj index a0bedda..ccd4394 100644 --- a/src/MonkeyCache/MonkeyCache.csproj +++ b/src/MonkeyCache/MonkeyCache.csproj @@ -1,35 +1,36 @@ - - - netstandard2.0;MonoAndroid10.0;Xamarin.iOS10;Xamarin.TVOS10;Xamarin.Mac20;net6.0-android;net6.0-ios;net6.0-maccatalyst - $(TargetFrameworks);uap10.0.19041;net6.0-windows10.0.19041;net461 - $(AssemblyName) ($(TargetFramework)) - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 - James Montemagno - MonkeyCache - true - https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png - en - https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE - James Montemagno - https://github.com/jamesmontemagno/monkey-cache - A simple caching library to cache any data structure for a specific amount of time in any .NET application. Requires backend MonkeyCache package to be installed. - xamarin, windows, ios, android, cache, http - 🐒 MonkeyCache.Core - A .NET Caching Library - A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. - 2022 Refractored LLC & James Montemagno - https://github.com/jamesmontemagno/monkey-cache - See: https://github.com/jamesmontemagno/monkey-cache + + + net6.0;net6.0-android;net6.0-ios;net6.0-maccatalyst + $(TargetFrameworks);net6.0-windows10.0.19041 + $(AssemblyName) ($(TargetFramework)) + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + 2.0.0.0 + James Montemagno + MonkeyCache + true + https://raw.githubusercontent.com/jamesmontemagno/monkey-cache/master/art/MonkeyCacheSmall.png + en + https://github.com/jamesmontemagno/monkey-cache/blob/master/LICENSE + James Montemagno + https://github.com/jamesmontemagno/monkey-cache + A simple caching library to cache any data structure for a specific amount of time in any .NET application. Requires backend MonkeyCache package to be installed. + xamarin, windows, ios, android, cache, http + 🐒 MonkeyCache.Core - A .NET Caching Library + A simple caching library to cache any data structure for a specific amount of time in any .NET application. Additionally, offers simple HTTP methods for caching web request data. + 2022 Refractored LLC & James Montemagno + https://github.com/jamesmontemagno/monkey-cache + See: https://github.com/jamesmontemagno/monkey-cache - default + default - $(DefineConstants); - true - MonkeyCache - true + $(DefineConstants); + true + MonkeyCache + true portable + true true @@ -46,21 +47,10 @@ + 21.0 10.0 - 10.0 13.1 - 10.14 - 21.0 10.0.16299.0 10.0.16299.0 - - 10.0.16299.0 - - - - - - -