From a09a60f95d66bd3d6b18cd53a0d4206466fb494c Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Tue, 27 Apr 2021 17:32:49 +0900 Subject: [PATCH 01/16] Switch default converter from Utf8Json-based to System.Text.Json-based --- src/CloudStructures/CloudStructures.csproj | 2 +- ...tf8JsonConverter.cs => SystemTextJsonConverter.cs} | 11 +++++------ src/CloudStructures/Converters/ValueConverter.cs | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) rename src/CloudStructures/Converters/{Utf8JsonConverter.cs => SystemTextJsonConverter.cs} (65%) diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index 365bd0d..9f84141 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/CloudStructures/Converters/Utf8JsonConverter.cs b/src/CloudStructures/Converters/SystemTextJsonConverter.cs similarity index 65% rename from src/CloudStructures/Converters/Utf8JsonConverter.cs rename to src/CloudStructures/Converters/SystemTextJsonConverter.cs index e95510d..483590c 100644 --- a/src/CloudStructures/Converters/Utf8JsonConverter.cs +++ b/src/CloudStructures/Converters/SystemTextJsonConverter.cs @@ -1,14 +1,13 @@ -using Utf8Json; -using Utf8Json.Resolvers; +using System.Text.Json; namespace CloudStructures.Converters { /// - /// Provides value converter using Utf8Json. + /// Provides value converter using System.Text.Json. /// - public sealed class Utf8JsonConverter : IValueConverter + public sealed class SystemTextJsonConverter : IValueConverter { /// /// Serialize value to binary. @@ -17,7 +16,7 @@ public sealed class Utf8JsonConverter : IValueConverter /// /// public byte[] Serialize(T value) - => JsonSerializer.Serialize(value, StandardResolver.AllowPrivate); + => JsonSerializer.SerializeToUtf8Bytes(value); /// @@ -27,6 +26,6 @@ public byte[] Serialize(T value) /// /// public T Deserialize(byte[] value) - => JsonSerializer.Deserialize(value, StandardResolver.AllowPrivate); + => JsonSerializer.Deserialize(value); } } diff --git a/src/CloudStructures/Converters/ValueConverter.cs b/src/CloudStructures/Converters/ValueConverter.cs index 31d7ec7..7bb4649 100644 --- a/src/CloudStructures/Converters/ValueConverter.cs +++ b/src/CloudStructures/Converters/ValueConverter.cs @@ -25,7 +25,7 @@ internal sealed class ValueConverter /// /// public ValueConverter(IValueConverter customConverter) - => this.CustomConverter = customConverter ?? new Utf8JsonConverter(); // fallback + => this.CustomConverter = customConverter ?? new SystemTextJsonConverter(); // fallback #endregion From ef821b85f931029cd00fef01d0f36016044df92b Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Tue, 27 Apr 2021 18:50:18 +0900 Subject: [PATCH 02/16] fix document comment --- src/CloudStructures/RedisConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CloudStructures/RedisConnection.cs b/src/CloudStructures/RedisConnection.cs index 5273727..854c963 100644 --- a/src/CloudStructures/RedisConnection.cs +++ b/src/CloudStructures/RedisConnection.cs @@ -73,7 +73,7 @@ internal IServer[] Servers /// Creates instance. /// /// - /// If null, use Utf8JsonConverter as default. + /// If null, use as default. /// /// public RedisConnection(RedisConfig config, IValueConverter converter = null, IConnectionEventHandler handler = null, TextWriter logger = null) From d0cff6a3ea1c7d24799bb401422d1dabf5422658 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Tue, 27 Apr 2021 21:09:46 +0900 Subject: [PATCH 03/16] Remove specified converter project --- ...udStructures.Converters.MessagePack.csproj | 34 ------------------- .../MessagePackConverter.cs | 32 ----------------- src/CloudStructures.sln | 6 ---- 3 files changed, 72 deletions(-) delete mode 100644 src/CloudStructures.Converters.MessagePack/CloudStructures.Converters.MessagePack.csproj delete mode 100644 src/CloudStructures.Converters.MessagePack/MessagePackConverter.cs diff --git a/src/CloudStructures.Converters.MessagePack/CloudStructures.Converters.MessagePack.csproj b/src/CloudStructures.Converters.MessagePack/CloudStructures.Converters.MessagePack.csproj deleted file mode 100644 index 14f9b5a..0000000 --- a/src/CloudStructures.Converters.MessagePack/CloudStructures.Converters.MessagePack.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - netstandard2.0 - CloudStructures.Converters - 8.0 - disable - - true - CloudStructures.Converters.MessagePack - 2.3.2.0 - Provides MessagePackConverter for CloudStructures. - https://github.com/neuecc/CloudStructures - - Redis, Redis Client, O/R Mapping, MessagePack, Converter - true - MIT - $(PackageProjectUrl) - Git - - neuecc, xin9le - Copyright© neuecc, xin9le - - - - - - - - - - - - diff --git a/src/CloudStructures.Converters.MessagePack/MessagePackConverter.cs b/src/CloudStructures.Converters.MessagePack/MessagePackConverter.cs deleted file mode 100644 index 58d193a..0000000 --- a/src/CloudStructures.Converters.MessagePack/MessagePackConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MessagePack; - - - -namespace CloudStructures.Converters -{ - /// - /// Provides value converter using MessagePack for C#. - /// - public sealed class MessagePackConverter : IValueConverter - { - /// - /// Serialize value to binary. - /// - /// Data type - /// - /// - public byte[] Serialize(T value) - => LZ4MessagePackSerializer.Serialize(value); - - - /// - /// Deserialize value from binary. - /// - /// Data type - /// - /// - public T Deserialize(byte[] value) - => LZ4MessagePackSerializer.Deserialize(value); - } -} - diff --git a/src/CloudStructures.sln b/src/CloudStructures.sln index 11b26a6..d6b99ca 100644 --- a/src/CloudStructures.sln +++ b/src/CloudStructures.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.31205.134 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStructures", "CloudStructures\CloudStructures.csproj", "{737EFD85-803D-48F6-AFB4-4637773E32A8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStructures.Converters.MessagePack", "CloudStructures.Converters.MessagePack\CloudStructures.Converters.MessagePack.csproj", "{FE910B93-6E35-4FCC-9EAD-A6C4E27D9EF5}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -17,10 +15,6 @@ Global {737EFD85-803D-48F6-AFB4-4637773E32A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {737EFD85-803D-48F6-AFB4-4637773E32A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {737EFD85-803D-48F6-AFB4-4637773E32A8}.Release|Any CPU.Build.0 = Release|Any CPU - {FE910B93-6E35-4FCC-9EAD-A6C4E27D9EF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE910B93-6E35-4FCC-9EAD-A6C4E27D9EF5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE910B93-6E35-4FCC-9EAD-A6C4E27D9EF5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE910B93-6E35-4FCC-9EAD-A6C4E27D9EF5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d1a8ee7b259393d356daacd9aa5fb3b2ea687430 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 01:16:50 +0900 Subject: [PATCH 04/16] Enable nullability --- src/CloudStructures/CloudStructures.csproj | 6 +-- .../Converters/ValueConverter.cs | 6 +-- .../Internals/EnumerableExtensions.cs | 13 ++--- .../Internals/RedisOperationHelpers.cs | 6 --- src/CloudStructures/RedisConfig.cs | 7 ++- src/CloudStructures/RedisConnection.cs | 13 +++-- src/CloudStructures/RedisResult.cs | 48 ++++++++----------- src/CloudStructures/Structures/RedisBit.cs | 6 +-- .../Structures/RedisDictionary.cs | 25 +++------- src/CloudStructures/Structures/RedisGeo.cs | 2 +- .../Structures/RedisHashSet.cs | 3 +- .../Structures/RedisHyperLogLog.cs | 2 +- src/CloudStructures/Structures/RedisList.cs | 6 +-- src/CloudStructures/Structures/RedisLock.cs | 2 +- src/CloudStructures/Structures/RedisLua.cs | 9 ++-- src/CloudStructures/Structures/RedisSet.cs | 14 +++--- .../Structures/RedisSortedSet.cs | 25 ++++++++-- src/CloudStructures/Structures/RedisString.cs | 2 +- 18 files changed, 88 insertions(+), 107 deletions(-) diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index 365bd0d..80d7b76 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -1,9 +1,9 @@  - netstandard2.0 - 8.0 - disable + net5 + 9.0 + enable true CloudStructures diff --git a/src/CloudStructures/Converters/ValueConverter.cs b/src/CloudStructures/Converters/ValueConverter.cs index 31d7ec7..4257bac 100644 --- a/src/CloudStructures/Converters/ValueConverter.cs +++ b/src/CloudStructures/Converters/ValueConverter.cs @@ -24,7 +24,7 @@ internal sealed class ValueConverter /// Creates instance. /// /// - public ValueConverter(IValueConverter customConverter) + public ValueConverter(IValueConverter? customConverter) => this.CustomConverter = customConverter ?? new Utf8JsonConverter(); // fallback #endregion @@ -40,7 +40,7 @@ public RedisValue Serialize(T value) { var converter = PrimitiveConverterCache.Converter; return converter == null - ? (RedisValue)this.CustomConverter.Serialize(value) + ? this.CustomConverter.Serialize(value) : converter.Serialize(value); } @@ -111,7 +111,7 @@ private static class PrimitiveConverterCache /// /// Gets converter. /// - public static IRedisValueConverter Converter { get; } + public static IRedisValueConverter? Converter { get; } /// diff --git a/src/CloudStructures/Internals/EnumerableExtensions.cs b/src/CloudStructures/Internals/EnumerableExtensions.cs index a02cbcc..17d8ee8 100644 --- a/src/CloudStructures/Internals/EnumerableExtensions.cs +++ b/src/CloudStructures/Internals/EnumerableExtensions.cs @@ -18,11 +18,7 @@ internal static class EnumerableExtensions /// /// public static bool IsEmpty(this IEnumerable source) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - return !source.Any(); - } + => !source.Any(); /// @@ -37,9 +33,6 @@ public static bool IsEmpty(this IEnumerable source) /// public static IEnumerable Select(this IEnumerable source, TState state, Func selector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (selector == null) throw new ArgumentNullException(nameof(selector)); - foreach (var x in source) yield return selector(x, state); } @@ -52,13 +45,13 @@ public static IEnumerable Select(this IEnumerable /// If true, returns empty sequence when source collection is null. If false, throws /// - public static IEnumerable Materialize(this IEnumerable source, bool nullToEmpty = true) + public static IEnumerable Materialize(this IEnumerable? source, bool nullToEmpty = true) { if (source == null) { if (nullToEmpty) return Enumerable.Empty(); - throw new ArgumentNullException("source is null."); + throw new ArgumentNullException(nameof(source)); } if (source is ICollection) return source; if (source is IReadOnlyCollection) return source; diff --git a/src/CloudStructures/Internals/RedisOperationHelpers.cs b/src/CloudStructures/Internals/RedisOperationHelpers.cs index b9cce0f..8c6753f 100644 --- a/src/CloudStructures/Internals/RedisOperationHelpers.cs +++ b/src/CloudStructures/Internals/RedisOperationHelpers.cs @@ -25,9 +25,6 @@ internal static class RedisOperationHelpers public static async Task ExecuteWithExpiryAsync(this TRedis structure, Func command, TArgs args, TimeSpan? expiry, CommandFlags flags) where TRedis : IRedisStructure { - if (structure == null) throw new ArgumentNullException(nameof(structure)); - if (command == null) throw new ArgumentNullException(nameof(command)); - if (expiry.HasValue) { //--- Execute multiple commands in tracsaction @@ -60,9 +57,6 @@ public static async Task ExecuteWithExpiryAsync(this TRedis struc public static async Task ExecuteWithExpiryAsync(this TRedis structure, Func> command, TArgs args, TimeSpan? expiry, CommandFlags flags) where TRedis : IRedisStructure { - if (structure == null) throw new ArgumentNullException(nameof(structure)); - if (command == null) throw new ArgumentNullException(nameof(command)); - if (expiry.HasValue) { //--- Execute multiple commands in tracsaction diff --git a/src/CloudStructures/RedisConfig.cs b/src/CloudStructures/RedisConfig.cs index 8006546..72bbe93 100644 --- a/src/CloudStructures/RedisConfig.cs +++ b/src/CloudStructures/RedisConfig.cs @@ -1,5 +1,4 @@ -using System; -using StackExchange.Redis; +using StackExchange.Redis; @@ -54,8 +53,8 @@ public RedisConfig(string name, string connectionString, int? database = default /// public RedisConfig(string name, ConfigurationOptions options, int? database = default) { - this.Name = name ?? throw new ArgumentNullException(nameof(name)); - this.Options = options ?? throw new ArgumentNullException(nameof(options)); + this.Name = name; + this.Options = options; this.Database = database ?? options.DefaultDatabase; } #endregion diff --git a/src/CloudStructures/RedisConnection.cs b/src/CloudStructures/RedisConnection.cs index 5273727..932fae2 100644 --- a/src/CloudStructures/RedisConnection.cs +++ b/src/CloudStructures/RedisConnection.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.IO; using System.Linq; using CloudStructures.Converters; @@ -32,13 +31,13 @@ public sealed class RedisConnection /// /// Gets connection event handler. /// - private IConnectionEventHandler Handler { get; } + private IConnectionEventHandler? Handler { get; } /// /// Gets logger. /// - private TextWriter Logger { get; } + private TextWriter? Logger { get; } /// @@ -76,9 +75,9 @@ internal IServer[] Servers /// If null, use Utf8JsonConverter as default. /// /// - public RedisConnection(RedisConfig config, IValueConverter converter = null, IConnectionEventHandler handler = null, TextWriter logger = null) + public RedisConnection(RedisConfig config, IValueConverter? converter = null, IConnectionEventHandler? handler = null, TextWriter? logger = null) { - this.Config = config ?? throw new ArgumentNullException(nameof(config)); + this.Config = config; this.Converter = new ValueConverter(converter); this.Handler = handler; this.Logger = logger; @@ -124,7 +123,7 @@ public ConnectionMultiplexer GetConnection() } } private readonly object gate = new object(); - private ConnectionMultiplexer connection = null; + private ConnectionMultiplexer? connection = null; #endregion } } diff --git a/src/CloudStructures/RedisResult.cs b/src/CloudStructures/RedisResult.cs index 1031a5d..cdb9f9c 100644 --- a/src/CloudStructures/RedisResult.cs +++ b/src/CloudStructures/RedisResult.cs @@ -30,8 +30,8 @@ public readonly struct RedisResult /// public T Value => this.HasValue - ? this.value - : throw new InvalidOperationException("has no value."); + ? this.value + : throw new InvalidOperationException("has no value."); private readonly T value; #endregion @@ -54,8 +54,8 @@ internal RedisResult(T value) /// Converts to string. /// /// - public override string ToString() - => this.HasValue ? this.Value.ToString() : null; + public override string? ToString() + => this.HasValue ? this.Value?.ToString() : null; #endregion @@ -64,8 +64,8 @@ public override string ToString() /// Gets value. Returns null if value doesn't exists. /// /// - public object GetValueOrNull() - => this.HasValue ? (object)this.Value : null; + public object? GetValueOrNull() + => this.HasValue ? this.Value : null; /// @@ -73,7 +73,7 @@ public object GetValueOrNull() /// /// /// - public T GetValueOrDefault(T @default = default) + public T? GetValueOrDefault(T? @default = default) => this.HasValue ? this.Value : @default; @@ -82,12 +82,8 @@ public T GetValueOrDefault(T @default = default) /// /// /// - public T GetValueOrDefault(Func valueFactory) - { - if (valueFactory == null) - throw new ArgumentNullException(nameof(valueFactory)); - return this.HasValue ? this.Value : valueFactory(); - } + public T? GetValueOrDefault(Func valueFactory) + => this.HasValue ? this.Value : valueFactory(); #endregion } @@ -117,8 +113,8 @@ public readonly struct RedisResultWithExpiry /// public T Value => this.HasValue - ? this.value - : throw new InvalidOperationException("has no value."); + ? this.value + : throw new InvalidOperationException("has no value."); private readonly T value; @@ -149,8 +145,8 @@ internal RedisResultWithExpiry(T value, TimeSpan? expiry) /// Converts to string. /// /// - public override string ToString() - => this.HasValue ? this.Value.ToString() : null; + public override string? ToString() + => this.HasValue ? this.Value?.ToString() : null; #endregion @@ -159,8 +155,8 @@ public override string ToString() /// Gets value. Returns null if value doesn't exists. /// /// - public object GetValueOrNull() - => this.HasValue ? (object)this.Value : null; + public object? GetValueOrNull() + => this.HasValue ? this.Value : null; /// @@ -168,7 +164,7 @@ public object GetValueOrNull() /// /// /// - public T GetValueOrDefault(T @default = default) + public T? GetValueOrDefault(T? @default = default) => this.HasValue ? this.Value : @default; @@ -177,12 +173,8 @@ public T GetValueOrDefault(T @default = default) /// /// /// - public T GetValueOrDefault(Func valueFactory) - { - if (valueFactory == null) - throw new ArgumentNullException(nameof(valueFactory)); - return this.HasValue ? this.Value : valueFactory(); - } + public T? GetValueOrDefault(Func valueFactory) + => this.HasValue ? this.Value : valueFactory(); #endregion } @@ -206,7 +198,7 @@ public static RedisResult ToResult(this in RedisValue value, ValueConverte return RedisResult.Default; var converted = converter.Deserialize(value); - return new RedisResult(converted); + return new(converted); } @@ -223,7 +215,7 @@ public static RedisResultWithExpiry ToResult(this in RedisValueWithExpiry return RedisResultWithExpiry.Default; var converted = converter.Deserialize(value.Value); - return new RedisResultWithExpiry(converted, value.Expiry); + return new(converted, value.Expiry); } } } diff --git a/src/CloudStructures/Structures/RedisBit.cs b/src/CloudStructures/Structures/RedisBit.cs index 576bad0..d49fdef 100644 --- a/src/CloudStructures/Structures/RedisBit.cs +++ b/src/CloudStructures/Structures/RedisBit.cs @@ -43,7 +43,7 @@ namespace CloudStructures.Structures /// public RedisBit(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } @@ -81,8 +81,8 @@ public Task OperationAsync(Bitwise operation, RedisBit first, RedisBit? se /// public Task OperationAsync(Bitwise operation, IReadOnlyCollection bits, CommandFlags flags = CommandFlags.None) { - if (bits == null) throw new ArgumentNullException(nameof(bits)); - if (bits.Count == 0) throw new ArgumentException("bits length is 0."); + if (bits.Count == 0) + throw new ArgumentException("bits length is 0."); var keys = bits.Select(x => x.Key).ToArray(); return this.Connection.Database.StringBitOperationAsync(operation, this.Key, keys, flags); diff --git a/src/CloudStructures/Structures/RedisDictionary.cs b/src/CloudStructures/Structures/RedisDictionary.cs index 984db0b..6a1f7d8 100644 --- a/src/CloudStructures/Structures/RedisDictionary.cs +++ b/src/CloudStructures/Structures/RedisDictionary.cs @@ -15,6 +15,7 @@ namespace CloudStructures.Structures /// Key type /// Value type public readonly struct RedisDictionary : IRedisStructureWithExpiry + where TKey : notnull { #region IRedisStructureWithExpiry implementations /// @@ -45,7 +46,7 @@ namespace CloudStructures.Structures /// public RedisDictionary(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } @@ -132,7 +133,7 @@ public Task ExistsAsync(TKey field, CommandFlags flags = CommandFlags.None /// /// HGETALL : https://redis.io/commands/hgetall /// - public async Task> GetAllAsync(IEqualityComparer dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) + public async Task> GetAllAsync(IEqualityComparer? dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) { var comparer = dictionaryEqualityComparer ?? EqualityComparer.Default; var entries = await this.Connection.Database.HashGetAllAsync(this.Key, flags).ConfigureAwait(false); @@ -161,7 +162,7 @@ public async Task> GetAsync(TKey field, CommandFlags flags = /// /// HMGET : https://redis.io/commands/hmget /// - public async Task> GetAsync(IEnumerable fields, IEqualityComparer dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) + public async Task> GetAsync(IEnumerable fields, IEqualityComparer? dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) { fields = fields.Materialize(false); var comparer = dictionaryEqualityComparer ?? EqualityComparer.Default; @@ -293,9 +294,6 @@ public async Task ValuesAsync(CommandFlags flags = CommandFlags.None) /// public async Task GetOrSetAsync(TKey field, Func valueFactory, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None) { - if (valueFactory == null) - throw new ArgumentNullException(nameof(valueFactory)); - var result = await this.GetAsync(field, flags).ConfigureAwait(false); if (result.HasValue) { @@ -316,9 +314,6 @@ public async Task GetOrSetAsync(TKey field, Func valueFact /// public async Task GetOrSetAsync(TKey field, Func> valueFactory, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None) { - if (valueFactory == null) - throw new ArgumentNullException(nameof(valueFactory)); - var result = await this.GetAsync(field, flags).ConfigureAwait(false); if (result.HasValue) { @@ -337,11 +332,8 @@ public async Task GetOrSetAsync(TKey field, Func> val /// HMGET : https://redis.io/commands/hmget /// HMSET : https://redis.io/commands/hmset /// - public async Task> GetOrSetAsync(IEnumerable fields, Func, IEnumerable>> valueFactory, TimeSpan? expiry = null, IEqualityComparer dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) + public async Task> GetOrSetAsync(IEnumerable fields, Func, IEnumerable>> valueFactory, TimeSpan? expiry = null, IEqualityComparer? dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) { - if (valueFactory == null) - throw new ArgumentNullException(nameof(valueFactory)); - var comparer = dictionaryEqualityComparer ?? EqualityComparer.Default; fields = fields.Materialize(false); if (fields.IsEmpty()) @@ -379,11 +371,8 @@ public async Task> GetOrSetAsync(IEnumerable fiel /// HMGET : https://redis.io/commands/hmget /// HMSET : https://redis.io/commands/hmset /// - public async Task> GetOrSetAsync(IEnumerable fields, Func, Task>>> valueFactory, TimeSpan? expiry = null, IEqualityComparer dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) + public async Task> GetOrSetAsync(IEnumerable fields, Func, Task>>> valueFactory, TimeSpan? expiry = null, IEqualityComparer? dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) { - if (valueFactory == null) - throw new ArgumentNullException(nameof(valueFactory)); - var comparer = dictionaryEqualityComparer ?? EqualityComparer.Default; fields = fields.Materialize(false); if (fields.IsEmpty()) @@ -440,7 +429,7 @@ public async Task> GetAndDeleteAsync(TKey field, CommandFlag /// HMGET : https://redis.io/commands/hmget /// HDEL : https://redis.io/commands/hdel /// - public async Task> GetAndDeleteAsync(IEnumerable fields, IEqualityComparer dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) + public async Task> GetAndDeleteAsync(IEnumerable fields, IEqualityComparer? dictionaryEqualityComparer = null, CommandFlags flags = CommandFlags.None) { //--- GetAsync fields = fields.Materialize(false); diff --git a/src/CloudStructures/Structures/RedisGeo.cs b/src/CloudStructures/Structures/RedisGeo.cs index 8137733..df1cd57 100644 --- a/src/CloudStructures/Structures/RedisGeo.cs +++ b/src/CloudStructures/Structures/RedisGeo.cs @@ -45,7 +45,7 @@ namespace CloudStructures.Structures /// public RedisGeo(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } diff --git a/src/CloudStructures/Structures/RedisHashSet.cs b/src/CloudStructures/Structures/RedisHashSet.cs index 85f6675..4fcf231 100644 --- a/src/CloudStructures/Structures/RedisHashSet.cs +++ b/src/CloudStructures/Structures/RedisHashSet.cs @@ -15,6 +15,7 @@ namespace CloudStructures.Structures /// /// Data type public readonly struct RedisHashSet : IRedisStructureWithExpiry + where T : notnull { #region IRedisStructureWithExpiry implementations /// @@ -45,7 +46,7 @@ namespace CloudStructures.Structures /// public RedisHashSet(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } diff --git a/src/CloudStructures/Structures/RedisHyperLogLog.cs b/src/CloudStructures/Structures/RedisHyperLogLog.cs index c42982e..d83d840 100644 --- a/src/CloudStructures/Structures/RedisHyperLogLog.cs +++ b/src/CloudStructures/Structures/RedisHyperLogLog.cs @@ -44,7 +44,7 @@ namespace CloudStructures.Structures /// public RedisHyperLogLog(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } diff --git a/src/CloudStructures/Structures/RedisList.cs b/src/CloudStructures/Structures/RedisList.cs index e4a7819..77b4aed 100644 --- a/src/CloudStructures/Structures/RedisList.cs +++ b/src/CloudStructures/Structures/RedisList.cs @@ -44,7 +44,7 @@ namespace CloudStructures.Structures /// public RedisList(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } @@ -277,7 +277,7 @@ public Task SortAndStoreAsync(RedisList destination, long skip = 0, lon { //--- I don't know if serialization is necessary or not, so I will fix the default value. RedisValue by = default; - RedisValue[] get = default; + RedisValue[]? get = default; return this.Connection.Database.SortAndStoreAsync(destination.Key, this.Key, skip, take, order, sortType, by, get, flags); } @@ -289,7 +289,7 @@ public async Task SortAsync(long skip = 0, long take = -1, Order order = Or { //--- I don't know if serialization is necessary or not, so I will fix the default value. RedisValue by = default; - RedisValue[] get = default; + RedisValue[]? get = default; var values = await this.Connection.Database.SortAsync(this.Key, skip, take, order, sortType, by, get, flags).ConfigureAwait(false); return values.Select(this.Connection.Converter, (x, c) => c.Deserialize(x)).ToArray(); } diff --git a/src/CloudStructures/Structures/RedisLock.cs b/src/CloudStructures/Structures/RedisLock.cs index 97aea50..1218b50 100644 --- a/src/CloudStructures/Structures/RedisLock.cs +++ b/src/CloudStructures/Structures/RedisLock.cs @@ -34,7 +34,7 @@ namespace CloudStructures.Structures /// public RedisLock(RedisConnection connection, RedisKey key) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; } #endregion diff --git a/src/CloudStructures/Structures/RedisLua.cs b/src/CloudStructures/Structures/RedisLua.cs index 8943da1..b50f87a 100644 --- a/src/CloudStructures/Structures/RedisLua.cs +++ b/src/CloudStructures/Structures/RedisLua.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using StackExchange.Redis; @@ -34,7 +33,7 @@ namespace CloudStructures.Structures /// public RedisLua(RedisConnection connection, RedisKey key) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; } #endregion @@ -47,14 +46,14 @@ public RedisLua(RedisConnection connection, RedisKey key) /// /// EVALSHA : http://redis.io/commands/evalsha /// - public Task ScriptEvaluateAsync(string script, RedisKey[] keys = null, RedisValue[] values = null, CommandFlags flags = CommandFlags.None) + public Task ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) => this.Connection.Database.ScriptEvaluateAsync(script, keys, values, flags); /// /// EVALSHA : http://redis.io/commands/evalsha /// - public async Task> ScriptEvaluateAsync(string script, RedisKey[] keys = null, RedisValue[] values = null, CommandFlags flags = CommandFlags.None) + public async Task> ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) { var result = await this.Connection.Database.ScriptEvaluateAsync(script, keys, values, flags).ConfigureAwait(false); if (result.IsNull) diff --git a/src/CloudStructures/Structures/RedisSet.cs b/src/CloudStructures/Structures/RedisSet.cs index 323fb73..9ae5cc9 100644 --- a/src/CloudStructures/Structures/RedisSet.cs +++ b/src/CloudStructures/Structures/RedisSet.cs @@ -44,7 +44,7 @@ namespace CloudStructures.Structures /// public RedisSet(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } @@ -125,8 +125,8 @@ public Task CombineAndStoreAsync(SetOperation operation, RedisSet desti /// public Task CombineAndStoreAsync(SetOperation operation, RedisSet destination, IReadOnlyCollection> others, CommandFlags flags = CommandFlags.None) { - if (others == null) throw new ArgumentNullException(nameof(others)); - if (others.Count == 0) throw new ArgumentException("others length is 0."); + if (others.Count == 0) + throw new ArgumentException("others length is 0."); var keys = others.Select(x => x.Key).Concat(new []{ this.Key }).ToArray(); return this.Connection.Database.SetCombineAndStoreAsync(operation, destination.Key, keys, flags); @@ -154,8 +154,8 @@ public async Task CombineAsync(SetOperation operation, RedisSet other, C /// It does not work unless you pass keys located the same server. public async Task CombineAsync(SetOperation operation, IReadOnlyCollection> others, CommandFlags flags = CommandFlags.None) { - if (others == null) throw new ArgumentNullException(nameof(others)); - if (others.Count == 0) throw new ArgumentException("others length is 0."); + if (others.Count == 0) + throw new ArgumentException("others length is 0."); var keys = new []{ this.Key }.Concat(others.Select(x => x.Key)).ToArray(); var values = await this.Connection.Database.SetCombineAsync(operation, keys, flags).ConfigureAwait(false); @@ -251,7 +251,7 @@ public Task SortAndStoreAsync(RedisSet destination, long skip = 0, long { //--- I don't know if serialization is necessary or not, so I will fix the default value. RedisValue by = default; - RedisValue[] get = default; + RedisValue[]? get = default; return this.Connection.Database.SortAndStoreAsync(destination.Key, this.Key, skip, take, order, sortType, by, get, flags); } @@ -263,7 +263,7 @@ public async Task SortAsync(long skip = 0, long take = -1, Order order = Or { //--- I don't know if serialization is necessary or not, so I will fix the default value. RedisValue by = default; - RedisValue[] get = default; + RedisValue[]? get = default; var values = await this.Connection.Database.SortAsync(this.Key, skip, take, order, sortType, by, get, flags).ConfigureAwait(false); return values.Select(this.Connection.Converter, (x, c) => c.Deserialize(x)).ToArray(); } diff --git a/src/CloudStructures/Structures/RedisSortedSet.cs b/src/CloudStructures/Structures/RedisSortedSet.cs index 560c291..d61a03f 100644 --- a/src/CloudStructures/Structures/RedisSortedSet.cs +++ b/src/CloudStructures/Structures/RedisSortedSet.cs @@ -45,7 +45,7 @@ namespace CloudStructures.Structures /// public RedisSortedSet(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } @@ -123,7 +123,7 @@ public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet /// ZUNIONSTORE : https://redis.io/commands/zunionstore /// ZINTERSTORE : https://redis.io/commands/zinterstore /// - public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet destination, IReadOnlyCollection> others, double[] weights = default, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) + public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet destination, IReadOnlyCollection> others, double[]? weights = default, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { if (others == null) throw new ArgumentNullException(nameof(others)); if (others.Count == 0) throw new ArgumentNullException("others length is 0."); @@ -243,7 +243,7 @@ public async Task[]> RangeByScoreWithScoresAsync(double s /// ZRANGEBYLEX : https://redis.io/commands/zrangebylex /// ZREVRANGEBYLEX : https://redis.io/commands/zrevrangebylex /// - public async Task RangeByValueAsync(T min = default, T max = default, Exclude exclude = Exclude.None, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None) + public async Task RangeByValueAsync(T min, T max, Exclude exclude, long skip, long take = -1, CommandFlags flags = CommandFlags.None) { var minValue = this.Connection.Converter.Serialize(min); var maxValue = this.Connection.Converter.Serialize(max); @@ -254,6 +254,21 @@ public async Task RangeByValueAsync(T min = default, T max = default, Exclu } + /// + /// ZRANGEBYLEX : https://redis.io/commands/zrangebylex + /// ZREVRANGEBYLEX : https://redis.io/commands/zrevrangebylex + /// + public async Task RangeByValueAsync(T? min = default, T? max = default, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None) + { + var minValue = this.Connection.Converter.Serialize(min); + var maxValue = this.Connection.Converter.Serialize(max); + var values = await this.Connection.Database.SortedSetRangeByValueAsync(this.Key, minValue, maxValue, exclude, order, skip, take, flags).ConfigureAwait(false); + return values + .Select(this.Connection.Converter, (x, c) => c.Deserialize(x)) + .ToArray(); + } + + /// /// ZRANK : https://redis.io/commands/zrank /// @@ -326,7 +341,7 @@ public Task SortAndStoreAsync(RedisSortedSet destination, long skip = 0 { //--- I don't know if serialization is necessary or not, so I will fix the default value. RedisValue by = default; - RedisValue[] get = default; + RedisValue[]? get = default; return this.Connection.Database.SortAndStoreAsync(destination.Key, this.Key, skip, take, order, sortType, by, get, flags); } @@ -338,7 +353,7 @@ public async Task SortAsync(long skip = 0, long take = -1, Order order = Or { //--- I don't know if serialization is necessary or not, so I will fix the default value. RedisValue by = default; - RedisValue[] get = default; + RedisValue[]? get = default; var values = await this.Connection.Database.SortAsync(this.Key, skip, take, order, sortType, by, get, flags).ConfigureAwait(false); return values.Select(this.Connection.Converter, (x, c) => c.Deserialize(x)).ToArray(); } diff --git a/src/CloudStructures/Structures/RedisString.cs b/src/CloudStructures/Structures/RedisString.cs index 69b788b..435cb41 100644 --- a/src/CloudStructures/Structures/RedisString.cs +++ b/src/CloudStructures/Structures/RedisString.cs @@ -42,7 +42,7 @@ namespace CloudStructures.Structures /// public RedisString(RedisConnection connection, RedisKey key, TimeSpan? defaultExpiry) { - this.Connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.Connection = connection; this.Key = key; this.DefaultExpiry = defaultExpiry; } From 4b6da2a21c473c4af4e695748872db6ccacc37dc Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 01:25:10 +0900 Subject: [PATCH 05/16] is null / target typed new --- src/CloudStructures/Converters/ValueConverter.cs | 4 ++-- .../Internals/EnumerableExtensions.cs | 2 +- src/CloudStructures/RedisConnection.cs | 8 ++++---- src/CloudStructures/Structures/RedisSortedSet.cs | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/CloudStructures/Converters/ValueConverter.cs b/src/CloudStructures/Converters/ValueConverter.cs index 4257bac..cbec9b9 100644 --- a/src/CloudStructures/Converters/ValueConverter.cs +++ b/src/CloudStructures/Converters/ValueConverter.cs @@ -39,7 +39,7 @@ public ValueConverter(IValueConverter? customConverter) public RedisValue Serialize(T value) { var converter = PrimitiveConverterCache.Converter; - return converter == null + return converter is null ? this.CustomConverter.Serialize(value) : converter.Serialize(value); } @@ -54,7 +54,7 @@ public RedisValue Serialize(T value) public T Deserialize(RedisValue value) { var converter = PrimitiveConverterCache.Converter; - return converter == null + return converter is null ? this.CustomConverter.Deserialize(value) : converter.Deserialize(value); } diff --git a/src/CloudStructures/Internals/EnumerableExtensions.cs b/src/CloudStructures/Internals/EnumerableExtensions.cs index 17d8ee8..1201085 100644 --- a/src/CloudStructures/Internals/EnumerableExtensions.cs +++ b/src/CloudStructures/Internals/EnumerableExtensions.cs @@ -47,7 +47,7 @@ public static IEnumerable Select(this IEnumerable public static IEnumerable Materialize(this IEnumerable? source, bool nullToEmpty = true) { - if (source == null) + if (source is null) { if (nullToEmpty) return Enumerable.Empty(); diff --git a/src/CloudStructures/RedisConnection.cs b/src/CloudStructures/RedisConnection.cs index 932fae2..5b3e91e 100644 --- a/src/CloudStructures/RedisConnection.cs +++ b/src/CloudStructures/RedisConnection.cs @@ -78,7 +78,7 @@ internal IServer[] Servers public RedisConnection(RedisConfig config, IValueConverter? converter = null, IConnectionEventHandler? handler = null, TextWriter? logger = null) { this.Config = config; - this.Converter = new ValueConverter(converter); + this.Converter = new(converter); this.Handler = handler; this.Logger = logger; } @@ -94,7 +94,7 @@ public ConnectionMultiplexer GetConnection() { lock (this.gate) { - if (this.connection == null || !this.connection.IsConnected) + if (this.connection is null || !this.connection.IsConnected) { try { @@ -102,7 +102,7 @@ public ConnectionMultiplexer GetConnection() var stopwatch = Stopwatch.StartNew(); this.connection = ConnectionMultiplexer.Connect(this.Config.Options, this.Logger); stopwatch.Stop(); - this.Handler?.OnConnectionOpened(this, new ConnectionOpenedEventArgs(stopwatch.Elapsed)); + this.Handler?.OnConnectionOpened(this, new(stopwatch.Elapsed)); //--- attach events this.connection.ConfigurationChanged += (_, e) => this.Handler?.OnConfigurationChanged(this, e); @@ -122,7 +122,7 @@ public ConnectionMultiplexer GetConnection() return this.connection; } } - private readonly object gate = new object(); + private readonly object gate = new(); private ConnectionMultiplexer? connection = null; #endregion } diff --git a/src/CloudStructures/Structures/RedisSortedSet.cs b/src/CloudStructures/Structures/RedisSortedSet.cs index d61a03f..4eb529c 100644 --- a/src/CloudStructures/Structures/RedisSortedSet.cs +++ b/src/CloudStructures/Structures/RedisSortedSet.cs @@ -125,10 +125,10 @@ public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet /// public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet destination, IReadOnlyCollection> others, double[]? weights = default, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { - if (others == null) throw new ArgumentNullException(nameof(others)); - if (others.Count == 0) throw new ArgumentNullException("others length is 0."); + if (others.Count == 0) + throw new ArgumentNullException("others length is 0."); - var keys = others.Select(x => x.Key).Concat(new []{ this.Key }).ToArray(); + var keys = others.Select(x => x.Key).Concat(new[] { this.Key }).ToArray(); return this.Connection.Database.SortedSetCombineAndStoreAsync(operation, destination.Key, keys, weights, aggregate, flags); } @@ -321,7 +321,7 @@ public Task RemoveRangeByValueAsync(T min, T max, Exclude exclude = Exclud var minValue = this.Connection.Converter.Serialize(min); var maxValue = this.Connection.Converter.Serialize(max); return this.Connection.Database.SortedSetRemoveRangeByValueAsync(this.Key, minValue, maxValue, exclude, flags); - } + } /// @@ -479,7 +479,7 @@ internal static class RedisSortedSetEntryExtensions public static SortedSetEntry ToNonGenerics(this in RedisSortedSetEntry entry, ValueConverter converter) { var value = converter.Serialize(entry.Value); - return new SortedSetEntry(value, entry.Score); + return new(value, entry.Score); } @@ -493,7 +493,7 @@ public static SortedSetEntry ToNonGenerics(this in RedisSortedSetEntry ent public static RedisSortedSetEntry ToGenerics(this in SortedSetEntry entry, ValueConverter converter) { var value = converter.Deserialize(entry.Element); - return new RedisSortedSetEntry(value, entry.Score); + return new(value, entry.Score); } } } From dac0fafd7eb34690ae46bf1aa7a8e222219b9cc3 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 01:34:38 +0900 Subject: [PATCH 06/16] Supports multi target frameworks --- src/CloudStructures/CloudStructures.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index 80d7b76..51756ae 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -1,7 +1,7 @@  - net5 + netstandard2.0;netstandard2.1;net461;net5 9.0 enable From cbd8a1ccacbb9da4d0497945c90330ee78b2b3d0 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 01:34:48 +0900 Subject: [PATCH 07/16] micro tuning --- src/CloudStructures/Structures/RedisSet.cs | 14 +++++++++++--- src/CloudStructures/Structures/RedisSortedSet.cs | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/CloudStructures/Structures/RedisSet.cs b/src/CloudStructures/Structures/RedisSet.cs index 9ae5cc9..80ca5c7 100644 --- a/src/CloudStructures/Structures/RedisSet.cs +++ b/src/CloudStructures/Structures/RedisSet.cs @@ -128,7 +128,11 @@ public Task CombineAndStoreAsync(SetOperation operation, RedisSet desti if (others.Count == 0) throw new ArgumentException("others length is 0."); - var keys = others.Select(x => x.Key).Concat(new []{ this.Key }).ToArray(); +#if NETSTANDARD2_1 || NET5_0_OR_GREATER + var keys = others.Select(x => x.Key).Append(this.Key).ToArray(); +#else + var keys = others.Select(x => x.Key).Concat(new[] { this.Key }).ToArray(); +#endif return this.Connection.Database.SetCombineAndStoreAsync(operation, destination.Key, keys, flags); } @@ -157,7 +161,11 @@ public async Task CombineAsync(SetOperation operation, IReadOnlyCollection< if (others.Count == 0) throw new ArgumentException("others length is 0."); - var keys = new []{ this.Key }.Concat(others.Select(x => x.Key)).ToArray(); +#if NETSTANDARD2_1 || NET5_0_OR_GREATER + var keys = others.Select(x => x.Key).Append(this.Key).ToArray(); +#else + var keys = others.Select(x => x.Key).Concat(new[] { this.Key }).ToArray(); +#endif var values = await this.Connection.Database.SetCombineAsync(operation, keys, flags).ConfigureAwait(false); return values.Select(this.Connection.Converter, (x, c) => c.Deserialize(x)).ToArray(); } @@ -267,6 +275,6 @@ public async Task SortAsync(long skip = 0, long take = -1, Order order = Or var values = await this.Connection.Database.SortAsync(this.Key, skip, take, order, sortType, by, get, flags).ConfigureAwait(false); return values.Select(this.Connection.Converter, (x, c) => c.Deserialize(x)).ToArray(); } - #endregion +#endregion } } diff --git a/src/CloudStructures/Structures/RedisSortedSet.cs b/src/CloudStructures/Structures/RedisSortedSet.cs index 4eb529c..9f6b2d2 100644 --- a/src/CloudStructures/Structures/RedisSortedSet.cs +++ b/src/CloudStructures/Structures/RedisSortedSet.cs @@ -128,7 +128,11 @@ public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet if (others.Count == 0) throw new ArgumentNullException("others length is 0."); +#if NETSTANDARD2_1 || NET5_0_OR_GREATER + var keys = others.Select(x => x.Key).Append(this.Key).ToArray(); +#else var keys = others.Select(x => x.Key).Concat(new[] { this.Key }).ToArray(); +#endif return this.Connection.Database.SortedSetCombineAndStoreAsync(operation, destination.Key, keys, weights, aggregate, flags); } From f9dfc12b02db8389700b5a537f63313600005ff2 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 01:35:06 +0900 Subject: [PATCH 08/16] Change exception type --- src/CloudStructures/Structures/RedisSortedSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CloudStructures/Structures/RedisSortedSet.cs b/src/CloudStructures/Structures/RedisSortedSet.cs index 9f6b2d2..39cc9d4 100644 --- a/src/CloudStructures/Structures/RedisSortedSet.cs +++ b/src/CloudStructures/Structures/RedisSortedSet.cs @@ -126,7 +126,7 @@ public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet public Task CombineAndStoreAsync(SetOperation operation, RedisSortedSet destination, IReadOnlyCollection> others, double[]? weights = default, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { if (others.Count == 0) - throw new ArgumentNullException("others length is 0."); + throw new ArgumentException("others length is 0."); #if NETSTANDARD2_1 || NET5_0_OR_GREATER var keys = others.Select(x => x.Key).Append(this.Key).ToArray(); From cfd849378e67b7b41a6db155b2afb9f2336cdf79 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 16:09:40 +0900 Subject: [PATCH 09/16] null forgive...( --- src/CloudStructures/Converters/SystemTextJsonConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CloudStructures/Converters/SystemTextJsonConverter.cs b/src/CloudStructures/Converters/SystemTextJsonConverter.cs index 483590c..91d076f 100644 --- a/src/CloudStructures/Converters/SystemTextJsonConverter.cs +++ b/src/CloudStructures/Converters/SystemTextJsonConverter.cs @@ -26,6 +26,6 @@ public byte[] Serialize(T value) /// /// public T Deserialize(byte[] value) - => JsonSerializer.Deserialize(value); + => JsonSerializer.Deserialize(value)!; // forgive } } From ef8a8ab6bd48b85ede26e2e9176f4a8ac540e71e Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 16:20:28 +0900 Subject: [PATCH 10/16] Add Memory <-> RedisValue converter --- .../Converters/PrimitiveConverter.cs | 25 ++++++++++++++++++- .../Converters/ValueConverter.cs | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/CloudStructures/Converters/PrimitiveConverter.cs b/src/CloudStructures/Converters/PrimitiveConverter.cs index 4362bf8..ccab3a3 100644 --- a/src/CloudStructures/Converters/PrimitiveConverter.cs +++ b/src/CloudStructures/Converters/PrimitiveConverter.cs @@ -1,4 +1,5 @@ -using StackExchange.Redis; +using System; +using StackExchange.Redis; @@ -287,4 +288,26 @@ internal sealed class ByteArrayConverter : IRedisValueConverter public RedisValue Serialize(byte[] value) => value; public byte[] Deserialize(RedisValue value) => value; } + + + + /// + /// Provides conversion function. + /// + internal sealed class MemoryByteConverter : IRedisValueConverter> + { + public RedisValue Serialize(Memory value) => value; + public Memory Deserialize(RedisValue value) => (byte[])value; + } + + + + /// + /// Provides conversion function. + /// + internal sealed class ReadOnlyMemoryByteConverter : IRedisValueConverter> + { + public RedisValue Serialize(ReadOnlyMemory value) => value; + public ReadOnlyMemory Deserialize(RedisValue value) => value; + } } diff --git a/src/CloudStructures/Converters/ValueConverter.cs b/src/CloudStructures/Converters/ValueConverter.cs index 64d50a3..e2f15d8 100644 --- a/src/CloudStructures/Converters/ValueConverter.cs +++ b/src/CloudStructures/Converters/ValueConverter.cs @@ -98,6 +98,8 @@ private static class PrimitiveConverterCache [typeof(double?)] = new NullableDoubleConverter(), [typeof(string)] = new StringConverter(), [typeof(byte[])] = new ByteArrayConverter(), + [typeof(Memory)] = new MemoryByteConverter(), + [typeof(ReadOnlyMemory)] = new ReadOnlyMemoryByteConverter(), }; } From 17f0a953bc372ed8c262ed10d71ed6d679d7226d Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 16:20:59 +0900 Subject: [PATCH 11/16] Prefer readonly --- src/CloudStructures/Converters/ValueConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CloudStructures/Converters/ValueConverter.cs b/src/CloudStructures/Converters/ValueConverter.cs index e2f15d8..a4746e5 100644 --- a/src/CloudStructures/Converters/ValueConverter.cs +++ b/src/CloudStructures/Converters/ValueConverter.cs @@ -70,7 +70,7 @@ private static class PrimitiveConverterCache /// /// Hold type and converter mapping table. /// - public static IDictionary Map { get; } = new Dictionary + public static IReadOnlyDictionary Map { get; } = new Dictionary { [typeof(bool)] = new BooleanConverter(), [typeof(bool?)] = new NullableBooleanConverter(), From 8090d1fa60eeb4f889b0f0a329f04a5535581961 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 16:36:18 +0900 Subject: [PATCH 12/16] Import System.Text.Json package only in environments exclude .NET 5. --- src/CloudStructures/CloudStructures.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index eb2981c..353f145 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -21,9 +21,12 @@ Copyright© neuecc, xin9le - + + + + From 54df7815b0606a8372ac57c137fdb7e5939b7c03 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 16:41:05 +0900 Subject: [PATCH 13/16] Update packages --- src/CloudStructures/CloudStructures.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index 353f145..8840648 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -23,11 +23,11 @@ - + - + From e1974993d2fd04348d60249d66147079b4af9b62 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Wed, 28 Apr 2021 16:42:18 +0900 Subject: [PATCH 14/16] remove white-space --- src/CloudStructures/CloudStructures.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index 8840648..a4794d3 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -21,7 +21,7 @@ Copyright© neuecc, xin9le - + From bab3bb84f5a2e5ff1f5f591c52d5d429dc53e7d0 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Thu, 29 Apr 2021 17:12:59 +0900 Subject: [PATCH 15/16] Update README.md --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4f914e6..eb26be0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # CloudStructures -CloudStructures is the [Redis](https://redis.io/) client based on [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis). **Now supports .NET Standard!!** +CloudStructures is the [Redis](https://redis.io/) client based on [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis). -StackExchange.Redis is very pure and low level library. It's Redis driver like ADO.NET. It's very difficult to use it as raw. CloudStructures provides simple O/R (Object / Redis) mapper like [Dapper](https://github.com/StackExchange/Dapper) for ADO.NET. +StackExchange.Redis is very pure and low level library. It's Redis driver like ADO.NET. It's difficult to use it as raw. CloudStructures provides simple O/R (Object / Redis) mapper like [Dapper](https://github.com/StackExchange/Dapper) for ADO.NET. [![Releases](https://img.shields.io/github/release/neuecc/CloudStructures.svg)](https://github.com/neuecc/CloudStructures/releases) @@ -9,7 +9,9 @@ StackExchange.Redis is very pure and low level library. It's Redis driver like A # Support framework -- .NET Standard 2.0 +- .NET 5+ +- .NET Standard 2.0+ +- .NET Framework 4.6.1+ @@ -74,10 +76,50 @@ var result = await redis.GetAsync(); # ValueConverter -If you use this library, you must implement `IValueConverter` to serialize your original class. However, we provides default implementations using [MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp) and [Utf8Json](https://github.com/neuecc/Utf8Json). Unless you pass custom `IValueConverter` to `RedisConnection` ctor, fallback to `Utf8JsonConverter` automatically. If you wanna use MessagePack version, you should install following package. +If you use this library, you *should* implement `IValueConverter` to serialize your original class. Unless you pass custom `IValueConverter` to `RedisConnection` ctor, fallback to `SystemTextJsonConverter` automatically that is default converter we provide. + +## How to implement custom `IValueConverter` + +```cs +using CloudStructures.Converters; +using Utf8Json; +using Utf8Json.Resolvers; + +namespace HowToImplement_CustomValueConverter +{ + public sealed class Utf8JsonConverter : IValueConverter + { + public byte[] Serialize(T value) + => JsonSerializer.Serialize(value, StandardResolver.AllowPrivate); + + public T Deserialize(byte[] value) + => JsonSerializer.Deserialize(value, StandardResolver.AllowPrivate); + } +} ``` -PM> Install-Package CloudStructures.Converters.MessagePack + +```cs +using CloudStructures.Converters; +using MessagePack; +using MessagePack.Resolvers; + +namespace HowToImplement_CustomValueConverter +{ + public sealed class MessagePackConverter : IValueConverter + { + private MessagePackSerializerOptions Options { get; } + + public MessagePackConverter(MessagePackSerializerOptions options) + => this.Options = options; + + public byte[] Serialize(T value) + => MessagePackSerializer.Serialize(value, this.Options); + + public T Deserialize(byte[] value) + => MessagePackSerializer.Deserialize(value, this.Options); + } +} ``` From 9fc664dd54c9b27a04a8e2f160b78cf2b3417d59 Mon Sep 17 00:00:00 2001 From: Takaaki Suzuki Date: Thu, 29 Apr 2021 17:15:38 +0900 Subject: [PATCH 16/16] Remove MessagePack converter project from pack.bat. --- nuget/pack.bat | 1 - 1 file changed, 1 deletion(-) diff --git a/nuget/pack.bat b/nuget/pack.bat index 8b4fb7a..b783704 100644 --- a/nuget/pack.bat +++ b/nuget/pack.bat @@ -1,2 +1 @@ dotnet pack ../src/CloudStructures/CloudStructures.csproj -c Release -o ../nuget/packages -dotnet pack ../src/CloudStructures.Converters.MessagePack/CloudStructures.Converters.MessagePack.csproj -c Release -o ../nuget/packages