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); + } +} ``` 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 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.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 diff --git a/src/CloudStructures/CloudStructures.csproj b/src/CloudStructures/CloudStructures.csproj index 365bd0d..a4794d3 100644 --- a/src/CloudStructures/CloudStructures.csproj +++ b/src/CloudStructures/CloudStructures.csproj @@ -1,9 +1,9 @@  - netstandard2.0 - 8.0 - disable + netstandard2.0;netstandard2.1;net461;net5 + 9.0 + enable true CloudStructures @@ -23,8 +23,11 @@ - - + + + + + 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.MessagePack/MessagePackConverter.cs b/src/CloudStructures/Converters/SystemTextJsonConverter.cs similarity index 68% rename from src/CloudStructures.Converters.MessagePack/MessagePackConverter.cs rename to src/CloudStructures/Converters/SystemTextJsonConverter.cs index 58d193a..91d076f 100644 --- a/src/CloudStructures.Converters.MessagePack/MessagePackConverter.cs +++ b/src/CloudStructures/Converters/SystemTextJsonConverter.cs @@ -1,13 +1,13 @@ -using MessagePack; +using System.Text.Json; namespace CloudStructures.Converters { /// - /// Provides value converter using MessagePack for C#. + /// Provides value converter using System.Text.Json. /// - public sealed class MessagePackConverter : IValueConverter + public sealed class SystemTextJsonConverter : IValueConverter { /// /// Serialize value to binary. @@ -16,7 +16,7 @@ public sealed class MessagePackConverter : IValueConverter /// /// public byte[] Serialize(T value) - => LZ4MessagePackSerializer.Serialize(value); + => JsonSerializer.SerializeToUtf8Bytes(value); /// @@ -26,7 +26,6 @@ public byte[] Serialize(T value) /// /// public T Deserialize(byte[] value) - => LZ4MessagePackSerializer.Deserialize(value); + => JsonSerializer.Deserialize(value)!; // forgive } } - diff --git a/src/CloudStructures/Converters/Utf8JsonConverter.cs b/src/CloudStructures/Converters/Utf8JsonConverter.cs deleted file mode 100644 index e95510d..0000000 --- a/src/CloudStructures/Converters/Utf8JsonConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Utf8Json; -using Utf8Json.Resolvers; - - - -namespace CloudStructures.Converters -{ - /// - /// Provides value converter using Utf8Json. - /// - public sealed class Utf8JsonConverter : IValueConverter - { - /// - /// Serialize value to binary. - /// - /// Data type - /// - /// - public byte[] Serialize(T value) - => JsonSerializer.Serialize(value, StandardResolver.AllowPrivate); - - - /// - /// Deserialize value from binary. - /// - /// Data type - /// - /// - public T Deserialize(byte[] value) - => JsonSerializer.Deserialize(value, StandardResolver.AllowPrivate); - } -} diff --git a/src/CloudStructures/Converters/ValueConverter.cs b/src/CloudStructures/Converters/ValueConverter.cs index 31d7ec7..a4746e5 100644 --- a/src/CloudStructures/Converters/ValueConverter.cs +++ b/src/CloudStructures/Converters/ValueConverter.cs @@ -24,8 +24,8 @@ internal sealed class ValueConverter /// Creates instance. /// /// - public ValueConverter(IValueConverter customConverter) - => this.CustomConverter = customConverter ?? new Utf8JsonConverter(); // fallback + public ValueConverter(IValueConverter? customConverter) + => this.CustomConverter = customConverter ?? new SystemTextJsonConverter(); // fallback #endregion @@ -39,8 +39,8 @@ public ValueConverter(IValueConverter customConverter) public RedisValue Serialize(T value) { var converter = PrimitiveConverterCache.Converter; - return converter == null - ? (RedisValue)this.CustomConverter.Serialize(value) + 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); } @@ -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(), @@ -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(), }; } @@ -111,7 +113,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..1201085 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 (source is 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..3888642 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; } /// @@ -73,13 +72,13 @@ 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) + public RedisConnection(RedisConfig config, IValueConverter? converter = null, IConnectionEventHandler? handler = null, TextWriter? logger = null) { - this.Config = config ?? throw new ArgumentNullException(nameof(config)); - this.Converter = new ValueConverter(converter); + this.Config = config; + this.Converter = new(converter); this.Handler = handler; this.Logger = logger; } @@ -95,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 { @@ -103,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); @@ -123,8 +122,8 @@ public ConnectionMultiplexer GetConnection() return this.connection; } } - private readonly object gate = new object(); - private ConnectionMultiplexer connection = null; + private readonly object gate = new(); + 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..80ca5c7 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,10 +125,14 @@ 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."); - - var keys = others.Select(x => x.Key).Concat(new []{ this.Key }).ToArray(); + if (others.Count == 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(); +#else + var keys = others.Select(x => x.Key).Concat(new[] { this.Key }).ToArray(); +#endif return this.Connection.Database.SetCombineAndStoreAsync(operation, destination.Key, keys, flags); } @@ -154,10 +158,14 @@ 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."); - - var keys = new []{ this.Key }.Concat(others.Select(x => x.Key)).ToArray(); + if (others.Count == 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(); +#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(); } @@ -251,7 +259,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,10 +271,10 @@ 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(); } - #endregion +#endregion } } diff --git a/src/CloudStructures/Structures/RedisSortedSet.cs b/src/CloudStructures/Structures/RedisSortedSet.cs index 560c291..39cc9d4 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,12 +123,16 @@ 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."); - - var keys = others.Select(x => x.Key).Concat(new []{ this.Key }).ToArray(); + if (others.Count == 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(); +#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); } @@ -243,7 +247,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 +258,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 /// @@ -306,7 +325,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); - } + } /// @@ -326,7 +345,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 +357,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(); } @@ -464,7 +483,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); } @@ -478,7 +497,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); } } } 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; }